y helo thar

REST is Not for APIs

REST wasn’t designed for modern APIs. It was a retrospective description of how early web browsers talked to HTTP servers — formalized by Roy Fielding to finish his PhD. It explained how the Web worked in the 90s, not how your API should work in 2025.

What we do today should probably be called JOHUR instead (JSON over HTTP, URL-based Routing).

Publish, Perish, and PhDs

Before we get into REST, you need to understand academia. In PhD programs, you're not judged by systems you build — you're judged by the papers you publish.

In the mid-90s, Roy Fielding was working on the early World Wide Web, and also trying to graduate. His dissertation, Architectural Styles and the Design of Network-based Software Architectures, was how he satisfied those requirements. Chapter 5 coined "Representational State Transfer" — REST — as a way to academically describe the evolving architecture of the Web.

He wasn’t writing a blog post for developers. He was describing what existed using academic language for academic purposes. Unfortunately, a generation of engineers read it as a how-to manual.

The Static Web

The original Web was simple: HTML files sitting on a server. Browsers like Tim Berners-Lee’s early prototype let you open those files and click links to others. That’s it.

Built at CERN, the Web’s original purpose was to help physicists share papers and data with each other. Think of it as “Hyperlinked Wikipedia for Scientists.” It ran on machines with floppy disks and 56kbps modems.

This wasn’t designed for social networks or e-commerce. It was text, tables, file downloads, and links. The links are what made them "hyper"-media. The systems were inspired by the same academic ideas that spawned an old 1988 MacOS app called HyperCard.

The Dynamic Web

Then someone had a wild idea: what if /bob/dynamic_page.exe wasn’t a static file, but a program that ran when requested? The server would run it, generate HTML on the fly, and send it over the network.

That small hack exploded into PHP, online stores, and fully dynamic websites.

This meant the Web was no longer static. But to browsers, nothing had changed. Everything still looked like plain HTML over HTTP. That abstraction — a program pretending to be a file — is key to understanding REST.

Towards Web Standards

The years 1993 to 2000 were a rich time for the web. Things grew, people wrote better web browsers. People wrote better web servers. PHP was invented to dynamically generate HTML pages better. HTML got images. HTML got styling. JavaScript was invented.

Lots and lots and lots of things happened.

Behind some of that stuff was Roy Fielding. He was one of many guys attending these meetings between web browser companies, web server companies, ISPs, universities, and whoever else was a stakeholder in these discussions and he was contributing to standards like "What kind of stuff should people be allowed to put in HTTP headers?" and "What should we do when the users refresh the page?"

Web 1.0 was very much a static file sharing web. But Web 1.1 (my terminology) allows arbitrary programs to run in order to generate web pages dynamically. It was in this context that REST was born.

Representational State Transfer

REST, as Fielding described it, was a formalization of how web browsers and HTTP servers interacted.

State was the information stored on the server (e.g., bank balances, product listings).

Representation was the HTML, CSS, and JavaScript used to present that state to users.

Transfer happened through HTTP requests and responses. Connect, send some data, get a response, disconnect.

REST wasn't a design guideline for APIs. It was an architectural snapshot of the Web. And the only truly RESTful clients out there today are web browsers.

REST’s deeper ideas still have value — in rare cases where hypermedia and runtime discoverability are the right fit. But for most practical API developers? REST is more legacy branding than relevant architecture.

The REST Paper in Plain English

This entire dissertation as Roy Fielding wrote it? It was an after-the-fact description of the web as he helped design it and what he managed to get all the stakeholders to agree on and implement. The dissertation itself was a formality in order to satisfy his graduation requirements.

When he talks about a "client" he means literally a web browser. Sure, curl is technically a client too, but nobody is going around curling web pages and manually following links around. Not even Richard Stallman.

When he talks about a "resource" he means a web page. Or a downloadable image. Or a script. He does NOT mean "a computer-understandable data payload for your bank account". He means a web page that displays your bank details and all the supporting stuff in there like scripts and stylesheets that might get cached by web browsers or university proxies.

As far as I can tell, this paper is not meant for us humdrum terrestrial software engineers. The best people to take advantage of this might be aliens who need to reinvent their own world wide web.

I'm not trying to discredit his work. The World Wide Web is an amazing piece of technology and Roy Fielding did an amazing job to push that entire ecosystem forward.

But how he designed the World Wide Web should not be the basis for how software engineers like us design everyday APIs for the banks and schools that we work for. And a misreading of his doctoral thesis shouldn't be the cause of endless architectural bike shedding.

Takeaways for the Real World

There's really no good reason to try to be a "RESTful"-purist. The only truly RESTful apps in the world? Google Chrome, Firefox, Opera, and Safari. That's it.

So let's take the bike shedding away. In the real world in 2025, REST has become a shorthand for the following architecture:

  1. JSON over HTTP
  2. Routing handled by URLs (sometimes with arguments parsed out of the URL path)
  3. Some attempt to try to follow HTTP error codes

