Best practices for designing a Ruby on Rails app that connects to a 3rd party API?
Do you need a service to complement your present application? Or do you have an idea for an app but don’t want the hassle of collecting the data you need? Whatever your reason, there are times in the development cycle of many web apps when a 3rd-party integration comes to be unavoidable. One of the easiest ways to do so is to expose a REST API for consumption. Below we’ve shared a few pointers for designing a REST API and will cover a bunch of good practices to ensure maintainability of your code.
What is RESTful API?
An architectural approach known as Representational State Transfer or REST counsels that web applications must handle HTTP as it was primarily envisioned. There are more than just three criteria which need to be fulfilled for a theoretically pure REST API, but the below ones are enough in practice.
- Every individual request mustn't have any context of the requests which came before it. Meaning, client state shouldn’t be stored on the server between requests.
- The resource, represented as data, generally JSON or XML must be returned by resource endpoints. The information returned should be enough for the client to identify then manipulate uniquely, the database rows in question.
- Each request must uniquely identify a particular resource. In other words, each request that modifies the database should act on only a single row of a single table. Requests which only fetch information should get zero or more rows from one table.
A few things to remember while designing for a basic RESTful API in Rails:
- When it comes to naming, while Rails allows you to more or less use PUT and POST interchangeably, your API will be consumed by other developers. Keeping this in mind, it is a good practice to adopt the principle of least surprise, PUT for update, POST for create, PATCH for update and insert. Also, your application will be maintained by different developers, probably long after you leave. Using methods that conform to standard practice makes it an easier exercise.
- Once you’ve exposed your API, assume someone is consuming it. Hence, except for critical bug fixes, you must never modify an existing API. Expose a new version instead of changing present endpoints. One method to do this probably is to build versioned routes and controllers. Also, only expose an API after it has gone through intensive internal testing.
- Coming to route parameters, while one can identify routes based on the id column of the targeted resource, it presumes a higher degree of knowledge by the consuming application. Making use of possibly not unique, but public identifiers such as name lessens the amount of system internals which needs to be exposed, while letting the client easily lookup the data required to make a request. The downside is longer nested routes.
- A base API controller comes in handy while handling extracting common API functionality and authentication. The usual approach is to require reauthentication on a per-request level. This is probably the simplest way to ensure statelessness.
- Defensive programming is a software design principle which states that a piece of software must be designed to continue functioning even in unforeseen circumstances. It is important to apply this practice in API design since your API will be exposed to third-party developers, allowing them to submit arbitrary inputs. When building APIs, in the spirit of defensive programming, ask yourself what the most random, least expected, or most malicious input a user can submit is. Then write code to handle that.
- HTTP status codes, like HTTP verbs, have well-defined meanings although it is so easy just to return 404 or Not Found, 200 or OK, and 500 or Internal Server Error. Use status codes that make sense, for the sake of other developers.
- A flexible, DRY, RESTful API allows us to have behavior that is unique to the API while maintaining the same Database Models. But to test this, request level specs are the most obvious fit, as each request is a stateless transaction and since the bulk of the lifting is done at the controller level. The easiest method of achieving this is controller tests, which aren’t significantly different from the usual model for controller endpoint tests. Basically, we create JSON requests and send them to the endpoints.
Hope this proves helpful.