I recently coded a Node server with express.js in Typescript. This is a typical combination for backend development. It is especially beneficial if you already have experience with frontend development in Typescript. In general I strongly recommend to use Typescript instead of Javascript for frontend as well as backend development. Typescript uses Javascript but is type based and therefore not as error prone!
Also, the focus should always be on clean code and it should be properly documented. Especially when a project gets bigger, proper documented work pays off. For example, to document REST endpoints it is recommended to use Swagger. The Swagger API documentation provides an overview of all endpoints and even the possibility to interact with them.
This might also be interesting for you: Angular Form with clickable SVG
List of components
- IDE (for example VS Code)
- Node.js
Documentation with swagger
Although Swagger is quite well known, I could not find a detailed implementation guide. Therefore I would like to explain 2 ways to implement Swagger in an existing node server. The first way is to add Swagger parameters directly to each endpoint. This is probably the faster variant, but can get messy depending on the number of endpoints. The second option is to create a “swagger.json” file that summarizes the parameters of the endpoints.
To use Swagger the following library and its type extension will be needed:
npm install --save swagger-ui-express npm install --save-dev @types/swagger-ui-express
Swagger creates documentation of the endpoints:
These can be opened and you can see an example request and an example response:
The “Try it out” button can be used to interact directly with the endpoint.
Swagger API documentation with params
This variant is more recommended for smaller projects. First, these two Swagger libraries must be integrated into the project:
npm install --save-dev @types/swagger-jsdoc npm install --save swagger-jsdoc
The libraries are configured in app.ts or for non Typescript users app.js. Due to the configuration set here, the documentation is located in “/api-docs”:
import express from "express"; import bodyParser from "body-parser"; import exampleRoutes from "./routes/example-route"; import swaggerJSDoc from "swagger-jsdoc"; import swaggerUi from "swagger-ui-express"; const app = express(); const options = { definition: { openapi: "3.0.1", info: { title: "REST API for Swagger Documentation", version: "1.0.0", }, schemes: ["http", "https"], servers: [{ url: "http://localhost:3000/" }], }, apis: [ `${__dirname}/routes/example-route.ts`, "./dist/routes/example-route.js", ], }; const swaggerSpec = swaggerJSDoc(options); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); app.use(bodyParser.json()); app.use(exampleRoutes); app.listen(3000);
The endpoint is described with the parameters as follows:
import { Router } from "express"; import { exampleFunction } from "../controller/example"; const router = Router(); /** * @swagger * /example: * post: * summary: Send the text to the server * tags: * - ExampleEndpoints * description: Send a message to the server and get a response added to the original text. * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * responseText: * type: string * example: This is some example string! This is an endpoint * responses: * 201: * description: Success * content: * application/json: * schema: * type: object * properties: * text: * type: string * example: This is some example string! * 404: * description: Not found * 500: * description: Internal server error */ router.post("/example", exampleFunction); export default router;
Swagger API documentation with swagger.json
To have a better overview and not to mix the documentation with the actual code, it is recommended to use one, or even more json files. When using TypeScript, the Swagger.json file must be placed in the rootDirectory:
Again, the configuration is first defined in app.ts or, for non-Typescript users, in app.js. The domain path “/api-docs” is also selected here for the documentation:
import express from "express"; import bodyParser from "body-parser"; import exampleRoutes from "./routes/example-route"; import swaggerUi from "swagger-ui-express"; import * as swaggerDocument from "./swagger.json"; const app = express(); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use(bodyParser.json()); app.use(exampleRoutes); app.listen(3000);
Important note: To import JSON files into a Typescript project this must be allowed in tsconfig.json. Also, the JSON file must be located in the root directory.
{ "compilerOptions": { "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "module": "commonjs" /* Specify what module code is generated. */, "rootDir": "./src" /* Specify the root folder within your source files. */, "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, "resolveJsonModule": true /* Enable importing .json files. */, "outDir": "./dist" /* Specify an output folder for all emitted files. */, "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, "strict": true /* Enable all strict type-checking options. */, "skipLibCheck": true /* Skip type checking all .d.ts files. */ } }
The previous Swagger documentation with parameters would thus look like this in swagger.json:
{ "openapi": "3.0.1", "info": { "title": "REST API for Swagger Documentation", "version": "1.0.0" }, "schemes": ["http"], "servers": [{ "url": "http://localhost:3000/" }], "paths": { "/example": { "post": { "tags": ["ExampleEndpoints"], "summary": "Send a text to the server", "description": "Send a message to the server and get a response added to the original text.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExampleSchemaHeader" } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExampleSchemaBody" } } } }, "404": { "description": "Not found" }, "500": { "description": "Internal server error" } } } } }, "components": { "schemas": { "ExampleSchemaBody": { "properties": { "responseText": { "type": "string", "example": "This is some example string! This is an endpoint" } } }, "ExampleSchemaHeader": { "required": ["text"], "properties": { "text": { "type": "string", "example": "This is some example string!" } } } } } }