This post explains how to build an effective API. Once it’s ready, you need to make it accessible to your users. NGINX and NGINX Plus are frequently used as an API gateway. For more information, check out these resources:
- The announcement for NGINX Plus R10, which includes support for the use of JSON Web Tokens (JWTs, or “jots”) for authentication
- A description of how to implement API authentication with JWTs from our own Liam Crilly
- A video describing how Adobe powers its API gateway with NGINX
This post is adapted from a presentation delivered at nginx.conf 2016 by Mike Stowe of MuleSoft. This second part focuses on best practices. The first part discusses spec‑driven API development. You can view a recording of the complete presentation on the NGINX, Inc. channel on YouTube.
Part 1 | |
29:03 | Incorporate Best Practices |
29:22 | Use Nouns |
30:14 | Use Plural Names |
30:45 | CRUD |
32:37 | Use Hypermedia |
34:07 | Popular Hypertext Link Specs |
35:04 | What Does Hypermedia Look Like? |
35:49 | Use the Accept and Content-Type Headers |
36:47 | Use Response Codes |
38:37 | Use Descriptive Error Messages |
39:33 | Common Descriptive Error Formats |
39:47 | Keep Your API Documentation Up to Date |
40:36 | When Adding to Your API |
29:03 Incorporate Best Practices
Mike Stowe: In addition to spec‑driven development, you want to incorporate best practices into your API.
Just to list some of the main best practices: you want to use nouns over verbs, utilize the HTTP methods or CRUD, utilize hypermedia, use the Accept
and Content-Type
headers, return header codes, and return descriptive error messages. There are even more, but we don’t have time to go into all of them.
29:22 Use Nouns
You should be using nouns, not verbs. With REST, resources are used to identify the objects you’re modifying. So, you specify [a resource called] users, and then you use the HTTP methods themselves to define [actions like] get a user, modify a user, or delete a user.
If you find yourself using verbs, you’re falling into what we call the RPC trap. (This is another reason why we encourage a design‑first approach.) If you’re working on an email API, for example, it’s very easy to think you need a send resource. But that’s a verb, how do you handle that?
If you have a design phase first, you can take a step back and say, “Well, for send, let’s try renaming it.” You’re able to rename and try different things with your users without spending serious time and energy, just modifying text.
And so, you can have it be something like emails, or messages.
30:14 Use Plural Names
When you’re making your resources, you usually want them to be plural. The only exception is when they absolutely have to be singular.
In this case, we’re going to have addresses, not address. What if your users only have one address? Well, that’s fine, but is there potential in the future for them to have multiple addresses? Could they have a shipping address and a billing address?
If the answer is that yes, in the future there could be multiple addresses, then you should start with plural now. That way you don’t have to change the resource down the road.
30:45 CRUD
Take advantage of CRUD. CRUD stands for Create, Read, Update, and Delete.
You should focus mostly on the HTTP methods. Know which method does what. When you create an object, use POST
. If you’re going to read or get an object, use GET
. For updating an object, use PUT
or PATCH
and understand the difference between them. Of course, delete using DELETE
.
It’s really important to understand the HTTP methods well, because there are some weird things that can happen. For example, if I use PUT
vs. PATCH
, I need to be aware that PUT
does a complete overwrite of the data.
Let’s say I have a record:
First name: Mike, Last name: Stowe, City: San Francisco
and I want to move to Austin. If I use PUT
and the only thing I send is City: Austin
, that record will now read
First name: <blank>, Last name: <blank>, City: Austin
PATCH
, on the other hand, patches the data. If I do the exact same thing but with PATCH
, I get
First name: Mike, Last name: Stowe, City: Austin
Everything [that is unchanged] is left intact. So, understand the difference yourself, and also be aware that a lot of users won’t know that PUT
and PATCH
act differently, so explain this to them, especially if you’re using both these methods.
Also, beware of using PUT
to create new items. You can use PUT
to create a new object if an explicit ID is provided and that ID does not exist. The problem is that most developers aren’t going to be expecting to create a new object with PUT
. They’re going to be expecting to get an error back saying, “This doesn’t exist. I couldn’t modify it.” So, if you decide to use that optional route there, make sure you return the right status code – a 201
, not a 200
– otherwise the developer’s not going to realize something was created and not merely updated. As a general rule, avoid using PUT
in this way.
POST
can be used to do other things as well: searches, or advanced types of methods within your API. It doesn’t always have to be for creating objects, but that’s its general purpose.
32:37 Use Hypermedia
Take advantage of hypermedia or Hypermedia As The Engine of Application State (HATEOAS).
If you’ve used the Internet, you’ve probably come across HTML, which is the Hypertext Markup Language. Hypermedia is just an extension of hypertext. All it is is links. It’s a way of specifying the relationship between things.
Hypermedia is so important in REST because REST doesn’t provide state. Let’s say we have three users: an admin, a user, and a spammer. If you [the developer] do GET
on the Users collection, you get those three users back.
How do you know what options you have? Obviously, you can’t delete the administrator because that’s a bad thing. You can’t give the spammer special privileges because he’s a spammer and he’s been suspended. The user, he’s doing great now but maybe you want to suspend him or maybe you want to make him an admin. How do you know what you can do with that object? The answer is hypermedia links.
For admin users, you’d have links letting you get a user or add a new user, but not for letting you suspend or delete him. For the user type you’d be able to get a user, add a user, suspend, delete, or make admin. For the spammer, you’d only be able to delete the user.
The hypermedia links are going to tell the relationship or the status of the object, and that’s what HATEOAS is really about.
34:07 Popular Hypertext Link Specs
There are several popular hypertext link specs out there.
- The first is HAL, which is a very popular specification. [The IETF draft is JSON Hypertext Application Language.]
- The second one is JSON‑LD, which is a W3C standard but was really designed for linking definitions between databases. I’d actually avoid using that one.
- JSON API is a very popular hypermedia format which I highly recommend.
- Collection+JSON was one of the original ones created by Mike Amundsen. It’s a great specification, but I would still lean towards HAL or JSON API.
- Siren is actually really interesting in that it went a different direction. Siren has foreign properties, class properties, and entity properties, and it’s action‑driven.
- Then there’s CPHL. I put the asterisk on because I made it. It’s also action‑driven.
A key question is whether you want to be resource‑driven or action‑driven. That’s something to look at because you want to make sure the hypermedia is providing information not just for the developer but also for the machine to interpret it.
35:04 What Does Hypermedia Look Like?
What does hypermedia look like?
In this case, we’re getting back a user along with links for that user. With HAL, the edit
there would actually probably be self
. With CPHL, it would be the actual action.
Then with HAL you specify a link for that user. With CPHL, because it’s action‑driven you can specify the methods. You can do PUT
or PATCH
against that user. Same thing for the next object, messages: you can do POST
.
So, that’s an illustration of the differences between HAL and CPHL. But again, the concept is the same, and it’s the same with JSON API. You have links which say, here are your next options. It’s similar to a “Choose Your Own Adventure” story book.
35:49 Use the Accept
and Content-Type
Headers
You should use the Accept
and Content-Type
headers.
This is the other mistake we tend to make with APIs. We tend to think, “I’m going to build a JSON REST API and it’s going to be awesome.” It works great, until you get that million‑dollar customer who needs XML. Then you have to go back and refactor the entire API for this customer. That’s why you should build your API from the start with the ability to add content types in the future.
You may think you only need to support JSON now, but what happens if YAML becomes the next big thing and replaces JSON, just like JSON replaced XML?
What if there’s a new specification? With hypermedia, many people are using HAL now, but they might say, “Actually, HAL was great but here’s this great new specification. Let’s use this instead.”
You want the ability to adjust based on your needs and have that flexibility built into your architecture and your API interface.
Right now, the most common content types are application/json
and application/xml
, but you want to give yourself the ability to support multiple specifications without worrying about breaking backward compatibility.
36:47 Use Response Codes
You should also use response codes.
I was looking at a guideline doc the other day that says, “Use three response codes. Use 200
, 400
, and 500
to tell people what went wrong.” Don’t do that.
If I want to know the status, whether this is an error, or if it’s successful, I can look at the first number and know that 200
means success, 300
is a redirect, and 400
is a client error. But please give me more information. Tell me what actually happened.
If I did GET
or an update, 200
is okay.
However, if I do POST
or a PUT
and something is created, use 201
. Tell me explicitly that the new object was created.
If I do PUT
or a PATCH
and nothing’s modified, return 304
[Not
Modified
].
If I send the wrong data or use the wrong format request, return 400
Bad
Request
.
If I haven’t logged in or I sent an invalid auth token, return 401
Not
Authorized
.
If I try to do something I’m not allowed to do, 403
Forbidden
.
404
if the object never existed, or it’s not there. Technically, you want to use the status code for Gone
[namely, 410
] if it once existed, but 404
is traditionally Not
Found
.
405
represents Method
Not
Allowed
. This goes beyond Forbidden
[403
] and says, “Hey, you can’t do this [for example, delete an object]. If you try a different method, that would work.”
415
corresponds to Unsupported
Media
Type
, for example if I request XML but you only support JSON.
500
means we have absolutely no idea what happened.
38:37 Use Descriptive Error Messages
Use descriptive error messages. This is one of my biggest pain points. You call an API and you get back an error that says, “Something went wrong.” I know something went wrong – it didn’t work!
Or my favorite one, “We’re out building a rocket ship, check back later.” This was actually from a company here in Texas, and I nearly drove from Minnesota to Texas to see this rocket ship.
Instead of generic error messages, tell the user specifically what went wrong. So, if you have a customer support status code, give them that code. If you give them an error message like Missing
UserID
, also tell them why there’s an error:
A UserID is required to edit a user
That makes sense, and then give them a link back into the documentation saying, “Here’s how you can find out more. Here’s how you can solve the problem.”
The reality is, if developers can’t use your API and can’t figure out what the problem is, they’re going to use your competitor’s API. They’re going to use an API they can figure out.
39:33 Common Descriptive Error Formats
You don’t need to reinvent the wheel. There are several great specifications out there already.
There’s vnd.error, which is a great specification, Google Errors (which is personally my favorite one out there), and JSON API – if you’re using that for hypermedia, they actually have an error format as well.
39:47 Keep Your API Documentation Up to Date
It might seem silly to mention this, but: keep your documentation up to date and in sync with your API.
This is one of the reasons I really recommend the spec‑driven development approach. Your documentation is always in sync with your API and is easily generated.
I was consulting with a company on its API, and one of their business development guys came to me and said, “Hey, we’re really glad you’re helping us out here. We need to build a new API.” And I said, “Okay, why do you guys need to build a new API?” And he said, “Well, it’s broken. Developers try using our documentation, and it doesn’t work.” What had happened was their documentation was out of sync, but what he saw was that the API didn’t work.
In the end, all they needed to do was update the documentation and things were fine. Had they not looked at the problem that way, they would have rebuilt the whole API and killed off the old API just for that reason.
40:36 When Adding to Your API
Finally, as you do API development and design, go in with a long‑term mindset. Everything you do should be designed to last. Understand what it is you’re actually building – what type of API you’re making – and stick to those standards.
Utilize spec‑driven development, and of course, incorporate best practices. It only takes one tiny little thing, just one mistake in your API that goes to production, to screw things up. Just like Facebook: they have this issue in production, but it’s in production now and they can’t change it. They know it’s there, but the second they change it, they break it for the thousands – if not millions – of users that are using that resource, and that’s kind of put them in a corner with that resource.
Last but not least, remember: building an API is really, really easy. There are all these great frameworks out there. Designing an API – that’s the most difficult part. That’s why you need to spend your time there and say, “Let’s get the design right, and everything else will follow.”
This post is adapted from a presentation delivered at nginx.conf 2016 by Mike Stowe of MuleSoft. This second part focuses on best practices. The first part discusses spec‑driven API development. You can view a recording of the complete presentation on the NGINX, Inc. channel on YouTube.