Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    You are allowed to invent HTTP status codes

    Here's my 2nd favorite frustration with RESTful APIs, one where I spent the first 10 years of my career valiantly arguing for the wrong side.

    You make a request. It returns 404 Not Found. What's wrong?

    Everything an error may mean

    You may be requesting a resource that doesn't exist. Server looked in the database, found nothing, returned 404. As expected.

    You may be requesting an API route that doesn't exist. Server got the request, couldn't figure out where it goes, returned 404. As ... expected?

    You may be requesting a domain that isn't configured. Server got the request, couldn't find the application, returned 404. Rare because DNS won't return 404, but it can happen.

    Any number of intermediary servers (proxies, VPNs, CDNs, NATs, etc) between your browser and your API may return a 404 Not Found. For any "didn't find the thing" reason.

    The response can even get cached along the way. You'll get 404 for a few seconds even after you fix routing on the server 💩

    Protocol vs application level errors

    HTTP Status Codes work just fine. Use them.

    The problem you're facing is that traditional RESTful design mixes domains between protocol and application errors.

    You can't tell the difference between an error that happened on the way to your server, in your server, or inside your business logic. Is it really the same error when you can't find a user and when you typo'd the path? 🤔

    Feels different doesn't it ...

    That's why many modern APIs have started returning 200 Success for everything. As long as the server machinery works.

        Status: 200
        {
          error: 'Not Found'
        }
    

    GraphQL does this – everything is a POST and gets a 200 response. Even if your app breaks down. As long as the app itself is responding, you get a 200.

    Some RESTful APIs do it too.

    An elegant (and evil?) solution

    200 Not Found feels weird to me.

    I understand the reasoning and dislike that nobody except your very special custom client understands there was an error. This can lead to over-aggressive caching from other servers in the chain.

    Imagine a CDN that caches a 200 Not Found. Fix all you want, CDN won't check with your server for 3 days 💩

    You can use additional headers that explain why a 404 happened. Good approach. Can be cumbersome.

    OR! You can invent a custom HTTP status code.

    Custom HTTP status code

    The idea is not as crazy as it sounds. I checked.

    Internet says it's okay
    Internet says it's okay

    In the 4xx space, status codes 452-499 are unassigned and free to use for your nefarious means.

    The ergonomics of a custom error code are great:

    async function example() {
      const res = await fetch("https://example.com/some/api")
    
      if (!res.ok) {
        if (res.status === 493) {
          throw new Error("Server said 493 not cool enough")
        }
      }
    
      return res.json()
    }
    

    Because your status code is in the 4xx space, every piece of web machinery knows the client made a bad request. They won't know what kind, but they do know it failed.

    You'll see it as an error in server logs too. Because that machinery knows it's an error.

    And it won't get unexpectedly cached. Because caches know it's an error and cache less aggressively.

    Even your browser shows it as a helpful error when debugging.

    Browser dev tools recognize the error
    Browser dev tools recognize the error

    The major downside

    Inventing HTTP status codes of your own comes with one major downside:

    What if the HTTP protocol starts using your code in the future? Then you're back to not knowing the difference and your status code is wrong.

    But HTTP moves slow 🤞

    Cheers,
    ~Swizec

    PS: APIs may return 404 on unauthenticated requests for security reasons. They don't want to reveal whether something exists, unless you're allowed to know. GitHub does this.

    Published on June 7th, 2022 in Backend, Fullstack, REST

    Did you enjoy this article?

    Continue reading about You are allowed to invent HTTP status codes

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.

    Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.

    Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.

    Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev

    Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️