All of these things are supported out of the box by pretty much every major web framework.

Misunderstanding 1: JSON over HTTP

JSON over HTTP is really good. Single request - single response protocols are really nice in general. Under the hood, this might be implemented through multiplexed TCP sockets, reordered packets, and who knows what other cursed things. But as far as our applications are concerned? We send one JSON payload, the server receives it, routes the data to the correct function through the URL, then it sends us back one JSON payload.

It it's great. I like it a lot. It's human readable. You can debug it with curl or the network tab in Chrome. 10/10.

Misunderstanding 2: Endpoints as "Resources"

Routing requests via URL? Fantastic. The alternative is that you pack the route inside the message itself and you ignore what web frameworks provide out of the box. I don't see a reason to fight against it.

Here's where we split from cargo-cult dogma. Nouns are NOT only for resources. Sometimes you really just need POST /accounts/123/supercharge. It's a business requirement. Renaming it to /accounts/123/superchargeness doesn't improve anything.

Also, the browser gods gave us only 7 verbs. Sometimes you just need to POST to /accounts/123/deactivate because you had already used DELETE for actual account deletion.

In fact, maybe to remove ambiguity altogether, you're better off writing two URL routes:

POST /accounts/123/deactivate

POST /accounts/123/delete

There's really no technical reason why DELETE is better than POST. In fact, in some cases, DELETE might be worse than POST. Some really old or misconfigured enterprise firewalls might block "weird" HTTP verbs.

9/10. Route to the correct handlers by URL. But don't obsess too much about verbs or nouns. Use what feels right.

On HTTP Verbs

Here's what actually matters with HTTP verbs:

GET requests

POST requests

PUT, PATCH, and DELETE?

Functionally, they behave almost identically to POST in most real-world systems — same preflight behavior, same lack of caching, same infrastructure quirks.

Yes, the spec says PUT should be idempotent, PATCH should be for partial updates, DELETE should remove resources — but that’s more of a design philosophy than a constraint. Browsers won’t really care. Neither will proxies.

Now here’s the weird part: some HTTP libraries — especially older or more restrictive ones — might drop the request body on DELETE. Even though the HTTP spec does allow bodies with DELETE requests, not every toolchain plays nicely with them. So if you're sending important data in a DELETE payload, double-check your stack. This isn’t a philosophical problem — it’s an infrastructure bug you’ll hit in production.

So use the verbs in ways that make sense for your system, your users, and your infrastructure. Just don’t expect the HTTP gods to enforce the rules for you. That’s on you.

Misunderstanding 3: HTTP Error Codes

Some people say the default HTTP error codes are all you need. That's likely not true as your API gets bigger.

There's only a few error codes out there. Less than 600. And some of them are April Fools' jokes like HTTP 418 IM A TEAPOT.

HTTP error codes were made for all the middle men. They're not for your app. Cloudflare needs to differentiate if they should email the web admin if the site goes down or if the user just submitted a bad form.

That's it. That's what the default HTTP error codes are for.

"Wrong Password" on a login form? I guess that's not so bad to be 401 Unauthorized. Try to access something not yours? Sure, 403 Forbidden.

But what about other weird application-specific errors like trying to withdraw too much money from a bank account? There's just no dedicated error code for that. So you just have to roll your own.

I think it's fine to use HTTP error codes. But you should probably plan to add a proper application-specific error code system. How you do it is up to you, but anyone who says that 600 standards-compliant error codes should be enough is making the same type of mistake as claiming 640K ought to be enough RAM for anybody.

Bill Gates never said 640K RAM is enough. And neither did Roy Fielding say 600 error codes are enough. Those are for the internet infrastructure. Your app deserves better. Translatable, traceable error codes.

The worst apps are the ones that just show popups saying "Error 400: Something went wrong!"

5/10. Good as a first approximation. Add more error codes if your app is going to get big.

Final Final Takeaways

The world today is very different from the one in 2000. We have faster chips, better operating systems APIs, and better networks now.

Much of these ideas still hold a lot of weight today. If it worked on worse hardware, why wouldn't it work on better ones? There's much wisdom in trying to build around stateless APIs and caching locally when you can.

But there are also plenty of things you can do now that they couldn't even dream of before. REST isn't a solution for everything. It's a product of its time that still holds some great ideas.

Roblox is a multiplayer game. On a browser. Not built on stateless REST-like protocols. They could never have built that in 2000 (without Flash or Java Applets).

REST, as Fielding described it, isn't just not a silver bullet — it's a bullet meant for a different gun entirely. Let's stop pretending we're re-inventing the web every time we design an API. It's okay to admit that most of the time, we just need a way to do a remote procedure call and HTTP is what browsers understand and JSON is flexible and easy to parse as a way to pass arguments.

In short, JOHUR is really good. It's not REST. It doesn't need to be.

Rant over. We can all go back to calling JOHUR REST now