Skip to content

Commit e1032f4

Browse files
committed
Assorted tiny API error blog post tweaks
1 parent 692487e commit e1032f4

File tree

1 file changed

+15
-15
lines changed

1 file changed

+15
-15
lines changed

src/content/posts/designing-api-errors.mdx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Designing API Errors"
3-
date: '2024-09-09T015:30'
3+
date: '2024-09-09T15:30'
44
cover_image: 'header-images/wrong-way.jpg'
55
author: Phil Sturgeon
66
authorUrl: https://philsturgeon.com/
@@ -13,15 +13,15 @@ When everything goes smoothly with an API, life is pretty straightforward: you r
1313

1414
HTTP status codes are like a first aid kit: they’re handy, but they won’t fix everything. They give you a broad idea of what’s gone wrong, which can help plenty of tools and developers make reasonable assumptions, like:
1515

16-
- **400 Bad Request:** report error to developers, something is broken.
17-
- **401 Unauthorized:** might need to refresh a token, don't try again until you have.
18-
- **404 Not Found:** If accepting user input to lookup a resource then this isn't a problem, so don't worry about it. Just tell the user the thing they're looking for isn't there.
16+
- **400 Bad Request:** Report error to developers, something is broken.
17+
- **401 Unauthorized:** Might need to refresh a token, don't try again until you have.
18+
- **404 Not Found:** If accepting user input to lookup a resource then this isn't a problem, so don't worry about it. Just tell the user the thing they're looking for isn't there.
1919
- **405 Method Not Allowed:** Ahhhh panic, the API has changed or the client was built wrong.
2020
- **429 Too Many Requests:** Do not retry this request until after the rate limit is over or you'll DDoS the server and get banned.
2121
- **501 Not Implemented:** Oh heck you've gone live relying on an endpoint which isn't ready in production, alert everyone.
22-
- **504 Gateway Timeout:** Probably retry that one straight away as its likely a network blip.
22+
- **504 Gateway Timeout:** Probably retry that one straight away as it's likely a network blip.
2323

24-
HTTP status codes can convey a lot of assumptions, but they cannot possibly cover all situations, so it's important to add something for the human developers to see whats wrong.
24+
HTTP status codes can convey a lot of assumptions, but they cannot possibly cover all situations, so it's important to add something for the human developers to see what's wrong.
2525

2626
## Written Description of the Problem
2727

@@ -30,11 +30,11 @@ Let’s say you’re building a carpooling app and you need to plan a trip betwe
3030
```http
3131
HTTP/1.1 400 Bad Request
3232
{
33-
"error": "Too close for a caprool to be organized, suggest get out and walk."
33+
"error": "Too close for a carpool to be organized, suggest get out and walk."
3434
}
3535
```
3636

37-
This is a 400 Bad Request, but that's a pretty common error and a little more information needs to be conveyed, so a string has been added explaining the problem.
37+
This is a 400 Bad Request, but that's a pretty common error and a little more information needs to be conveyed, so a string has been added explaining the problem.
3838

3939
Next a user tries to plan a road trip from London to Iceland, which is logistically problematic. The API might come back with:
4040

@@ -47,15 +47,15 @@ HTTP/1.1 400 Bad Request
4747

4848
Here is another 400, and a very different problem. This human message could be passed on to the user so they can figure out what to do next, but the application will not be able to determine the difference between these two errors programmatically, so cannot update the interface differently for either problem.
4949

50-
You could try and find another status code, and people get pretty deep in the weeds trying to find specific codes for every situation ever, but that generally leads to bending conventions beyond their purpose. People do weird things like throwing a "417 Expectation Failed" for something because the clients expectation could not be met... when that code is explicitly tied to the `Expect` header, something you'll likely never use in your API.
50+
You could try and find another status code, and people get pretty deep in the weeds trying to find specific codes for every situation ever, but that generally leads to bending conventions beyond their purpose. People do weird things like throwing a "417 Expectation Failed" for something because the client's expectation could not be met... when that code is explicitly tied to the `Expect` header, something you'll likely never use in your API.
5151

52-
If we think of HTTP status codes are like exceptions in your programming language of choice, if all you saw was `Exception` with no other information, you'd have no clue what is going wrong. Status codes provide a category of error `RuntimeError`, `TypeError`, `ArgumentCountError`, and the error description is like the string passed to an exception: `RuntimeError("This particular problem occurred.")`.
52+
If we think of HTTP status codes like exceptions in your programming language of choice, if all you saw was `Exception` with no other information, you'd have no clue what is going wrong. Status codes provide a category of error `RuntimeError`, `TypeError`, `ArgumentCountError`, and the error description is like the string passed to an exception: `RuntimeError("This particular problem occurred.")`.
5353

