I happened to notice two issues with fetch
calls in the documentation:
-
Not checking HTTP success (several sets of pages)
-
Not reading the response body, but seeming to expect it to have been read (one set of pages)
I can do a PR for this if it would help.
Not checking HTTP success
Unfortunately, fetch
doesn't reject its promise on HTTP error, just on network error (a footgun in the API IMHO). So code like this is problematic:
const fetcher = url => fetch(url).then(r => r.json())
If there's an HTTP error (status 500, for instance, or 404 or 401, etc.), that will try to read the response and parse it as JSON, leading to either A) A JSON parsing error, obscuring the real problem; or B) (If the server is unusual and returns a JSON body for the error page) Incorrect (error) data returned as though it were correct data.
The code should be something like:
const fetcher = url => fetch(url).then(r => {
if (!r.ok) {
throw new Error(`HTTP error ${r.status}`)
}
return r.json()
})
Programmers have a tendency to copy and paste from documentation from justly-respected folks like Vercel, so it would be better to help them not get hit by this footgun.
This is present on:
swr-v1.<lang>.mdx
change-log.<lang>.mdx
data-fetching.<lang>.mdx
error-handling.<lang>.mdx
getting-started.<lang>.mdx
global-configuration.<lang>.md
mutation.<lang>.md
(see also the issue below)
options.<lang>.mdx
prefixing.<lang>.mdx
Not reading the response
On the mutation.<lang>.md
pages, the main example doesn't read the response, but the code seems to expect that the response has been read:
mutate('/api/todos', async todos => {
// let's update the todo with ID `1` to be completed,
// this API returns the updated data
const updatedTodo = await fetch('/api/todos/1', {
method: 'PATCH',
body: JSON.stringify({ completed: true })
})
// filter the list, and return it with the updated item
const filteredTodos = todos.filter(todo => todo.id !== '1')
return [...filteredTodos, updatedTodo]
})
There, updatedTodo
is a Response
object, not a "todo" object. It should probably be (fixing both this and the HTTP error part):
mutate('/api/todos', async todos => {
// let's update the todo with ID `1` to be completed,
// this API returns the updated data
const response = await fetch('/api/todos/1', {
method: 'PATCH',
body: JSON.stringify({ completed: true })
})
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const updatedTodo = await response.json();
// filter the list, and return it with the updated item
const filteredTodos = todos.filter(todo => todo.id !== '1')
return [...filteredTodos, updatedTodo]
})