DEV Community

bricoo
bricoo

Posted on • Updated on

Migrate Existing Fastify Project to Use TypeScript

Several developers don't use TypeScript when they are starting a project or just trying to implement their new idea. TypeScript could be cumbersome and slow down the development process due to the need to add more code. However, when the project grows larger, it is inevitable that TypeScript can help a lot by catching bug and typing errors. This guide will show you how to migrate your already growing project in JavaScript to TypeScript. Specifically, this post will guide you how to migrate to TypeScript if you use Fastify as your framework.


1. Install Packages

Install these packages by adding --save-dev option

  • @types/node
  • 'typescript'
  • ts-node
  • 'fastify-tsconfig'
  • 'concurrently'
npm i -D @types/node typescript ts-node fastify-tsconfig concurrently
Enter fullscreen mode Exit fullscreen mode

If you want to use a testing library tap, also add @types/tap in devDependencies

npm i -D tap @types/tap
Enter fullscreen mode Exit fullscreen mode

2. package.json

Change the script field of package.json into these lines

"scripts": {
    "start": "npm run build:ts && fastify start -l info dist/app.js",
    "build:ts": "tsc",
    "watch:ts": "tsc -w",
    "dev": "npm run build:ts && concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\"",
    "dev:start": "fastify start --ignore-watch=.ts$ -w -l info -P dist/app.js"
  },
Enter fullscreen mode Exit fullscreen mode

If you want to enable testing using tap, add this line into scripts

"test": "npm run build:ts && tsc -p test/tsconfig.json && tap --ts \"test/**/*.test.ts\"",
Enter fullscreen mode Exit fullscreen mode

3. tsconfig.json

Create a new tsconfig.json inside the root directory.

{
  "extends": "fastify-tsconfig",
  "compilerOptions": {
    "outDir": "dist",
    "sourceMap": true
  },
  "include": ["src/**/*.ts"]
}
Enter fullscreen mode Exit fullscreen mode

Note: Modify the include field to your project needs. In my case, all my app is stored inside a /src folder.


4. The App Itself

 

Change all files that ends with .js to .ts.

 

app.js or server.js or index.js

This file is the main entry point of your Node.js application

Before:

module.exports = function (fastify, opts) { ... }
Enter fullscreen mode Exit fullscreen mode

After:

export default function(fastify: FastifyInstance, opts: FastifyServerOptions) { ... }
Enter fullscreen mode Exit fullscreen mode

 

Plugins

Change require("module") to import module from "module", including fastify-plugin.

Before (mongodb.js):

const fp = require('fastify-plugin')

module.exports = fp(async function (fastify, opts) {
  fastify.register(require('@fastify/mongodb'))
})
Enter fullscreen mode Exit fullscreen mode

After (mongodb.ts):

import fp from 'fastify-plugin'
import fastifyMongodb from '@fastify/mongodb'

export default fp(async function (fastify, opts) {
  fastify.register(fastifyMongodb)
})
Enter fullscreen mode Exit fullscreen mode

By using import, TypeScript knows that you will call fastify.mongo somewhere inside your app. Note that Fastify stated that "declaration merging is not very smart" i.e. if you import the plugin somewhere but did not call fastify.register, then TypeScript still assumes that you already registered it.

 

Furthermore, some plugins have additional rules. Be sure to check their documentation. For example, if you use @fastify/env to load your .config file, you need to add this code.

declare module 'fastify' {
    interface FastifyInstance {
        config: {
            field1: string,
            field2: string
        };
    }
}

export default fp(async function (fastify, opts) {
    const schema = { ... }; // JSON-schema that match data
    const data = { field1: 'Value 1', field2: 'Value 2' };

    fastify.register(fastifyEnv, {
        schema: schema,
        data: data
    })
})
Enter fullscreen mode Exit fullscreen mode

 

Custom Request

You might want to access a specific request.body or request.query. To tell TypeScript that a request has these fields, you need to create a new type that based on FastifyRequest. Example:

type CustomRequest = FastifyRequest<{
    Body: {
        username: string
    },
    Querystring: {
        product_id: string
    }
}>

function root(fastify: FastifyInstance, request: CustomRequest, reply: FastifyReply) {
    const username = request.body.username;
    const productId = request.query.product_id;
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Further Readings

This guide mainly came from how fastify-cli generates a new Fastify project with TypeScript in mind. Check this out to read more.

If you have more issues, feel free to comment down below and I will try to help you. Here are other links that might help you.

Top comments (0)