Design principles behind RESTful APIs
RESTful stands for "Representational State Transfer". Consider this as a concept and a pattern of building client-server APIs. I have been building Python APIs that consume some popular RESTful APIs for the past 5+ years. This article outlines the aspects of a thoughtful and well-designed REST API. Some of the aspects here are from the perspective of a consumer, not the maker of RESTful APIs.
What makes a web API RESTful?
- Presence of a strong separation between client and server
- Server should not remember previous actions of the client, ergo, be what people call "stateless".
- Each request from client should be treated independently and as if it is the first request. This goes with the previous point about server being stateless. Thus, the onus of sending context is upon the client, not the server to remember.
- However, when designing anything for a user, you need to make it as simple as possible. Thus, servers can exchange authentication and context for
tokens
with the client. This allows for a stateless design, but gives users a stateful experience (such as a shopping website remembering what user added to shopping cart) to the client / user. - Responses from server can be marked as cacheable or non-cacheable. This way, client does not have to talk to server for the same requests.
- Server should provide a uniform interface, no matter what the client is(mobile vs desktop vs another server).
Some advanced features of RESTful APIs
- If the logic is complex, you can implement a layered system where clients interact with say, 'Server A'. Then, Server A would interact with many other servers to fulfill the request and give results back to client. Client does not have to know how to talk to the rest of the servers to accomplish this.
- code on demand: Server might optionally send executable code (like JS) to run on client to fulfil a request.
HTTP Request structure
Next, let us talk about what gets exchanged between the client and the server. A request from client to server consists of 3 parts:
- header - which contains:
- A request line. This line contains a HTTP verb, a URI and HTTP version number. An example syntax of request line is
GET /index.html HTTP 1.1
- optional request headers which appear as kvp. For instance
Accept: image/gif, image/jpeg, */*
,Accept-Language: en-us
.
- A request line. This line contains a HTTP verb, a URI and HTTP version number. An example syntax of request line is
- blank line
- body (optional) - body can contain any additional information, such as auth info etc.
- for instance
puppyId=12345&name=Fido+Lava
- for instance
HTTP Response structure
Similar to a request, the response from server to client consists of 3 same parts
- a header, which contains
- status line which has the status code, HTTP version
- optional response headers
- blank line
- body (optional). The body contains what the client asked for. For instance a mp3 file, image file or an html page.
Making RESTful APIs elegant
The rules listed above are just the skeleton. Although not strictly needed, when APIs use the the following design patterns, they become easy for the end user to predict and understand the architecture of the backend system they are working with.
URI design
- URI should take name of resource, not the action to take on them. For instance,
/tickets/4
is good, whilegetTicketInfo/4
is not. Remember, URIs should be nouns, not verbs. - URI should be a plural form for each resource name. For instance,
/puppies
,/tickets
. Depending on the resource needed for the backend, calling the plural form of a resource (/items/
) can list all the items, or give a summary info. - Use HTTP verbs to indicate action. The verbs are
GET
,POST
,PUT
,DELETE
,HEAD
,OPTIONS
,TRACE
,CONNECT
.HEAD
andGET
can be called by web crawlers and search engines, so ensure these resources can only get information and not modify.GET
,POST
resources typically give information to clientPUT
,DELETE
resources can add, update, delete resources on the server. Thus we cover theCRUD
operations using HTTP verbs.
- Use HTTP status codes such as
404
,401
along with error messages appropriately. - Expose child resources progressively. For instance, if
/items/
does not expose all the items and only provides a summary or count, then expose a/items/search
for client to search the backend database. Finally, ensure/search/
accepts well known SQL syntax queries, don't make a custom one. - Give the benefit of doubt to the client. Build the REST handler to be fault tolerant, error tolerant by building the validation logic on backend, and not on the client side.
API versioning
- Versioning allows to maintain backward compatibility when you build a permanent web API.
- You can either add version in the URI, as
/puppies/v2/resource
or in the header of the call.
Conclusion
To learn more about RESTful APIs, go - restfulapi.net - Udacity course on Flask
To look at an example of building RESTful APIs, go to my blog article Building RESTful APIs with Flask in Python.