Skip to main content
imvinojanv.dev
  • About
  • Blog
  • Projects
  • Snippets

Command Palette

Search for a command to run...

imvinojanv.dev
Open to Hire

I'm always open to discussing software engineering work or partnership.

HomeAboutResumeUses
BlogProjectsSnippets
© 2026 Vinojan Veerapathirathasan —— Colombo, Sri Lanka.
MediumGitHubLinkedInRSS
Back

Build a Token based RESTful APIs with Fastify, Prisma & TypeScript — Part 2

Continue building secured product API routes with Fastify, Prisma & TypeScript and add API documentation using Swagger.

Published on April 30, 2024
6 min read
Build a Token based RESTful APIs with Fastify, Prisma & TypeScript — Part 2

Hi there👋, welcome to the second part of the series of Token-based Authenticated API using Fastify.

In this part, I’ll guide you to building the secured product APIs. Let’s start to continue the development.

#1: Create the folder structure for product

File Structure
.
├── src
  ├── app.ts
  ├── modules
  ├── product
  │  ├── product.route.ts
  │  ├── product.schema.ts
  │  ├── product.controller.ts
  │  └── product.service.ts
  └── user
    ├── user.route.ts
    ├── user.schema.ts
    ├── user.controller.ts
    └── user.service.ts

Create those four files within the product folder.

#2: Product Schemas

  1. Create schemas
product.schema.ts
import * as z from "zod";
import { buildJsonSchemas } from "fastify-zod";
// Define the properties
const productInput = {
  // users input properties
  title: z.string(),
  price: z.number(),
  content: z.string().optional(), // optional
};
const productGenerated = {
  // automatically generated when the product is created
  id: z.number(),
  createdAt: z.string(),
  updatedAt: z.string(),
};
// Define the schemas
const createProductSchema = z.object({
  ...productInput,
});
const productResponseSchema = z.object({
  ...productInput,
  ...productGenerated,
});
const productsResponseSchema = z.array(productResponseSchema);
// Export the type
export type CreateProductInput = z.infer<typeof createProductSchema>;
// Export the schemas
export const { schemas: productSchemas, $ref } = buildJsonSchemas({
  createProductSchema,
  productResponseSchema,
  productsResponseSchema,
});

Let’s take a look at what is happening above:

  • First, we separate the properties into two, such as productInput which will be input by users, and productGenerated which will be generated automatically when the user creates a product.
  • Define those properties according to our prisma schema
  • Export the type of CreateProductInput.
  • Then create schemas for createProductSchema, productResponseSchema and export them with buildJsonSchemas.
  1. Register the schema with fastify
src/app.ts
import { productSchemas } from "./modules/product/product.schema";
// other codes
async function main() {
  for (const schema of [...userSchemas, ...productSchemas]) {
    // It should be before you register your routes
    fastify.addSchema(schema);
  }
  // other codes
}

#3: Product Services

Create services that will be associated with the product controller

product.service.ts
import { db } from "../../utils/prisma";
import { CreateProductInput } from "./product.schema";
export async function createProduct(
  data: CreateProductInput & { ownerId: number },
) {
  return db.product.create({
    data,
  });
}

#4: Product Controller

Create createProductHandler controller

product.controller.ts
import { FastifyReply, FastifyRequest } from "fastify";
import { CreateProductInput } from "./product.schema";
import { createProduct } from "./product.service";
export async function createProductHandler(
  request: FastifyRequest<{
    Body: CreateProductInput;
  }>,
  reply: FastifyReply,
) {
  const product = await createProduct({
    ...request.body,
    ownerId: request.user.id,
  });
  return product;
}

#5: Product route

  1. Create productRoutes.
src/modules/product.route.ts
import { FastifyInstance } from "fastify";
import { createProductHandler } from "./product.controller";
import { $ref } from "./product.schema";
async function productRoutes(fastify: FastifyInstance) {
  fastify.post(
    "/",
    {
      preHandler: [fastify.authenticate],
      schema: {
        body: $ref("createProductSchema"),
        response: {
          201: $ref("productResponseSchema"),
        },
      },
    },
    createProductHandler,
  );
}
export default productRoutes;
  1. Register the productRoutes with fastify
src/app.ts
import productRoutes from "./modules/product/product.route";
// other codes
async function main() {
  // ...
  fastify.register(userRoutes, { prefix: "api/users" }); // user routes
  fastify.register(productRoutes, { prefix: "api/products" }); // product routes
  // ...
}

