TypeScript
TypeScript
The Fastify framework is written in vanilla JavaScript, and as such type definitions are not as easy to maintain; however, since version 2 and beyond, maintainers and contributors have put in a great effort to improve the types.
The type system was changed in Fastify version 3. The new type system introduces
generic constraining and defaulting, plus a new way to define schema types such
as a request body, querystring, and more! As the team works on improving
framework and type definition synergy, sometimes parts of the API will not be
typed or may be typed incorrectly. We encourage you to contribute to help us
fill in the gaps. Just make sure to read our
CONTRIBUTING.md
file before getting started to make sure things go smoothly!
The documentation in this section covers Fastify version 3.x typings
Plugins may or may not include typings. See Plugins for more information. We encourage users to send pull requests to improve typings support.
🚨 Don't forget to install @types/node
Learn By Example
The best way to learn the Fastify type system is by example! The following four examples should cover the most common Fastify development cases. After the examples there is further, more detailed documentation for the type system.
Getting Started
This example will get you up and running with Fastify and TypeScript. It results in a blank http Fastify server.
- Create a new npm project, install Fastify, and install typescript & Node.js types as peer dependencies:
npm init -y
npm i fastify
npm i -D typescript @types/node
- Add the following lines to the
"scripts"
section of thepackage.json
:
{
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "node index.js"
}
}
- Initialize a TypeScript configuration file:
npx tsc --init
or use one of the recommended ones.
Note: Set target
property in tsconfig.json
to es2017
or greater to avoid
FastifyDeprecation warning.
- Create an
index.ts
file - this will contain the server code - Add the following code block to your file:
import fastify from 'fastify'
const server = fastify()
server.get('/ping', async (request, reply) => {
return 'pong\n'
})
server.listen({ port: 8080 }, (err, address) => {
if (err) {
console.error(err)
process.exit(1)
}
console.log(`Server listening at ${address}`)
}) - Run
npm run build
- this will compileindex.ts
intoindex.js
which can be executed using Node.js. If you run into any errors please open an issue in fastify/help - Run
npm run start
to run the Fastify server - You should see
Server listening at http://127.0.0.1:8080
in your console - Try out your server using
curl localhost:8080/ping
, it should returnpong
🏓
🎉 You now have a working Typescript Fastify server! This example demonstrates
the simplicity of the version 3.x type system. By default, the type system
assumes you are using an http
server. The later examples will demonstrate how
to create more complex servers such as https
and http2
, how to specify route
schemas, and more!
For more examples on initializing Fastify with TypeScript (such as enabling HTTP2) check out the detailed API section here
Using Generics
The type system heavily relies on generic properties to provide the most
accurate development experience. While some may find the overhead a bit
cumbersome, the tradeoff is worth it! This example will dive into implementing
generic types for route schemas and the dynamic properties located on the
route-level request
object.
-
If you did not complete the previous example, follow steps 1-4 to get set up.
-
Inside
index.ts
, define three interfacesIQuerystring
,IHeaders
andIReply
:interface IQuerystring {
username: string;
password: string;
}
interface IHeaders {
'h-Custom': string;
}
interface IReply {
200: { success: boolean };
302: { url: string };
'4xx': { error: string };
} -
Using the three interfaces, define a new API route and pass them as generics. The shorthand route methods (i.e.
.get
) accept a generic objectRouteGenericInterface
containing five named properties:Body
,Querystring
,Params
,Headers
andReply
. The interfacesBody
,Querystring
,Params
andHeaders
will be passed down through the route method into the route method handlerrequest
instance and theReply
interface to thereply
instance.server.get<{
Querystring: IQuerystring,
Headers: IHeaders,
Reply: IReply
}>('/auth', async (request, reply) => {
const { username, password } = request.query
const customerHeader = request.headers['h-Custom']
// do something with request data
// chaining .statusCode/.code calls with .send allows type narrowing. For example:
// this works
reply.code(200).send({ success: true });
// but this gives a type error
reply.code(200).send('uh-oh');
// it even works for wildcards
reply.code(404).send({ error: 'Not found' });
return `logged in!`
}) -
Build and run the server code with
npm run build
andnpm run start
-
Query the API
curl localhost:8080/auth?username=admin&password=Password123!
And it should return back
logged in!
-
But wait there's more! The generic interfaces are also available inside route level hook methods. Modify the previous route by adding a
preValidation
hook:server.get<{
Querystring: IQuerystring,
Headers: IHeaders,
Reply: IReply
}>('/auth', {
preValidation: (request, reply, done) => {
const { username, password } = request.query
done(username !== 'admin' ? new Error('Must be admin') : undefined) // only validate `admin` account
}
}, async (request, reply) => {
const customerHeader = request.headers['h-Custom']
// do something with request data
return `logged in!`
}) -
Build and run and query with the
username
query string option set to anything other thanadmin
. The API should now return a HTTP 500 error{"statusCode":500,"error":"Internal Server Error","message":"Must be admin"}
🎉 Good work, now you can define interfaces for each route and have strictly typed request and reply instances. Other parts of the Fastify type system rely on generic properties. Make sure to reference the detailed type system documentation below to learn more about what is available.
JSON Schema
To validate your requests and responses you can use JSON Schema files. If you didn't know already, defining schemas for your Fastify routes can increase their throughput! Check out the Validation and Serialization documentation for more info.
Also it has the advantage to use the defined type within your handlers (including pre-validation, etc.).
Here are some options on how to achieve this.
Fastify Type Providers
Fastify offers two packages wrapping json-schema-to-ts
and typebox
:
And a zod
wrapper by a third party called fastify-type-provider-zod
They simplify schema validation setup and you can read more about them in Type Providers page.
Below is how to setup schema validation using the typebox
,
json-schema-to-typescript
, and json-schema-to-ts
packages without type
providers.