If you're coming from Next.js or Nuxt.js and are looking for a practical Fastify-flavored replacement, jump straight to the @fastify/react or @fastify/vue documentation sections. All topics in this documentation section cover @fastify/vite from the ground up, targeted mainly at framework authors, and of course, potential contributors to @fastify/react, @fastify/vue and perhaps new core renderers for other frameworks.
Build and Deploy
Without building, you will quickly notice running your Fastify server without the --dev
flag can get you the following error message:
% node server.js
/../node_modules/@fastify/vite/mode/production.js:6
throw new Error('No distribution bundle found.')
^
Error: No distribution bundle found.
% node server.js
/../node_modules/@fastify/vite/mode/production.js:6
throw new Error('No distribution bundle found.')
^
Error: No distribution bundle found.
This means you're trying to run @fastify/vite
in production mode, in which case a distribution bundle is assumed to exist. You need to first build your application code in preparation for @fastify/vite
.
Building multiple bundles
Depending on your application, there can be up to three distribution bundles:
- The bundle that gets delivered to the browser. Required no matter what.
- The bundle used for server side rendering (SSR). Only required if you intend to use server side rendering.
- The "bundle" that contains the code that instantiates the Fastify server itself. Some servers need this, such as ones that are written in TypeScript and do not want to use type-stripping. This build process is entirely up to you.
Building the browser bundle and the ssr bundle can be accomplished by running a single vite build
command. Their entry points are as follows:
├── client/
│ ├── index.js <-- ssr entry point (default)
│ ├── mount.js <-- browser entry point
│ └── index.html
├── client/
│ ├── index.js <-- ssr entry point (default)
│ ├── mount.js <-- browser entry point
│ └── index.html
You can use the included Vite plugin to customize the SSR build:
import { resolve } from 'node:path'
import viteFastify from '@fastify/vite/plugin'
export default {
root: resolve(import.meta.dirname, 'client'),
plugins: [
viteFastify({/* see below for available options */})
],
}
import { resolve } from 'node:path'
import viteFastify from '@fastify/vite/plugin'
export default {
root: resolve(import.meta.dirname, 'client'),
plugins: [
viteFastify({/* see below for available options */})
],
}
The viteFastify
plugin can be given the following options:
spa
- Set this totrue
to disable the SSR build entirely. Default:false
.clientModule
- The location of the SSR entry point, relative to the index.html file. Defaults toindex.js
. You can also use an absolute path to be extra safe.
Assuming you do not need to build your Fastify server itself, the only build script you need in your package.json
file is below:
{
"scripts": {
"build": "vite build"
}
}
{
"scripts": {
"build": "vite build"
}
}
If you do need to run a custom build process on the code that instantiates the Fastify server itself, your package.json
file likely contains the scripts below:
{
"scripts": {
"build": "npm run build:client && npm run build:server",
"build:client": "vite build",
"build:server": "tsc",
}
}
{
"scripts": {
"build": "npm run build:client && npm run build:server",
"build:client": "vite build",
"build:server": "tsc",
}
}
In the example above, tsc
is used as the custom build process for the server code, but this can be replaced with whatever you need to use. If you do this, you probably want to customize the location of your dist directory (see below).
The dist directory
The vite build
script above creates a dist
directory with the following contents:
├── dist/
│ ├── client/ (contains client bundle)
│ └── server/ (contains ssr bundle, if enabled)
├── dist/
│ ├── client/ (contains client bundle)
│ └── server/ (contains ssr bundle, if enabled)
Depending on whether you have decided to use a src
directory, you may also want to customize where your dist
directory is located. This especially true if you have a custom build process for your server code. Common locations for the dist
directory can be:
- Within the client folder, aka the location specified as the
root
of yourvite.config.js
file. This is the default and is recommended if you do not run a custom build for your server code. - At the root of the application. If you run a custom build for your server code, it generally builds into a dist folder that is a sibling to your
src
directory. In this case, you likely want to put your client and SSR bundles into this folder as well.
If you choose to go with (2), your dist
folder should also contain the built server files at its root level:
├── dist/
│ ├── client/ (contains client bundle)
│ ├── server/ (contains ssr bundle, if enabled)
+ | └── server.js (the built server files)
├── dist/
│ ├── client/ (contains client bundle)
│ ├── server/ (contains ssr bundle, if enabled)
+ | └── server.js (the built server files)
To achieve this, you need to do two things:
- Instruct the included
viteFastify
plugin to output its files into your customdist
directory location using vite'sbuild.outDir
option. - Instruct your custom build process to output the built server files into the same
dist
directory.
If you do not do the above two steps, you could end up with two separate dist
folders that you will need to handle on your own. Below is an example of this setup using tsc
as the custom server build process:
import { resolve } from 'node:path'
import viteFastify from '@fastify/vite/plugin'
export default {
root: resolve(import.meta.dirname, 'client'),
build: {
outDir: resolve(import.meta.dirname, 'dist'),
},
plugins: [
viteFastify()
],
}
import { resolve } from 'node:path'
import viteFastify from '@fastify/vite/plugin'
export default {
root: resolve(import.meta.dirname, 'client'),
build: {
outDir: resolve(import.meta.dirname, 'dist'),
},
plugins: [
viteFastify()
],
}
{
"compilerOptions": {
"outDir": "dist"
}
}
{
"compilerOptions": {
"outDir": "dist"
}
}
Running in production mode
Once your build processes are complete and your dist
directory is populated, you should be able to run a Fastify server with @fastify/vite
in production mode. Run node server.js
without the --dev
flag to see. Don't forget to include your dist
directory as part of your deployment.
Make sure to have
NODE_ENV
set toproduction
as well in case your framework requires it (like React) for SSR in production as well. Accidentally running React SSR in development mode is quite common and can lead to serious performance degradation.
Also note that in production mode, @fastify/vite
will serve static assets from your Vite application via @fastify/static
automatically, but you should consider using a CDN for those files if you can, or just serve through Nginx instead of directly through Node.js.
If you don't need SSR, it can also just serve as a convenience to serve your static Vite bundle through Fastify via @fastify/static, automatically inferring your bundle's output directory from your Vite configuration file, and still allowing you to leverage Vite's development server for hot reload.
Tip: If you are using Docker, your final
CMD
should benode server.js
and notnpm run anything
. This will allow SIGTERM signals to be sent directly to thenode
process and not an intermediarynpm
process which usually fails to forward the messages properly tonode
.