So that when someone else contributes to our application’s source code, or other frontend developers can know the endpoints they can use to interact with the User App, we need to write documentation for the API. In this section, I will use Swagger.
First, in the docs folder, create a swagger folder. In this swagger folder, create the files helpers.ts, index.ts and type.ts.
Add code into type.ts first.
import type { RequestHandler } from "express";
export type HTTPMethod = "get" | "post" | "put" | "delete" | "patch";
export type TSwaggerResponse = {
description: string;
content?: {
[contentType: string]: {
schema: object;
};
};
};
export type TSwaggerSchema = {
type: string;
items?: TSwaggerSchema;
properties?: {
[K: string]: TSwaggerSchema;
};
};
export type TSwaggerParameters = {
name: string;
in: string;
required?: boolean;
schema: TSwaggerSchema;
description?: string;
};
export type TRouteDefinition = {
method: HTTPMethod;
path: string;
handler: RequestHandler;
security?: {
bearerAuth?: Array<any>;
};
summary?: string;
description?: string;
tags?: Array<string>;
parameters?: Array<TSwaggerParameters>;
requestBody?: {
required?: boolean;
content: {
[contentType: string]: {
schema: TSwaggerSchema;
};
};
};
responses?: {
[statusCode: string]: TSwaggerResponse;
};
};

In helpers.ts, we will add the following code.
Import some things:
// Import types
import type { Express } from "express";
import type {
TRouteDefinition,
TSwaggerResponse,
TSwaggerSchema,
} from "./type";
Function to create a JSON response used in the OpenAPI schema:
/**
* Tạo một phản hồi Json.
*
* @param status - http status code.
* @param schema - response schema.
* @param [description="Success"].
*/
export function jsonResponse(
status: string,
schema: TSwaggerSchema,
description = "Success",
): Record<string, TSwaggerResponse> {
return {
[status]: {
description,
content: {
"application/json": { schema },
},
},
};
}
Function to add route definitions into Swagger Docs. Because I won’t be doing it in doc string format, I need to create this function.
/**
* Thêm các routes trong một group route vào trong doc.
*
* @param app - express app.
* @param routes - các routes đã được định nghĩa.
* @param swaggerDoc - main swagger doc.
*
* @returns
*/
export function registerRoutes(
app: Express,
routes: TRouteDefinition[],
swaggerDoc: any,
) {
routes.forEach((route) => {
// Swagger Path
const swaggerPath = route.path.replaceAll(/:(\w+)+/g, "{$1}");
// Đăng ký vào Express
(app as any)[route.method](route.path, route.handler);
// Đăng ký vào Swagger
swaggerDoc.paths[swaggerPath] = swaggerDoc.paths[swaggerPath] || {};
(swaggerDoc.paths[swaggerPath] as any)[route.method] = {
summary: route.summary,
description: route.description,
tags: route.tags,
parameters: route.parameters,
requestBody: route.requestBody,
responses: route.responses,
};
});
}

Finally, in index.ts, we will define some options and create an instance of the Swagger doc.
First, import some things:
import path from "path";
import swaggerJSDoc from "swagger-jsdoc";
// Import constants
import { APP_CONSTANTS } from "../../../utils/constants";
const options = {
definition: {
openapi: "3.0.0",
info: {
title: "Cognito Example Application with Typescript",
version: "1.0.0",
description:
"Tài liệu API cho Cognito Example Application with Typescript",
},
components: {
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
},
security: [
{
bearerAuth: [],
},
],
servers: [
{
url: `http://${APP_CONSTANTS.HOST}:${APP_CONSTANTS.PORT}`,
},
],
},
apis: [path.join(__dirname, "./routes/*.js")]
};

With this, the basics in the core are almost ready. After this part, we will start building the features.