Last weekend I finally got to the point in an application where I needed to build a private API. I finished the website and wanted to start on the iOS prototype. My first thoughts were:
- How can I secure the API?
- What are my endpoints?
- What should my URL scheme look like?
Naturally, I started by searching the web for "api design", which turned up a whole lot of duds. Naturally there were irrelevant results, but the few that did seem relevant didn't have any specifics. Plus, the few that did have any specifics all contradicted each other. Use REST, don't use REST, strict usage of HTTP verbs, only use GET and POST HTTP verbs, etc. Ironically, the only consistent suggestion was to be consistent. This should be obvious, but it is amazing how often the obvious is ignored.
Since I couldn't find any unifying principles, I decided to come up with my own answers to the three questions I had.
How can I secure the API?
The API I was building was for private use only. The plan is to not allow public access, but you can never stop everyone. So I had to choose something that would keep most people out without creating too much trouble for myself.
I started by looking for encryption patterns I could follow. Maybe I could encrypt the data returned and parameters sent? On the surface, this sounds fine, until you start thinking about replay and man in the middle attacks. If you have a simple endpoint with few or no arguments plus similar data returns, someone can bruteforce your encryption eventually. Not to mention the added strain on your app and servers of encrypting that data. Not to mention, this can be done with HTTPS, so the app doesn't have to deal with it.
HTTPS can be broken with a man in the middle attack though, so how can that be prevented. At this point I started thinking seriously about OAuth. I only knew about 3-legged OAuth at the time though. I didn't need the overhead of user authorization though. Luckily I came across an article discussing 2-legged OAuth and comparing it to 3-legged (2-legged vs 3-legged OAuth).
2-legged OAuth provides everything needed. You get secured communications over HTTPS. You can block replay attacks with a nonce. You can prevent duplicate calls using a timestamp. You get secured communications with the HMAC verification. Plus, if your secret key does get compromised, you can change it easily.
I ended up going with 2-legged OAuth, because it gives me as much security as I could build myself, plus it is battle tested. Standard encryption libraries are used, making them less likely to have programming flaws. Plus, the pattern is known if I ever want to open up the API to public consumption.
What are my Endpoints? / What should my URL Scheme look like?
Now that I've decided on my security, what are the actual endpoints and url scheme? Most of the time, I see the suggestion that your endpoint URL should be a noun, and the HTTP verb describes the action to take.
- GET - Fetch resources, no modification
- POST - Send data to be updated
- PUT - Also send data to be updated (This and POST are debated with some fervor)
- DELETE - Delete the resource
Using those 3 or 4 (if you like PUT) verbs, you can perform any action. My personal disagreement with this pattern is that you end up with one endpoint performing too many actions. REST Purists will lose their mind at that last sentence.
Endpoints should be descriptive. It should be obvious what is going to happen. If I see an endpoint /message and I'm told to POST data to it, I don't know what data I can send and what it will do. Whereas /message/:id/archive is much more descriptive. Then make it a POST verb, and you comply with the spirit. My endpoint tells you what it is going to do, but only responds to a verb telling it to modify data. Here is an example for dealing with messages on a service.
- /messages - Get a list of messages, use GET with parameters. Pagination is a good example.
- /messages/:id - Get a specific message using GET
- /messages/:id/delete - Delete a specific message, use POST since we're modifying data.
- /messages/:id/archive - Archive a specific message, use POST since we're modifying data.
- /messages/new - Send a new message, use POST with data to specify the message
The above is just an example, but it is simple for someone to read and understand. The HTTP verbs are conformed to, and the endpoint describes what it does. There is no ambiguity. This is the pattern I have used and had the most luck understanding/explaining.
In the End
It's up to each individual/group how they design their API. The only thing I hope, is that you make something that makes sense. Documentation is great, but it's easier if I don't have to read any documentation. With the above message endpoint example, everything seems easy. If you want to know what it comes with, call the API and look at the JSON/XML/??? that is returned and read it. Always remember, KISS (Keep it simple, stupid).