Replies: reply
The second handler argument is the reply object, the server's response. Through it you set the status code (a number such as 200 or 404 that tells the client the result), headers and the body.
Status code
By default Fastify answers with 200. A created resource is usually returned with 201:
// src/routes/posts.js
app.post('/posts', async (request, reply) => {
const post = { id: posts.length + 1, ...request.body };
posts.push(post);
reply.code(201);
return post;
});reply.code(201) only changes the code; the body still comes from return. curl with the -i flag shows the first response line HTTP/1.1 201 Created.
reply.send
Instead of return you can send the reply explicitly. reply methods return reply itself, so they are chained:
// src/routes/posts.js
app.get('/posts/:id', async (request, reply) => {
const post = posts.find((item) => item.id === Number(request.params.id));
if (!post) {
return reply.code(404).send({ error: 'Post not found' });
}
return post;
});The address /posts/99 returns code 404 and {"error":"Post not found"}, while /posts/1 returns the post.
An empty reply
After a delete there is nothing to return. Code 204 "No Content" is for exactly that:
// src/routes/posts.js
app.delete('/posts/:id', async (request, reply) => {
const index = posts.findIndex((post) => post.id === Number(request.params.id));
if (index !== -1) {
posts.splice(index, 1);
}
return reply.code(204).send();
});The command curl -i -X DELETE http://localhost:3000/posts/1 shows 204 No Content with no body.
Headers and type
| Method | What it does |
|---|---|
reply.code(404) | Status code |
reply.header('cache-control', 'no-store') | One header |
reply.headers({ ... }) | Several headers as an object |
reply.type('text/plain') | The Content-Type header |
reply.redirect('/posts') | A redirect |
Fastify sends a string as text/plain, and an object or array as application/json.
Redirect
// src/routes/posts.js
app.get('/articles', async (request, reply) => {
return reply.redirect('/posts', 301);
});Open http://localhost:3000/articles — the browser moves to /posts. The second argument is the code: 301 means "moved permanently"; without it 302 is used.
Common mistake
Do not call reply.send() and return a value at the same time: the reply would be sent twice, and the log shows a "Reply was already sent" warning. In an async handler choose one — return data or return reply.send(data).