#6: Optional (Error fixing)

I think some of you might get some error message in your terminal.⚠️ Don’t bother to make such changes in the _product.schema.ts_ file where you export the schemas.

product.schema.ts
// other codes
export const { schemas: productSchemas, $ref } = buildJsonSchemas(
  {
    createProductSchema,
    singleProductResponseSchema,
    productsResponseSchema,
  },
  {
    $id: "productSchemas", // Ensure the root $id is unique
  },
);

Great! 👏 It’s time to check the createProduct route is working successfully or not. Let’s play around with the Postman. 🚀

#7: Get all products (Public API endpoint)

  1. Create the controller for getProductsHandler
product.controller.ts
import { createProduct, getProducts } from "./product.service";
// createProductHandler fonction
export async function getProductsHandler() {
  const products = await getProducts();
  return products;
}
  1. Create getProducts function as service
product.service.ts
import { db } from "../../utils/prisma";
import { CreateProductInput } from "./product.schema";
// createProduct function
export async function getProducts() {
  return db.product.findMany({
    include: {
      owner: {
        select: {
          id: true,
          name: true,
        },
      },
    },
  });
}
  1. Create get products route
src/modules/product.route.ts
import { createProductHandler, getProductsHandler } from "./product.controller";
async function productRoutes(fastify: FastifyInstance) {
  // create product route
  fastify.get(
    // public API endpoint
    "/",
    {
      schema: {
        response: {
          201: $ref("productsResponseSchema"),
        },
      },
    },
    getProductsHandler,
  );
}

We done this product part🫣, Let’s see the responses on Postman.

Figure 1 : Create product & Get products are working successfully

#8: Documenting API endpoints with Swagger

I’ll guide you how to properly documenting the back-end APIs. Let’s start to make the documentation part together 🤝

#9: Install Swagger Dependencies

npm install @fastify/swagger @fastify/swagger-ui

#10: Register and execute the Swagger

src/app.ts
// other codes
async function main() {
  // ...
  fastify.register(require("@fastify/swagger"), {});
  fastify.register(require("@fastify/swagger-ui"), {
    routePrefix: "/docs",
    swagger: {
      info: {
        title: "Fastify Prisma REST API",
        description: "A REST API built with Fastify, Prisma and TypeScript",
        version: "1.0.0",
        contact: {
          name: "Vinojan Abhimanyu",
          url: "https://vinojan.online",
          email: "imvinojanv@gmail.com",
        },
      },
      externalDocs: {
        url: "https://github.com/imvinojanv/fastify-prisma-rest-api",
        description: "Fastify Tutorial source code is on GitHub",
      },
      host: "0.0.0.0:3000",
      basePath: "/",
      schemes: ["http", "https"],
      consumes: ["application/json"],
      produces: ["application/json"],
    },
    uiConfig: {
      docExpansion: "none", // expand/not all the documentations none|list|full
      deepLinking: true,
    },
    staticCSP: false,
    transformStaticCSP: (header: any) => header,
    exposeRoute: true,
  });
  // Executes Swagger
  fastify.ready((err) => {
    if (err) throw err;
    fastify.swagger();
  });
  // ...
}

📑 Swagger docs will be successfully executed at 👇 [http://localhost:3000/docs](http://localhost:3000/docs)

Figure 2 : Swagger UI

I appreciate you taking the time to read this article.🙌

In this part, We learned how to implement the general API secured with authentication and how to create a public routes and how to documenting the API endpoints using Swagger. I hope you are able to create a scalable APIs with the proper documentation from now on. I’m so glad I taught you ☺️

Part 1: https://imvinojanv.dev/blog/build-a-token-based-restful-apis-with-fastify-prisma-typescript-part-1

Source code: https://github.com/imvinojanv/fastify-prisma-rest-api

Before you move on to explore the next article, don’t forget to give your claps 👏 for this article and share with your friends. Stay connected with me on social media. Thanks for your support and have a great rest of your day! 🎊

✍️ Vinojan Veerapathirathasan.

0
0
0
0
0

On This Page

#1: Create the folder structure for product#2: Product Schemas#3: Product Services#4: Product Controller#5: Product route#6: Optional (Error fixing)#7: Get all products (Public API endpoint)#8: Documenting API endpoints with Swagger#9: Install Swagger Dependencies#10: Register and execute the Swagger
Last updated: April 30, 2024