5454
This is better, but does not help applications programmatically differentiate between two different types of problem with the same status code, without doing something horrible like substring matching on text which might change.
5555

5656
## Helping Machines with Error Codes
5757

58-
Forcing developers to match human-readable strings for different errors is no good, we also need to help "the machines" know specifically what is going on, so they can work out if they should trigger different interfaces, modals, retry, back-off, report the problem, or do something else.
58+
Forcing developers to match human-readable strings for different errors is no good, we also need to help "the machines" know specifically what is going on, so they can work out if they should trigger different interfaces, modals, retry, back-off, report the problem, or do something else.
5959

6060
```json
6161
{
@@ -74,7 +74,7 @@ Now programmers can do `if (error.type === 'missing_api_version')` instead of ma
7474

7575
A type and a message are a great start, and if that's how far you get then fine, but there's more you can do to turn errors into a handy feature instead of just a red flag.
7676

77-
Here's the full list of what an API error should include:
77+
Here's the full list of what an API error should include:
7878

7979
- **HTTP Status Code**: Indicating the general category of the error (4xx for client errors, 5xx for server errors).
8080
- **Short Summary**: A brief, human-readable summary of the issue (e.g., "Cannot checkout with an empty shopping cart").
@@ -84,7 +84,7 @@ Here's the full list of what an API error should include:
8484

8585
You can build your own custom format for this, but why bother when there's an excellent standard in play already: [RFC 9457 - Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html) (replacing RFC 7807 which is basically the same.)
8686

87-
```
87+
```json
8888
{
8989
"type": "https://signatureapi.com/docs/v1/errors/invalid-api-key",
9090
"title": "Invalid API Key",
@@ -93,7 +93,7 @@ You can build your own custom format for this, but why bother when there's an ex
9393
}
9494
```
9595

96-
This example of an error from the [Signature API](https://signatureapi.com/docs/errors) incudes a `type`, which is basically the same as an application-specific error code, but instead of an arbitrary string like `invalid-api-key` the standard suggests a URI which is unique to your API (or ecosystem): `https://signatureapi.com/docs/v1/errors/invalid-api-key`. This does not have to resolve to anything (doesn't need to go anywhere if someone loads it up) but it _can_, and that covers the "link to documentation" requirement too.
96+
This example of an error from the [Signature API](https://signatureapi.com/docs/errors) includes a `type`, which is basically the same as an application-specific error code, but instead of an arbitrary string like `invalid-api-key` the standard suggests a URI which is unique to your API (or ecosystem): `https://signatureapi.com/docs/v1/errors/invalid-api-key`. This does not have to resolve to anything (doesn't need to go anywhere if someone loads it up) but it _can_, and that covers the "link to documentation" requirement too.
9797

9898
Why have both a `title` and a `description`? This allows the error to be used in a web interface, where certain errors are caught and handled internally, but other errors are passed on to the user to help errors be considered as functionality instead of just "Something went wrong, erm, maybe try again or phone us". This can reduce incoming support requests, and allow applications to evolve better when handling unknown problems before the interface can be updated.
9999

@@ -114,7 +114,7 @@ Content-Type: application/problem+json
114114

115115
This example shows the same `type`, `title`, and `detail`, but has extra bits.
116116

117-
The `intstance` field allows you to point to a specific resource (or endpoint) which the error is relating to. Again URI could resolve (it's a relative path to the API), or it could just be something that does not necessarily exist on the API but makes sense to the API, allowing clients to report a specific instance of a problem back to you with more information that "it didn't work...?".
117+
The `instance` field allows you to point to a specific resource (or endpoint) which the error is relating to. Again URI could resolve (it's a relative path to the API), or it could just be something that does not necessarily exist on the API but makes sense to the API, allowing clients to report a specific instance of a problem back to you with more information that "it didn't work...?".
118118

119119
The `balance` and `account` fields are not described by the specification, they are "extensions", which can be extra data which helps the client application report the problem back to the user. This is extra helpful if they would rather use the variables to produce their own error messages instead of directly inserting the strings from `title` and `details`, opening up more options for customization and internationalization.
120120

0 commit comments

Comments
 (0)