An Introduction to APIs
zapier.com
zapier.com
For most shit you wanna do, its view, edit, delete, its really not that complicated.
Another time, the server had the resource but didn’t like the state of the data, so refused to serve it. Debate ensued as to whether this was a 400 or 500 class error. People got religious.
Yes there’s an answer but it should be so obvious that we don’t have the debate. This isn’t sophisticated verbs, both happened with GET.
https://httpwg.org/specs/rfc9110.html#overview.of.status.cod...
> The 4xx (Client Error) class of status code indicates that the client seems to have erred.
> The 5xx (Server Error) class of status code indicates that the server is aware that it has erred or is incapable of performing the requested method.
There's basic error handling/reporting that seems to transcend technology and architecture, and a big part of that is that errors should have unique error codes. In the context of a web API, both "route not found" and "resource does not exist" should return a 404, but each should have a unique `code` in the body:
{"code": 0, "err": "route not found"}
{"code": 1, "err", "user not found"}
For HTTP, the status code is often for general application development, and the error code is for debugging, though there can be overlap and it's completely fine if a client wants to implement custom logic based on `body.code`.A validation error should look like:
{"code": 2, err: "invalid", "data": {
"username": [{"code": 100, "err": "required"}],
"password": [{"code": 101, "err": "must be at least 6 characters", "data": {"min": 6}}]
}}
The `code` always indicates what other data, if any, exists. Above, a code of 2 means there'll be a `data` field of errors. A validation error of `101` means there'll be a `min` field in `data`. `err` is an user-safe (developer friendly) description of the message which can always be regenerated from `code` + `data`.For errors that aren't known ahead of a time (e.g. a server error), that should also have a distinct code, say 500, and the "data" field should contain an `error_id` which can be used to look up the error.
One improvement I'd steal from theirs and drop in yours - constant (or enum) string codes. It's a lot more scannable when debugging/reviewing/maintaining than having to look up integer codes in a table.
Error codes should be accompanied by helpful messages so you don't have to look up the table.
https://datatracker.ietf.org/doc/html/rfc7807
People shouldn’t invent their own custom error JSON when a standardised format will work.
I forget if it was 404 or something else, but you should check if it actually works first. One of our sites did exactly as you suggest here, and it worked totally fine in development (django "runserver"), but didn't work in production (wsgi behind apache). Turned out with that HTTP code, apache was discarding the body.
But, in general, it's totally fine to return a 404 with no other info. That's a totally acceptable API.
Everytime i need to plan out an API i stumble into a lengthy PUT/PATCH analysis and read up session that shouldnt really be necessary.
POST should be the only thing needed, if the object already exists, mutate it, if not, create it, in 99% of the times you dont need the “idempotency” argument.
Desktop, embedded, video games, HPC suddenly cried out in terror and were suddenly silenced.
What leads you to believe that a HTTP API does not meet the definition of a API?
I don't think so. The pervasiveness of web apps and applications consuming web services means there's a lot of work involving web APIs. This doesn't mean the definition of API was narrowed. It's all about context. If your bread and butter is developing web services, you don't waste time with context-free definitions of your API. You talk about the API and everyone is on the same page.
You even admitted "The pervasiveness of web apps and applications consuming web services means there's a lot of work involving web APIs" and "If your bread and butter is developing web services, you don't waste time with context-free definitions of your API" which is acknowledging the common use definition of API has morphed into Web API. It's only in the context of technical documents or documents that are explicitly referring to non-web APIs that use API and it is understood to mean something other than a web API.
For me, an API is a lot more concrete: a set of data structures (whether they be classes, objects, pairs, tuples, lambda functions, etc.) with extensible functionality (member functions, pure functions accepting those data structures, etc.) that developers can use independently, or together, to produce some new functionality, i.e. program using interfaces.
So what I'd traditionally think of APIs are the .NET API, the C++ STL, the Java API, the Windows API, the Linux headers, Qt, GTK, even React.
In non-web development the APIs can be described formally as function-signatures in any statically typed programming language.
Or is there some simple language in which to write descriptions of web-services?
They're not hugely popular for web services largely because the web was designed to avoid exactly this kind of a priori contractual requirements between communicating parties. But that's a much bigger conversation.
Like, this is a fundamental aspect of how I interact with my job and business has taken the term and elevated it into its own distinct thing
Make all requests POST and enjoy easy life without useless debates on should creation of resource be on POST or PUT or should you return HTTP status 404 or 200 if resource/document on server is not found (of course if should be 200 because request was a success, 404 should only be used it api method is not found).
I 100% agree with Troy Griffitts beautiful take https://vmrcre.org/web/scribe/home/-/blogs/why-rest-sucks
If you'd like a good back and forth on the idea the classic c2 page is a great resource. http://wiki.c2.com/?ApiVsProtocol
Most of the time (like 99.9%) what you happen to need is JSON RPC. Even if some parts of your API surface look like they would fit the ReST model, the bulk does not. Ignore that, build a protocol along the lines of your subject area. Always return 200 if your server did not fail or reject the request, use internal status signaling for details. Limit yourself to GET and POST. Use HTTP as a mere transport.
"Use internal status signaling" for example doesn't seem any better than deciding what status codes mean what; it's just a second layer of codes where the first one is now useless.
"Limit yourself to GET and POST." - delete and patch are pretty useful for documentation simplicity too. If there were a LIST verb that would be even handier, but nothing's perfect.
"build a protocol along the lines of your subject area" - I think you can do this (and well or badly) using REST or RPC forms.
- Everything is a POST, so normal HTTP caching is out of the question.
- JSON RPC code generators are non-existent or badly maintained depending on the language. Same with doc generators.
- Batching is redundant with HTTP2, just complicates things.
- Because everything is a POST normal logging isn't effective (i.e. see the url in logs, easy to filter etc). You'll have to write something yourself.
- Not binary like Protobufs or similar
But yeah, "the silent pro's choice"... Let's keep it silent.
JSON RPC is pretty much dead at this point and superseded by better alternatives if you're designing an RPC service.
thin CRUD wrappers obviously but usually when you are piping data from one source/format to another, you typically want to do something that is ever so slightly “not-CRUD” (call another API/service, etc.)
Probably the confusion comes from the fact a lot of people think having a verb in their URI makes the API RPC, while only having nouns is the proper REST.
But the whole verbs vs nouns debate in the context of REST sounds a bit like... arguing whether building a round or square control tower out of straw will attract more cargo.
HATEOAS is the cornerstone of REST, this is what sets it apart from RPC-style applications, not the absence or presence of verbs in URIs.
Think of a regular (that is non-SPA) Django, RoR, etc application.
The user points their browser to the app's home page. The backed receives the HTTP request, renders the HTML, and sends it back to the browser.
The browser renders the HTML and lets the user interact with all the control elements on the page. When the user clicks a button or follows a link, the browser sends the corresponding HTTP request to the backed which inspects it and decides what next HTML page (or maybe the same) representing the state of app should be transferred to the user.
This is basically REST. The key to notice here is at no point in this example the browser gets to decide what the app's "flow" is supposed to be -- this is the sole responsibility of the backend.
A consequence of this is the entire structure of pages (aka resources) can undergo a drastic change at any moment, but as long as the home page URI stays the same, the user doesn't suddenly need another browser to access the app.
If changing a resource's URI, or removing a resource altogether can break an existing client, if an existing client cannot make use of a new resource without changes to the client's sources -- that's RPC even if there's not a single verb in the API URIs.
Most likely this architectural style isn't something that first comes to mind when we think of today's mobile apps or SPAs as API clients. And in my opinion it's just not a good fit for most of them: the server isn't expected to drive their flow, it just exposes an API and lets each client come up with its own UX/UI.
Very much so. It’s in a terrible state where I’ve looked. Most of the tooling is by OpenAPI or similar which comes with a bloatload of crap that is only marginally better than say SOAP. It needs to be much simpler.
> - Not binary like Protobufs or similar
Agreed. This is not an issue for small things that can be base64 encoded but once you need large blob transfers you don’t have any reasonable option. This is a problem in eg graphql which also misses the mark and you have to step outside for things like file uploads.
It feels like the whole standardization effort around json rpc is weak. It doesn’t address the needs of modern RPC-like systems. Which is unfortunate because there’s a real opportunity to improve upon the chaos of REST.
However, the main issue with big base64 blobs is that you can and should never assume that JSON parsers are streaming. So you may need to load the whole thing in memory, which of course isn't good.
Note that I'm not necessarily blaming JSON for this. My gut feeling is that crusading for streaming parsers is a Bad Idea. Instead, this is something that should probably be a higher-level protocol, either by streaming chunks (a la gRPC) or by having separate logical data streams (see e.g. QUIC). JSON RPC does not, afaict, solve these issues.
SO (https://stackoverflow.com/questions/4715415/base64-what-is-t...)
- compression is often more efficient with larger payloads
- can reduce per-request overheads, e.g. do authentication once rather than X times
- easier to coalesce multiple queries, e.g. merge similar requests to enable data retrieval via a bulk query, instead of X individual queries
Batching means combining multiple logical operations in a single physical request. HTTP/2 muxes N logical requests over 1 physical connection, which is good, but the application will still process requests independently. You always want to batch workloads into single requests if you can, HTTP/2 doesn't change this.
I mostly dislike REST because nobody can agree on what it is and there are too many zealots who love to bikeshed. If you stick with the simple parts of REST and ignore the zealots, it's decent enough for many scenarios.
I've yet to find an RPC protocol that fills all requirements I've encountered, they all have tradeoffs and at this point you're better off learning the tradeoffs and how to deal with them (REST, JSON RPC, gRPC, WebSockets, etc.) and how they interact with their transports (HTTP/1.1, H2, QUIC, etc.), and then play the unfortunate game of balancing tradeoffs.
I want to emphasize that I was not thinking about JSON RPC as a specific protocol, but more as a JSON format to transfer data, similar to how REST APIs usually do, and some kind of "HTTP method agnostic remote procedure call", it does not have to be JSON RPC standard.
Personally, I am a fan of just having API Class-es + methods that automatically map to API calls with automatic api interface and doc builders. I find that it would be super strange if I had to prefix my internal methods with DELETE or PUT based on do they remove or add to some Array. Using that logic, why do that in APIs.
I just find it super strange that people want to mirror their app logic + error response codes to some protocol like HTTP – ridiculous :) Why not go even lower as TCP and use some of that spec for our client <> server API conn. Many people will laugh, but if you think about it, where is the difference?
It's true that POST ends up being a bit of a grab bag for all the non-CRUD API calls.
But I find it very useful when looking over someonje's API to find them using PUT, or DELETE. PUT in particular provides really useful signals about the nature of the resource we are dealing with.
And lets not get started with the in-built caching etc. you throw away by not using GET.
Why is this ridiculous?
HTTP is the default protocol for network services, so it seems to me that it is perfectly sensible to design your API to be compatible with HTTP semantics.
> Why not go even lower as TCP and use some of that spec for our client <> server API conn. Many people will laugh, but if you think about it, where is the difference?
Because HTTP is the only protocol that can reliably transit arbitrary networks (middle-boxes, NAT, etc.) in practice.
> Technically, an API is just a set of rules (interface) that the two sides agree to follow. The company publishing the API then implements their side by writing a program and putting it on a server. In practice, lumping the interface in with the implementation is an easier way to think about it.
which is neither technically correct, nor easy to understand for a layperson. Compare to Wikipedia:
> An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software.
This is the most difficult topic for me. I struggle to discover, understand and implement in my code highly linked resources. How an API is organized or designed should be its own chapter IMHO
“We'll skip the details… […] REST practitioners are split on how to solve the problem of associating resources.”
UUUG
Still some concerns to solve, for me, with REST APIs.
> standardization of @id and @type special attributes by JSON-LD sound like a good step forward
But those were standardised. In the JSON-LD spec.
If you want a format that has specified semantics, use a format that has specified semantics. Like JSON-LD. JSON only solves the syntax part of the problem, by design. It’s not there to define semantics, that’s what formats built on top of JSON do.
We also built a framework like trpc but for Next REST APIs[1] to get all the nice benefits of shared types but also the nice benefits of OpenAPI generation that typically come with RESTful frameworks https://github.com/seamapi/nextlove
https://dictionary.cambridge.org/dictionary/english/layperso...
someone who is not an expert in or does not have a detailed knowledge of a particular subject
"bluffer" would be a humorous alternative.
This avoids classification of the reader. To refer to someone who lacks the knowledge of a domain, they are a “layman”.
Non-technical = not technical = does not have technical expertise.
Seems pretty logical to me
I find it hard to call a data analyst (for example), who can be highly technical, as a non-technical person.
> These are called endpoints simply because they go at the end of the URL, as in http://example.com/<endpoint_goes_here>.
> PUT - Asks the server to edit/update an existing resource
Maybe I've been doing it wrong all these years but it seems to me that the guides flip-flops the responsibility of POST and PUT. My understanding is that POST should edit/modify while PUT creates/replaces a resource.
> The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.
- POST creates
- PUT replaces (i.e. edit, but you need to provide the whole resource)
- PATCH edits (i.e. you can only provide some fields)
APIs rarely implement all these properly in practice but that's my understanding of the theory.
I’ve also implemented some GET endpoints that are a GET but have a side effect of marking something as read. (Normally as a variant to an existing endpoint for sessioned user).
I would expect at a minimum though if you are doing writes during a GET it should be idempotent.
That's different, and safer, than something like a "read" bit on an entity, presumably tracked in an application data layer. I don't think you can mark something as "read" in your application from a GET request. Even if your server sees the response to that GET request as successful, it doesn't necessarily mean that the requesting client actually successfully received the response. As one of infinitely many possible counter-examples, consider a middlebox between a client and your server, where one client request to the server may generate N requests from the middlebox to the server.
Let’s say you have some notification resource which is a link redirecting to the thing that triggered the notification. Ideally the notification will automatically be marked read after the user sees the thing they clicked.
My setting the read bit in the GET that makes the redirect you open up. 2 negative possibilities:
- if someone could guess the GUIDs of the notifications they could CSRF a users notifications as marked read. (Unlikely and low impact if it does occur). - Adds the potential that the client may not have loaded page after the redirect and seen the resource.
There is a UX tradeoff now though if we make this a separate PUT after the page loads:
- in a web application context the user will have to either enable JavaScript so the app can automatically mark this as read or have a separate form on every landing page to mark it as read.
Another alternative would be to make this a POST form to view the notification that redirects but you have in effect the same issue of the user maybe not loading the page after the redirect.
At the end of the day for something as minor as a notification being marked read (as a result of a user clicking directly on it), some idempotent modification can work out and be easy to implement.
Now to be clear I am referring to a purpose built endpoint for a web application.
We expose 1000s of truly restful endpoints that are used outside of a web context and something like this doesn’t really make sense for them.
There are just so many ways for GET requests to be delivered to a server (or load balancer, or IP, or domain, or...) multiple times for a given client request. That capability is built in to HTTP and exploited in more places than you can ever hope to account for, or even detect.
The way I've been segmented them is based on idempotency.
If you repeat the same call multiple times, do you get the same result as if you just ran it once? Then PUT is appropriate.
But if you have side-effects like creating new resources, that would result in different action each time you make the call, then POST it is.
Idempotent methods include GET, HEAD, PUT and DELETE, the resource should always end up in the same state after calling them N times (barring errors/exceptions and such of course). I'm fairly I got this from when I initially read the specification, it's probably mentioned with a bit more grace in the HTTP/1.1 spec.
If you have a longer-running query you could alternatively construct a Query resource of some sort and then GET it until it's ready.
PUT - is effectively an "upsert" at a specific url. Doesn't exist? Create it, does exist? replace it.
PATCH - update a resource with a diff, at a specific url.
POST - this is a RPC, in the case of a REST API it can be used to create a new resource where the "id" is not provided and set by the server, it then redirects to the new url.
POST can be used for any RPC endpoints, even as part of a REST api.
It was written in 2014, not sure if it's still 'up to date' but the articles seem well written, concise, to the point.