Web API Development

Original author: Brian Mulloy
  • Transfer

Intro


This is a brief translation of the main points from the brochure “Web API Design. Crafting Interfaces that Developers Love ”by Brian Malloy of Apigee Labs. Apigee is developing various API services and consulting. By the way, among the customers of this company, such giants as Best Buy, Cisco, Dell and Ebay lit up.

The commentary of the translator comes across in the text, they are in italics .

We collect 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 basic URLs 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 URLs 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 entity URLs of your API. Many developers use verbs to describe objects. It is clear that in the work often have to model complex entities. And these entities interact with each other. But when trying to introduce verbs for modeling entities and their relationships, we will most likely receive a large number of difficult to remember and incomprehensible calls:
/getAllDogs
/getAllLeashedDogs
/getDog
/newDog
/saveDog

See these challenges? That's horrible.

Instead of verbs - HTTP

We just described dogs using two basic URLs with nouns. Now we need to work with the created entities. Most often, reading, creating, editing and deleting operations are required (CRUD - Create - Read - Update - Delete). For this, the HTTP methods GET , POST , PUT, and DELETE are perfect for us .
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 clear. Beauty!

Plural

It’s good to use plural nouns to describe base URLs. Worse - in the singular. It’s bad to mix addresses with nouns in the singular and plural.
/checkins у Foursquare
/deals y GroupOn
/Product y Zappos

Concrete names are better than abstract

Abstract names - this is often considered cool by API architects. And this is not at all cool for those who will later work with this API.
/ blogs, / videos, / news, / articles - this seems obvious. But / items and / assets are not.

Communications

Some resources are always linked to other resources. Is there an obvious and easy way to show these relationships through the API? Moreover, remembering our division into nouns and verbs?
GET /owners/5678/dogs
POST /owners/5678/dogs

We just presented the connection of two resources in a simple way. The GET method in this case will return us the list of dogs of the owner 5678, and the POST method will add another dog to the owner of 5678.
Communication is very convenient to represent in the form / resource / identifier / resource .

Complex things need to be hidden behind the sign "?"

Almost every API has a bunch of parameters that you can read, update, filter and work with them in any other way. But all these parameters should not be visible in the base addresses. It’s best to specify the parameters inside the call 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 what the errors look like 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 the 200 OK code to us.
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 for each error in the API we selected an appropriate HTTP error code. Like Facebook, the guys also provide error information in the body of the response. And, best of all, they give a link to the documentation on the problem.
Simplegeo
HTTP Status Code: 401
{"code" : 401, "message": "Authentication Required"}

SimpleGeo gives an error message in the HTTP code and provides it with a little explanation in the body of the response.

Use HTTP response codes

Feel free to take HTTP response codes and correlate with the responses of your API. In total, about 70 codes are in frequent use. Few developers remember all of them, so it’s better to take about 10 out of 70. 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 are worth using?

Only 3 API responses possible
  1. The request was successful
  2. Wrong data was sent to the input - client error
  3. An error occurred while processing the data - server error

So you can take 3 response codes as a basis:
  1. 200 OK
  2. 400 Bad Request
  3. 500 Internal server error

If 3 codes are not enough for you - take another 5:
  1. 201 Created (Record Created)
  2. 304 Not Modified (Data has not changed)
  3. 404 Not Found
  4. 401 Unauthorized
  5. 403 Forbidden (Access Denied)

It is best not to follow this practice blindly. Measure 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 limit yourself to error codes in the response body. And the standard 200 OK response will allow you to create two error handling mechanisms: for server errors and for errors of the API itself. In general - think, weigh. And then make a decision.


And, of course, with an error, you always need to attach a message to the developers. Attaching 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 every time you request an API to pass the time when the developer application was compiled. Based on this date, Twilio determines which version of the API the application needs to provide. 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 you should not use a dot in the version numbering - this provokes unnecessarily frequent changes in the API. 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’s 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 pass the version in the request begin to fail.

How to implement versions in the API?


Use the v prefix , 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 a lot of different versions and require you to specify them in the headings - 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 can return a bunch of unnecessary information that is almost never used: all sorts of time stamps, metadata, etc. In order not to request extra information, Google came up with a partial answer.
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 with a comma in the request address is a very simple and convenient approach. Take it into service.

Do simple pagination

Giving all the records on the first request is a very bad idea. Therefore, you must definitely consider provisioning. Let's see how you can pull records from 50 to 75 in popular APIs.
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

It’s worthwhile to add meta-information to each answer: the current page, how many records are available.

Provide default values: if the user did not pass the pagination parameters in the request, consider the limit as 10 and the offset as 0 (print the first 10 records).

What about action?

Not all APIs 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 just looking at the call list in the documentation can distinguish entities from actions.

Support for multiple formats

It’s 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 through the Accept HTTP header.

We recommend the Foursquare approach.
/dogs.json
/dogs/1234.json

You can also provide API responses not only in different formats, but also answers for different types of clients. For example, you can make an API that can work simultaneously with an iOS application and the front end of a 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 used immediately on the front end of a web application.

Attribute Names

Attributes can be called in many ways.
Twitter created_at
Bing DateTime
FoursquarecreatedAt

There are many conventions for naming variables. As Ruby on Rails specialists, the Twitter convention is close to us. But we consider the Foursquare approach as the best approach: - camelCase (variables with a small letter, classes - with a capital). This naming convention is most preferable for submitting data to JSON: the data looks like JavaScript. Which, in general, is logical for JSON.

Although the author advises using camelCase more often, it’s better to think about the client and then make a decision. For example, a program written in C may communicate with your API, and it’s better to use a few_other_conventions .

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 one domain

Facebook provides two domains.
api.facebook.comthis address appeared first
graph.facebook.comthis address was entered after the implementation of the global graph

Foursquare limited to one address
api.foursquare.com

Twitter got 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 got themselves to several addresses: it is easier to send requests to different clusters via DNS than through logic. But we are making 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 so as not to create problems with cross-domain ajax requests on the front end.

Redirect

If someone accesses your API domain and does not pass any parameters, feel free to redirect to the domain with the documentation for developers. Get some intuitive documentation domains and redirect to the main developer domain.
api.* -> developers.*
dev.* -> developers.*
developer.* -> developers.*


HTTP response codes and client exceptions

As we said earlier, it is best to send an 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 provided a brilliant and simple solution - add a parameter to the request.
/public_timelines.json?suppress_response_code=true

As a result, errors will come with code 200.
Provide the suppress_response_codes parameter in your API and make it equal to true by default.

But 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 inaccurately handle such links and do not provide them with proper security, bots (such as the Google search robot) can trigger such links. And you will get infinitely created and deleted records every time the bot enters.

Login

This is a complex issue, and my colleagues and I can never agree quickly. Let's see what the leading players do.
PayPal Permissions Service API
Facebook OAuth 2.0
Twitter 1.0a

Please note that PayPal implemented its authorization system before OAuth was invented.

If you require user authorization through third-party applications - only OAuth. And in no case do something like OAuth, but "a little different."

Chatty APIs

“Chatty” - this means that to perform popular operations you have to make 3-4 API calls in a row. This is bad.

Try to look at your challenges through the eyes of the user. You will see that approximately 80% of calls receive and send data of the same structure. This means that it is entirely possible to make aliases for sequences of calls. After that, the user will be able to send data once to the input, and the entire chain of calls will be executed by itself.

Also popular now: