This post covers RESTful API fundamentals, design, authentication, and best practices. (it's more fun than it sounds I swear)
So what is an API anyways?
Simply put, an API (Application Programming Interface) is a friendly way for an application to allow outside elements to interact with it.
APIs are obviously associated with and made for a specific technical audience, but that's no excuse for a poor implementation. An API should be treated just like any other great consumer facing product—with the consumer's needs at the front.
Design
One of my favorite things about RESTful design is the simplicity of it. By definition REST (representational state transfer) is the software architectural style of the World Wide Web. Requests are made using standard HTTP methods to endpoints that expose predefined resources to operate on. While there is no 100% official design standard, most RESTful web service implementations tend to adhere to the following:
The base URI identifies the API version — This makes it easy to know what version of the API you're working with before making a request.*
/v1
Resources are identified in the URI — Or in other words, common sense design that makes life much easier and reduces the potential for mistakes.*
/v1/customers/active/:id
The action to perform is identified by the request method—Again, common sense design that makes it crystal clear to the RESTful service what your intentions are.
GET /v1/customers/active/
POST /v1/customers/active/
PUT /v1/customers/active/:id
DELETE /v1/customers/active/:id
The request body is formatted consistently and contains data the RESTful service can safely use—Helps ensure data goes to the right place and provides a basic level of input sanitization for the RESTful service.
POST /v1/customers/active/
{
"name": "John Doe",
"address": "123 Fake St",
"phone": "123-456-7890"
}
The response status code identifies the result of the request and the body contains data in a consistent format—Makes it simple for the client to react programmatically based on the response.
HTTP/1.1 200 OK
{
"id": 1234,
"message": "customer 1234 created"
}
Response Status Codes
RESTful APIs should return status codes with each response that are meaningful to the client and denote what the outcome of the request was. Here are the common ones:
- 200—success, tells client the request was successful
- 400—bad request, tells client the request payload contained errors
- 401—unauthorized, tells client the request was unauthorized
- 500—internal error, tells client there is an internal problem with the API
Returned Data
There is no technical restriction on the type of data a RESTful API can return in the response body. I personally prefer JSON but I've seen RESTful services return other types such as HTML, XML, and even RSS. (that was a while ago though)
The important thing here is keeping the response data consistent. If one endpoints returns JSON, all endpoints should return JSON. RESTful APIs work best when the result is predictable.
Authentication
APIs are generally used to retrieve and operate on sensitive data, so authenticating a request before performing an action is important. Since an API is not accessed in the same way as a traditional website that accepts a username and password, different authentication methods must be used. Some common authentication methods are:
- HTTP Basic Authentication
- OAuth (1.0 & 2.0)
While each method is different, they all share a golden rule: ENCRYPTION (HTTPS). If it were up to me, every single API in the world would enforce HTTPS and reject everything else. (Note: it's not enough to simply upgrade the request for the client! More on this later) Enforcing secure requests ensures that anything in between the client and your API that may intercept a request is kept in the dark. You should always design and implement any API with the assumption that people are looking for vulnerabilities. (this should be applied to everything honestly)
HTTP Basic Authentication
HTTP Basic Authentication (HTTP Auth, Basic Auth) involves simply sending an Authorization request header that contains:
- The word "Basic" followed by a single whitespace and
- A base64 encoded value produced from username and password separated by a colon (username:password)
Which results in a header that looks like:
Authorization: Basic dXNlcm5hbWU6b21nYXNlY3JldHBhc3N3b3JkbG9s
The API can then decode the value and authenticate the request using the username/password. It's a good security practice to make sure an API accepts dedicated credentials that can only be used for API requests and not for other services. (clients should also be able to expire their API credentials and generate new ones should they become compromised)
Authentication headers are not encrypted and therefore requests must ALWAYS be sent over HTTPS. (hence why it's called BASIC auth) Otherwise anyone can intercept the request and decode it for unauthorized access. Your API should never upgrade insecure requests either (forwarding HTTP to HTTPS), as that still counts as an insecure request.
HTTP Basic Authentication Pros:
- Authentication is achieved from a single request (no multi-legged auth)
- Easy to implement—no third party libraries or additional servers
- Not a drain on server processing resources
HTTP Basic Authentication Cons:
- Every request must decode the auth header and validate credentials
- Prone to header and body tampering
- Not scope friendly—typically used for isolated, single client to server
OAuth
OAuth takes things to a new level and allows for a much more flexible integration. It's commonly used in enterprise because it allows for access to applications that are hosted on different servers without compromising security. You can think of OAuth as having the ability to authorize specific data or access a user might have at another websites and introduce it to yours without revealing their password and out of scope data. (Facebook and Twitter are good examples of when you might use OAuth authentication)
This authorization flow has three general steps:
- Website A asks for a request token from Website B on behalf of the user (by passing a valid application key and secret)
- The user is directed to Website B and required to login and specifically grant access to Website A (access scopes can be introduced here)
- If granted, the user is directed back to Website A with a request token that is then exchanged for an access token
The resulting access token can then be used for requests to data on Website B that the user has explicitly granted access to without Website A knowing the password of the user or having access other data that may be out of scope. Access tokens automatically expire after a short amount of time to improve security.
OAuth 2.0 introduces refresh tokens which allow for a much more complex authorization implementation. A common refresh token scenario allows Website A to make a request for a new access token on behalf of the user to Website B without the user having to re-authorize with a username and password once the original access token expires.
OAuth also supports less complicated methods of authentication for simple client to server communication while still retaining the familiar access token retrieval process.
OAuth Pros:
- A more secure authorization implementation (tokens rather than passwords)
- More convenient to end users depending on the scenario
- Ability to introduce access scopes for complex implementations
OAuth Cons:
- Harder to setup and maintain (third party dependencies, access portal)
- Expensive to operate depending on implementation (dedicated auth server)
- Not stateless—API "session cookies" (a privacy concern in some cases)
The best way to implement API authorization is completely dependent on your personal preference, your unique application environment, and potentially your audience. You can find success with either one and I urge you to research other methods not discussed in this post as well to see what works best for you. (digest auth, HMAC)
Versioning
API versioning is an important but often overlooked topic—especially with less popular services. The basic idea of a RESTful API is to allow others to implement your application into an outside environment—which means an API is (in some ways) a dependency.
If we start thinking about API design this way, we can arrive at the following guideline: once a significant release has been made, significant changes to that release should stop. Obviously bug fixes and security patches may still be required, but new functionality, endpoints, and features should be left for a release under a newer version.
Luckily RESTful design already has us covered with the API version being declared in the base URI. (/v1) This allows people to integrate with a specific version and not worry that one day their implementation is going to break because you decided to remove a core feature later that year.
Similarly, this allows you to develop newer versions of your API without the fear that you may be breaking someones integration. Your new version can be changed in any way you want because it doesn't force existing integrations to use it. This empowers users and lets them decide when to migrate to a newer version of your API. (rather than forcing them)
Documentation
APIs can only be as good as their documentation allows them to be. While RESTful design tries to conform to common sense standards, it still relies on solid documentation to be successful.
Rather than write a novel on all the different ways you can approach this, I'll provide you with a few links to some of my favorite examples. (pay attention to how they personalize the docs — assuming you're logged in)
Putting things to REST
(horrible attempt at a pun)
Ultimately every RESTful API is going to be unique and will depend on a number of different factors. The choices you make with yours are entirely up to you. Hopefully I've provided you with some new perspective and ways to think about APIs.
If you take anything away from this piece let it be this: always design, develop, and deploy with your audience in mind.