I believe that good interfaces are even more important than good code. When refactoring a bad interface, code behind (providers) that interface and code before (consumers) that interface has to be refactored (not to forget the interface itself). Weighting even more heavily the more systems use the bad interface. When refactoring bad code behind a good interface or before a good interface, everything else stays untouched.
Locators Methods Sessions Response codes and error handling HATEOAS REST Maturity Model Useful query parameters Further reading
I observed that many developers, even the very experience ones, don’t seem to care too much on interfaces, doing them on the fly, thereby creating a technical debt
for the future and causing many pains being hard or impossible to get rid of again …
While working on microservices
I was doing some research on how to design RESTful
interfaces. For doing good API
design (in terms of library
) you are focusing on the possible use cases, taking care of decoupling
, regarding the Law of Demeter
and more the such. In addition you require some sound experience in doing good API
design.
Designing RESTful
interfaces is somehow different to API
design as the former focuses on resources
and domains
and the latter focuses on functionality, transactions
or the such.
The rules on
RESTful
interface design fit on a single page; though when you take them seriously, following the idea of everything being aresources
, then you end up with astoundingly robust and extensible (non-breaking) interfaces.
Below find an except from resources such as Using HTTP-Methods for RESTful Services from which I distilled what I consider helpful when designing your robust end extensible RESTful
services:
Locators
Yes, the basics are just about your resources
being collections
and elements
which your service
provides using locators
to address your resources
. The art of building a RESTful
service is to arrange your collections
and elements
in a locator
’s path to represent your domains
in question. Let’s formalize the process of creating your locators
:
Creating good
RESTful
interfaces is much easier than creating a goodAPI
as creatingRESTful
interfaces can be formalized!
Break down your domain
into a tree representing a hierarchical structure of your domain
. Let’s call this hierarchical structure your domain resource hierarchy
. To address a resource
in this domain resource hierarchy
you use a locator
which points to that very resource
.
Let’s take a look at a banking account service and let us break down this banking
domain
into adomain resource hierarchy
and see whichlocators
magically jump out of the nowhere.
Given the online bank acme.com
has customers whom have bank accounts, money transactions may be applied on such a bank account and so forth. The hierarchical structure for this business case ends up in this banking domain resource hierarchy
:
The schema for the path
or locator
pointing to an individual transaction distills as follows:
customers
→ <customer_id>
→ accounts
→ <account_id>
→ transactions
→ <transaction_id>
Given a customer with the customer_id=d
having an account with account_id=3
on which a transaction with transanction_id=5161
has been applied, locators
till the transaction 5161
for a RESTful
service look like this:
https://acme.com/customers/
d
https://acme.com/customers/
d
/accounts/
https://acme.com/customers/
d
/accounts/
3
https://acme.com/customers/
d
/accounts/
3
/transactions/
https://acme.com/customers/
d
/accounts/
3
/transactions/
5161
As REST
is all about collections
and elements
the above locators
read as follows:
- Address the customer
d
element - Address a collection of all accounts for customer
d
- Address the account
3
element of customerd
- Address a collection of all transactions for account
3
of customerd
- Address the transaction
5161
element for account3
of customerd
Methods
The HTTP
methods GET
, PUT
, POST
and DELETE
of RESTful
services distinguish between single collections
and elements
:
Collection
: E.g.http://acme.com/
resources
Element
: E.g.http://acme.com/resources/
item17
Method | Collection | Element |
---|---|---|
GET |
200 OK , list the URIs and perhaps other details of the collection ’s members. Use pagination, sorting and filtering to navigate big lists. |
200 OK , retrieve a representation of the addressed member of the collection , expressed in an appropriate Internet media type. 404 Not Found , if ID not found or invalid. |
PUT |
404 Not Found , unless you want to update/replace every element in the entire collection : Then replace the entire collection with another collection . |
200 OK or 204 No Content , replace (update) the addressed member of the collection , or if it doesn’t exist, create it. 404 Not Found , if ID not found or invalid. |
POST |
201 Created , create a new entry in the collection . The new entry’s URI is assigned automatically and is usually returned by the operation. Location header with link to the new element . |
404 Not Found , not generally used. When supported, then treat the addressed member as a collection in its own right and create a new entry in it. |
DELETE |
404 Not Found , unless you want to delete the whole collection - not often desirable: If so, then delete the entire collection . |
200 OK , delete the addressed member of the collection . 404 Not Found , if ID not found or invalid. |
Sessions
In case you really, really, really need something like a
server side state
for a user, be it of security reasons or other constraints, think about handling suchserver side session
information as a separateresource
.
Some inspirations on sessions
flying around out there on the internet:
- Establish a session token via a
POST
or by using an API key as a POST body argument or as a cookie. - Usernames, passwords, session tokens, and API keys do not appear in the URL, as this can be captured in web server logs, which makes them intrinsically valuable.
- Use only the session token or API key to maintain client state in a server-side cache (no state blob being sent as part of the transaction).
Response codes and error handling
Applicable HTTP
response codes for the HTTP
methods regarding collections
and elements
:
Response Code | GET | PUT | POST | DELETE |
---|---|---|---|---|
200 OK |
Collection, Element | Element | n/a | Element |
201 Created |
n/a | n/a | Collection | n/a |
204 No Content |
n/a | Element | n/a | n/a |
404 Not Found |
Element | Element, Collection | Element | Collection |
HATEOAS
“… Hypermedia as the Engine of Application State, kurz HATEOAS, ist ein Entwurfsprinzip von REST-Architekturen. Bei HATEOAS navigiert der Client einer REST-Schnittstelle ausschließlich über URLs, welche vom Server bereitgestellt werden…” (HATEOAS)
REST Maturity Model
“… Das REST Maturity Model (REST-Reifegradmodell), kurz RMM, ist ein von Leonard Richardson entwickelter Maßstab, der angibt, wie strikt ein Service REST implementier…” (REST Maturity Model)
The REST Maturity Model
defines how RESTful
a REST service
actually is:
Level | HTTP-Methods | URIs/Resources | Protocol |
---|---|---|---|
0 | One (often POST ) |
Single URI | XML-RPC , SOAP |
1 | One (often POST ) |
Multiple web resources | ROA |
2 | GET , PUT , POST , DELETE |
Multiple web resources | ROA |
3 | GET , PUT , POST , DELETE |
Multiple web resources | ROA alongside HATEOAS /HAL using hypermedia for navigation |
Useful query parameters
Working on collections
means that you might want to get just those elements
of interest, be it as of performance reasons or as of business logic requirements. Below find some according useful query parameters:
Purpose | Parameters |
---|---|
Pagination | ?offset=x&limit=y |
Sorting | sort=<criteria>&order=[ASC|DESC] |
Max results | ?max=n |
Dedicated elements |
?id=xyz&id=abc&id=def&... |