Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
In the past, when tasked with building an API for a web site I would define a suite of URLs to handle the various tasks I wanted to accomplish.
- If I wanted to get a list of posts I would GET from /posts
- If I wanted to publish a new post as the user Sam I would POST to /users/sam/posts
- If I wanted to edit an existing post I would PUT the new data to /posts/the-post-id
Because that’s the right way to do it, right?
Well, I’ve thought about this a whole lot and have come up with an alternate approach that I think works quite well.
Maybe it’s a terrible idea, maybe it’s useful; it’s almost certainly been done before. Let’s jump right into it after a picture that Medium says will “capture people’s interest”.
Photo by Tim Evans on Unsplash. Good one, Tim.
Behold, a scenario: I’ve got a site that sells t-shirts. It’s called Wit-T-Shirt, a clue to the hilarity of the slogans on the apparel.
Somewhere on this site there’s a button that lets a user add a product to their shopping cart. In the browser, clicking this button will call a function called addProductToCart and pass an object containing the product details and the ID of the user that did the clicking.
Then some other stuff will happen — the topic of this post.
Then eventually, on the server, a function addProductToCart will be called which expects the user ID and the details of the product. It will then go and check stock levels, update the user details in the database, calculate postage, and whatever else.
The way things start out and the way they end up are perfectly sensible, it’s the steps in the middle that have been troubling me.
The status quo
Up until now, I would have approached this the same way I think a lot of people do.
On the client (in that addProductToCart function) I’d split the data apart to create a URL with the user ID in it, then I’d initiate a network request with the method of POST (after spending 10 minutes Googling if it should be a PUT or a POST) and stuff the remainder of the data in the body of that request.
On the server, I’d create a route to handle this request. It’s about the same in any language, but here it is in NodeJS with Express.
I’m taking the data that is now split between the URL, method and body, and joining them back together. The user ID from the URL, the product details from the body, and the fact that I want to add something to a cart is inferred from a combination of the HTTP method and the path.
Oh and for this request I’m not even using query parameters! If I were I’d probably want some code to turn a JavaScript object into a string that satisfies the requirements of that key/value syntax.
Speaking of URL encoding, what a strange little thing the URL is. I mean, think about it, the path is an array of variables — a mixture of resource descriptors and IDs — converted to a string and joined with / characters. Oh and also if there’s a ? in there, then the part after that is an array joined by & symbols, each of which is a key/value pair separated by an = symbol. And all in the form of a string with a limited set of allowed characters. What a terrible vehicle for transporting information!
If only there was a better way…
Introducing, the O API
An Obvious API is an API where you say what you want. It is a dumb name (especially the space after the O) but I’m sticking with it.
Let’s look at the above scenario implemented as an O API.
Since I no longer need to chop up my information and ram it in various holes of the HTTP specification, I can use the same URL and HTTP method for all requests — they no longer convey meaning.
In the body of the request I’ll say explicitly what I want to do (action) and pass the data needed to do it (data).
And on the server, I’ll replace my route with this:
Since I’m now sending all information in the body of a request, I can abstract all the other HTTP details into a function named something straightforward like sendToServer.
And now when I want to communicate with the server, I deal only in ‘actions’ and ‘data’.
(It’s up to you where you put your catch and response => response.json(), I’ve left them out of these code snippets because there’s no difference between the two approaches.)
You might be thinking, um, hey stupid, that’s the same amount of code.
Well, yes, you are right. But the aim of the game isn’t fewest lines. The aim of the game is code that’s easy to read, easy to write, and hard to get wrong.
So the real benefit — as I see it — is that this removes code that implies what needs to be done and the matching code that infers what needs to be done, with code that says explicitly what should be done.
Now, if I extend this logic to replace many endpoints, I can employ a handler object and call the appropriate method using bracket notation, and while I’m at it handle requests with no matching handler.
The way I actually handle the requests on the server is the same for an O API or a REST API, except with REST I have to gather up information strewn between req.body, req.params and req.query before doing something with it.
Another nice thing: if your server happens to be JavaScript, you can share these ‘actions’ as constants. This means you can eliminate the brittleness of relying on ‘addProductToCart’ on the client matching ‘addProductToCart’ on the server.
If you have a fake-enum like so:
In the browser you would have
And on the server, if each of your handlers is its own file, you might see:
If you’re a Redux user this is probably looking pretty familiar. The way your action creators dispatch an action and a payload to be handled by the store is just the same as the way you dispatch an action and a payload to the server.
And why would they be different? In both cases you’re sending a message to a different part of the application to do something with some data. Who cares if it’s elsewhere in the browser code or back on the server?
Some name dropping
It’s interesting how changing one little thing can make this seem like a whole different approach. Maybe I just need some sleep, but fetch now resembles an event emitter, app.post('/api' ...) feels like a listener and the body smells like a ‘data transfer object’.
So am I just describing or some butchered version of event-driven architecture or message-driven architecture, or remote procedure calls (or JSON-RPC) or any number of other things that I haven’t heard of?
Maybe, I suppose, if that’s how you want to see it; but really I’m just doing what I’ve been doing all along, with a few of the implementation details around the network request changed.
I would be remiss if I didn’t mention GraphQL. So …
GraphQL.
My suggestion
If you are in the rare and unfortunate position of valuing my opinion, here it is: as you start to design your next API, work out into which of these two buckets it falls:
- The API serves your back end. You want it to allow controlled access to CRUD operations on the underlying data. It is generic and has no knowledge of the application making the requests.
- The API serves your front end. The API’s role is to serve the needs of a specific user interface. It must provide data in the most appropriate format and make it simple for the client to send instructions to the server, allowing the client get on with the business of rendering pixels and handling user interactions.
Clearly, if option 1 is what you want, a REST API is a good solution. And I’d add that since it’s the ‘norm’, anything you’re exposing to the public should be REST.
But if you’re writing an API that will only ever be consumed by your own front end code, and you value code that isn’t more complex than it needs to be, maybe have a think about considering weighing up the option of assessing the viability of an O API.
Wrapping up
Whenever I write a post of this nature (a category I call “you’re all doing it wrong and I’ve come up with a new way even though I don’t really know what I’m talking about”) I tend to get quite a few negative comments.
I don’t have anything to say to quell the flow, I just want you to know I saw it coming.
Thanks a whole lot for reading, have a super day!
O API — an alternative to REST APIs was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.