Static files
Static files are sent to the client as they are, without processing: HTML pages, styles, scripts, images. Fastify does not serve them on its own; the official @fastify/static plugin does. For example, next to the API you can place a simple blog page that loads posts with fetch.
Installing
npm install @fastify/staticThe public folder
Create a public folder in the project root and the file public/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Blog</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Blog</h1>
<ul id="posts"></ul>
</body>
</html>Put public/style.css with any styles next to it. The project structure:
blog-api/
public/
index.html
style.css
src/
app.jsRegistering
Register the plugin in src/app.js:
// src/app.js
import path from 'node:path';
import fastifyStatic from '@fastify/static';
app.register(fastifyStatic, {
root: path.join(import.meta.dirname, '..', 'public'),
prefix: '/',
});root is an absolute path to the folder. import.meta.dirname is the folder of the current file (src), so '..' goes up to the project root. Open http://localhost:3000/ — the "Blog" page appears, and http://localhost:3000/style.css returns the stylesheet.
The prefix
The prefix option sets the start of file addresses. With prefix: '/assets/' the file public/style.css is served at /assets/style.css. A prefix keeps files from being confused with API routes.
Sending a file from a route
The plugin adds the reply.sendFile() method — use it to send a file from the root folder in an ordinary route:
// src/routes/pages.js
export default async function pageRoutes(app) {
app.get('/about', async (request, reply) => {
return reply.sendFile('about.html');
});
}Create public/about.html, register pageRoutes in src/app.js and open http://localhost:3000/about — the contents of that file appear.
Common mistake
Do not put a relative path such as 'public' in root: the plugin is designed for an absolute path, and a relative one depends on the folder the process was started in, so starting from another folder breaks file lookup. Always build the path with import.meta.dirname and path.join.