Intro
This is a brief translation of the main theses from the Web API Design brochure. Brian Malloy from Apigee Labs. Apigee develops various API services and installation. By the way, giants such as Best Buy, Cisco, Dell and Ebay appeared among the clients of this company.
The text comes across the translator's comments,
they are in italics .
We build APIs that other developers will like.
Friendly URLs for API calls
The first principle of good REST design is to make
things clear and simple . You should start with the main URL addresses for your API calls.
')
Your call addresses should be clear even without documentation. To do this, make it a rule to describe any entity using short and clear base URL addresses containing a
maximum of 2 parameters . Here is a great example:
/dogs
/dogs/12345
Nouns are good, and verbs are bad
Keep verbs as far away as possible from the base URLs of the entities in your API. Many developers use verbs to describe objects. It is clear that in work it is often necessary to model complex entities. And these entities interact with each other. But when trying to introduce verbs to model entities and their connections, we will most likely receive a large number of difficult-to-remember and obscure calls:
/getAllDogs
/getAllLeashedDogs
/getDog
/newDog
/saveDog
See these challenges? That's horrible.
HTTP verbs instead
We just described dogs using two base URLs with nouns. Now we need to work with the created entities. Most often, read, create, edit and delete operations are required (CRUD - Create - Read - Update - Delete). For this we are perfectly suited HTTP-methods
GET ,
POST ,
PUT and
DELETE .
POST /dogs β
GET /dogs β
PUT /dogs β
DELETE /dogs β
POST /dogs/12345 β ( 12345 )
GET /dogs/12345 β
PUT /dogs/12345 β 12345
DELETE /dogs/12345 β
Base URLs look simple, verbs are not used, everything is intuitive and understandable. Beauty!
Plural
It is good to use plural nouns to describe basic URLs. Worse - in the singular. Itβs bad to mix addresses with singular and plural nouns.
/checkins Foursquare
/deals y GroupOn
/Product y Zappos
Specific names are better than abstract ones.
Abstract names are often considered cool with API architects. And this is not cool at all for those who will work with this API later.
/ blogs, / videos, / news, / articles - this looks obvious. But / items and / assets - no.
Connections
Some resources are always associated with other resources. Is there an obvious and simple way to show these links through an API? At the same time remembering our division into nouns and verbs?
GET /owners/5678/dogs
POST /owners/5678/dogs
We have just presented the connection of two resources in a simple form. The GET method in this case will return us the list of dogs of the owner 5678, and the POST method will add one more dog to the owner 5678.
Links are very convenient to represent as
/ resource / id / resource .
Difficult things need to be hidden behind the β?β Sign
Almost every API has a lot of parameters that can be read, updated, filtered and worked with them by any other means. But all these parameters should not be visible in the base addresses. It is best to specify the parameters inside reference to the base addresses.
GET /dogs?color=red&state=running&location=park
What about mistakes?
A good error handling mechanism is part of any good API. Let's see how errors look in popular APIs.
Facebook
HTTP Status Code: 200
{"type" : "OauthException", "message":"(#803) Some of the
aliases you requested do not exist: foo.bar"}
It doesn't matter how the request is executed - Facebook will always return us the code 200 OK.
Twilio
HTTP Status Code: 401
{"status" : "401", "message":"Authenticate","code": 20003, "more
info": "http://www.twilio.com/docs/errors/20003"}
Twilio did a good job and selected an adequate HTTP error code for every error in the API. Like Facebook, the guys provide information about the error also in the response body. And, which is the coolest, they give a link to the documentation on the problem.
Simplegeo
HTTP Status Code: 401
{"code" : 401, "message": "Authentication Required"}
SimpleGeo give an error message in the HTTP code and provide it with a little explanation in the response body.
Use HTTP response codes.
Feel free to take HTTP response codes and match them with the responses of your API. In total, about 70 codes are often used. Few developers remember them all, so from these 70s it is better to take about 10. By the way, most API providers do this.
Google gdata
200 201 304 400 401 403 404 409 410 500
Netflix
200 201 304 400 401 403 404 412 500
Digg
200 400 401 403 404 410 500 503
What HTTP Error Codes Should I Use?
Only 3 API responses are possible.
- Request successful
- Wrong data was passed to the input - client error
- An error occurred during data processing - server error
So we can take as a basis 3 response codes:
- 200 OK
- 400 Bad Request (invalid request)
- 500 Internal server error
If 3 codes are not enough for you - take another 5:
- 201 Created (Record created)
- 304 Not Modified (Data has not changed)
- 404 Not Found (Data not found)
- 401 Unauthorized (unauthorized access)
- 403 Forbidden (Access denied)
It is best not to follow this practice blindly. Evaluate the benefits of using HTTP response codes.
in each case. Most likely, the use of response codes will not always be justified and appropriate. For example, if you are writing a backend for a small web application, then you can easily confine yourself to error codes in the response body. And the standard answer 200 OK will allow to create two mechanisms for error handling: for server errors and for errors of the API itself. In general - think, weigh. And then make a decision.And, of course, in case of an error, you should always attach a message to the developers. And to attach a link to the documentation on the problem to the message is even better.
Never release an API without specifying a version.
Twilio /2010-04-01/Accounts
salesforce.com /services/data/v20.0/sobjects/Account
Facebook ?v=1.0
Twilio requires you to pass the time at each request to the API when the developer's application has been compiled. Based on this date, Twilio determines which version of the API to provide to the application. This is a smart and interesting approach, but too complicated. And you can easily get confused with dates.
Salesforce.com inserts v20.0 in the middle of the request API address. And this is a very good approach. But do not use a point in the version numbering - it provokes unnecessarily frequent changes in the API interface. You can change the logic of work inside the API as often as you like, but the interfaces themselves should change as rarely as possible. So it is better to do without a point and not tempt yourself.
Facebook also uses version numbering in the request, but hides it in the request parameters. And this approach is bad because after the next implementation of the new version of the API, all applications that do not transmit the version in the request begin to fail.
How to implement versions in the API?
Use the prefix
v , integers and put the version number on the left side of the address. For example,
/ v1 / dogs .
Keep at least one previous version working
You can also specify the version in the server response headers. This may provide some additional features when working with the API. But if you use many different versions and you need to be sure to indicate them in the headlines, this is a symptom of a big problem.
Partial answer
A partial response allows developers to request only the information they need. For example, requests to some APIs may return a bunch of unnecessary information that is almost not used: all sorts of timestamps, metadata, etc. In order not to request extra information, Google came up with a partial response.
Linkedin /people: (id, first-name, last-name, industry)
Facebook /joe.smith/friends?fields=id,name,picture
Google ?fields=title, media:group(media:thumbnail)
Listing the required fields separated by commas in the address of the request is a very simple and convenient approach. Take it into service.
Make a simple padjin
Giving all entries on the first request is a very bad idea. Therefore, you should definitely provide padzhinatsiyu. Let's see how in popular APIs you can pull records from 50 to 75.
Facebook: 50 25
Twitter: 3 25
Linkedin: 50- 25
We recommend using limit and offset. This approach is more intuitive.
/dogs?limit=25&offset=50
Still it is necessary to attach meta-information to each answer: the current page, how many total records are available.
Provide default values: if the user did not pass padzionation parameters in the request, consider the limit equal to
10 and the offset equal to
0 (display the first 10 entries).
What about action?
Not all API resources are records that can be read, edited, and deleted. There are also API actions. Translations, calculations, conversions are all actions.
It is best to use verbs rather than nouns for such queries.
/convert?from=EUR&to=USD&amount=100
Make sure that even by looking at the call list in the documentation, you can distinguish entities from actions.
Multiple format support
Great to support multiple response formats.
Google ?alt=json
Foursquare /venue.json
Digg ?type=json
By the way, Digg allows you to set the response format via the HTTP Accept header.
We recommend the Foursquare approach.
/dogs.json
/dogs/1234.json
You can also provide API responses not only in different formats, but also responses for different types of clients. For example, you can create an API that can work simultaneously with an iOS application and a front-end web application. It might look like this: /dogs.ios and /dogs.web.Default format
JSON is probably the best default format. It is less verbose than XML. It is supported by all popular languages. It can be immediately used on the front end of the web application.
Attribute Names
Attributes can be called in different ways.
Twitter created_at
Bing DateTime
createdAt
There are many variable naming conventions. We, like Ruby on Rails, have a Twitter consensus on the Twitter convention. But we consider the Foursquare approach to be the best approach: - camelCase (variables with a small letter, classes with a large letter). This type of naming is most attractive for submitting data to JSON: the data looks like JavaScript. That, in general, is logical for JSON.
Although the author advises to use camelCase more often, it is better to think about the client and then make a decision. For example, a program written in C can communicate with your API, and it is better for it to use a few_conversion .Search
Global search is simple:
/search?q=search+word
A search for specific entities can be easily imagined based on what we learned earlier:
/owners/5678/dogs?q=red
And in order to present data in different formats, we recall the extensions:
/search.xml?q=search+word
Collect all API calls on the same domain.
Facebook provides two domains.
api.facebook.com
this address appeared first
graph.facebook.com
this address was entered after the introduction of the global graph
Foursquare limited to one address
api.foursquare.com
Twitter brought 3 addresses, 2 of which are focused on posts and search
stream.twitter.com
api.twitter.com
search.twitter.com
It is easy to guess how Facebook and Twitter brought themselves several addresses: it is easier to send requests to different clusters via DNS than through logic. But we are doing a nice API for developers, so again we will choose the Foursquare approach.
Just in case, let me remind you that developers of a small backend for a web application do not need to allocate a separate domain in order not to create problems with cross-domain ajax requests on the front end.Redirect
If someone is accessing your API domain and does not pass any parameters, feel free to redirect to the domain with documentation for developers. Get some intuitive domains for documentation and redirect to the main developer domain.
api.* -> developers.*
dev.* -> developers.*
developer.* -> developers.*
HTTP response codes and exceptions on the client
As we said earlier, it is best to send the error code not only in the body of the API response, but also in the HTTP response code. But what if the client throws an exception when receiving any HTTP code other than 200? For such cases, the guys from Twitter have provided a brilliant and simple solution - to add a parameter to the request.
/public_timelines.json?suppress_response_code=true
As a result, errors will come with code 200.
Provide
suppress_response_codes parameter in your API and make it equal to
true by default.
What if the client supports a limited set of HTTP methods?
In this case, you need to emulate a full REST API using GET parameters.
/dogs
/dogs?method=post
/dogs/1234?method=put
/dogs/1234?method=delete
Be careful with this approach! If you carelessly handle such links and do not provide them with adequate security, bots (like Googleβs search robot) may call such links. And you will get endlessly created and deleted records at each entry of the bot.
Authorization
This is a difficult question, and my colleagues and I can never agree quickly. Let's see what the leading players are doing.
PayPal Permissions Service API
Facebook OAuth 2.0
Twitter 1.0a
Please note that PayPal implemented its authorization system even before OAuth was invented.
If you need to authorize users through third-party applications - only OAuth. And in any case, do not do something like OAuth, but "a little more."
"Chatty" API
"Chatty" - this means that to perform popular operations, you have to perform 3-4 API calls in a row. This is bad.
Try to look at your calls through the eyes of the user. You will see that approximately 80% of calls accept and send data of the same structure. This means that it is possible to make pseudonyms for call sequences. After that, the user will be able to send data once to the input, and the entire call chain will be executed by itself.