TypeScript Error with Custom Fastify Decorator in Separate Declaration File

120 views Asked by At

I'm working on a Fastify application with TypeScript and I've added authentication using @fastify/jwt and @fastify/cookie. I created a custom decorator named authenticate, which I'm using in my route files. However, I'm encountering a TypeScript error when I try to organize my code by moving the decorator type declaration to a separate file.

When I define the authenticate decorator directly in my server.ts, everything works fine. But if I try to move the type declaration to a separate file (e.g., fastify.d.ts in the root of my project and include it in tsconfig.json), TypeScript starts showing an error.

{
  "resource": "/u:/dekada/api/src/models/teacher/teacher.route.ts",
  "owner": "typescript",
  "code": "2339",
  "severity": 8,
  "message": "Property 'authenticate' does not exist on type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.",
  "source": "ts",
  "startLineNumber": 32,
  "startColumn": 41,
  "endLineNumber": 32,
  "endColumn": 53
}

I've defined my custom decorator in fastify.d.ts like this:

import { FastifyInstance } from "fastify";

declare module "fastify" {
  export interface FastifyInstance {
    authenticate: any; // Replace 'any' with a more specific type if possible
  }
}

And my 'tsconfig.json includes:

{
  "include": [
    "src/**/*",
    "fastify.d.ts"
  ],
  "exclude": ["node_modules"]
}

Am I doing something wrong with the authenticate decorator, or should I specify more information in the module declaration? Any insights or suggestions on how to resolve this issue would be greatly appreciated.

This is my server.ts:

import fastify, { FastifyReply, FastifyRequest } from "fastify";
import dotenv from "dotenv";
import { logger } from "./common/utils";
import { jwtPlugin, cookiePlugin } from "./common/plugins";
import { customErrorHandler } from "./common/errors/errorHandler";

declare module "fastify" {
  export interface FastifyInstance {
    authenticate: any;
  }
}

import teacherRoutes from "./models/teacher/teacher.route";

dotenv.config();

const server = fastify({ logger, disableRequestLogging: true });

server.setErrorHandler(customErrorHandler);

// Register plugins
server.register(jwtPlugin);
server.register(cookiePlugin);

//
server.decorate(
  "authenticate",
  async (request: FastifyRequest, reply: FastifyReply) => {
    const token = request.cookies.access_token;
    if (!token) {
      return reply.status(401).send({ message: "Authentication required" });
    }
    try {
      const decoded = await server.jwt.verify(token);
      request.user = decoded;
    } catch (error) {
      return reply.status(401).send({ message: "Authentication required" });
    }
  }
);

// Register routes
server.register(teacherRoutes, { prefix: "api/teachers" });

server.get("/healthcheck", async function () {
  return { status: "OK" };
});

export default server;

This is my index.ts

import { teacherSchemas } from "./models/teacher/teacher.schema";
import server from "./server";

const start = async () => {
  try {
    for (const schema of teacherSchemas) {
      server.addSchema(schema);
    }
    await server.listen({ port: 4000, host: "0.0.0.0" });
    server.log.info("Server listening on port 4000");
  } catch (error) {
    server.log.error(error);
    process.exit(1);
  }
};

start();

And this is my teacher.route.ts:

import { FastifyInstance } from "fastify";
import {
  getTeachersHandler,
  loginTeacherHandler,
  registerTeacherHandler,
} from "./teacher.controller";
import { $ref } from "./teacher.schema";

const teacherRoutes = async (server: FastifyInstance) => {
  server.post(
    "/",
    {
      schema: {
        body: $ref("createTeacherRequestSchema"),
        response: { 201: $ref("createTeacherResponseSchema") },
      },
    },
    registerTeacherHandler
  );

  server.post(
    "/login",
    {
      schema: {
        body: $ref("loginTeacherRequestSchema"),
        response: { 200: $ref("loginTeacherReplySchema") },
      },
    },
    loginTeacherHandler
  );

  server.get("/", { preHandler: [server.authenticate] }, getTeachersHandler);
};

export default teacherRoutes;
1

There are 1 answers

0
2conthanlancon On BEST ANSWER

create src/@types/fastify.d.ts and remember add it in typeRoots

content file:

import * as http from 'http';

declare module 'fastify' {
  export interface FastifyInstance<
    HttpServer = http.Server,
    HttpRequest = http.IncomingMessage,
    HttpResponse = http.ServerResponse,
  > {
    authenticate(): void;
  }
}

in typescript.json

    "typeRoots": [
      "../node_modules/@types",
      "./src/@types/*"
    ]