You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/posts/designing-api-errors.mdx
+15-15Lines changed: 15 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
title: "Designing API Errors"
3
-
date: '2024-09-09T015:30'
3
+
date: '2024-09-09T15:30'
4
4
cover_image: 'header-images/wrong-way.jpg'
5
5
author: Phil Sturgeon
6
6
authorUrl: https://philsturgeon.com/
@@ -13,15 +13,15 @@ When everything goes smoothly with an API, life is pretty straightforward: you r
13
13
14
14
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:
15
15
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.
19
19
-**405 Method Not Allowed:** Ahhhh panic, the API has changed or the client was built wrong.
20
20
-**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.
21
21
-**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.
23
23
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.
25
25
26
26
## Written Description of the Problem
27
27
@@ -30,11 +30,11 @@ Let’s say you’re building a carpooling app and you need to plan a trip betwe
30
30
```http
31
31
HTTP/1.1 400 Bad Request
32
32
{
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."
34
34
}
35
35
```
36
36
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.
38
38
39
39
Next a user tries to plan a road trip from London to Iceland, which is logistically problematic. The API might come back with:
40
40
@@ -47,15 +47,15 @@ HTTP/1.1 400 Bad Request
47
47
48
48
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.
49
49
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.
51
51
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.")`.
53
53
54
54
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.
55
55
56
56
## Helping Machines with Error Codes
57
57
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.
59
59
60
60
```json
61
61
{
@@ -74,7 +74,7 @@ Now programmers can do `if (error.type === 'missing_api_version')` instead of ma
74
74
75
75
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.
76
76
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:
78
78
79
79
-**HTTP Status Code**: Indicating the general category of the error (4xx for client errors, 5xx for server errors).
80
80
-**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:
84
84
85
85
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.)
@@ -93,7 +93,7 @@ You can build your own custom format for this, but why bother when there's an ex
93
93
}
94
94
```
95
95
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.
97
97
98
98
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.
This example shows the same `type`, `title`, and `detail`, but has extra bits.
116
116
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...?".
118
118
119
119
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.
0 commit comments