Node Express Typescript 2024
From Logic Wiki
Setup.
terminal :
npm init -y npm i --save express dotenv npm i -D typescript @types/node @types/express nodemon ts-node npx tsc --init
tsconfig.json
"rootDir": "./src", "outDir": "./dist",
package.json ->
{
"name": "logicmade_api",
"version": "1.0.0",
"description": "API for many things needed",
"main": "index.js",
"scripts": {
"build": "npx tsc",
"start": "node dist/index.js",
"dev": "nodemon src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.11.28",
"nodemon": "^3.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.2"
},
"dependencies": {
"dotenv": "^16.4.5",
"express": "^4.18.3"
}
}
.env file
PORT=3000
./src/index.ts ->
import express, {Express, Request, Response } from "express"
import dotenv from "dotenv"
dotenv.config()
const app: Express = express()
const port = 3000
console.log("env : ", process.env.PORT )
app.get("/", (req:Request, res:Response) => {
res.send("OK")
})
app.listen(port, ()=> {
console.log(`[server]: Server is running at http://localhost:${port}`)
})
Vitest
npm i -D vitest npm i -D @vitest/ui
package.json ->
"scripts": {
...
"test": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage"
},
Morgan (Logging the requests)
npm i -S morgan npm i -D @types/morgan npm i rotating-file-stream
/src/index.ts
import morgan from "morgan"
import { createStream } from "rotating-file-stream"
...
import Router from "./routes"
...
var accessLogStream = createStream('access.log', {
interval: '1d', // rotate daily
path: path.join(__dirname, '../log')
})
...
app.use(express.json());
app.use(morgan('combined', { stream: accessLogStream }));
app.use(express.static("public"));
app.use(Router);
/src/routes/index.ts
import express from "express";
import PingController from "../controllers/ping";
const router = express.Router();
router.get("/ping", async (_req, res) => {
const controller = new PingController();
const response = await controller.getMessage();
return res.send(response);
});
export default router;
/src/controller/ping.ts
interface PingResponse {
message: string;
}
export default class PingController {
public async getMessage(): Promise<PingResponse> {
return {
message: "hello",
};
}
}
Swagger
npm i -S tsoa swagger-ui-express npm i -D @types/swagger-ui-express concurrently
tsconfig.json
{
"compilerOptions": {
...
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
tsoa.json (create)
{
"entryFile": "src/index.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"spec": {
"outputDirectory": "public",
"specVersion": 3
}
}
package.json
"nodemonConfig": {
"watch": [
"src"
],
"ext": "ts",
"exec": "ts-node src/index.ts"
},
"scripts": {
"predev": "npm run swagger",
"prebuild": "npm run swagger",
"build": "npx tsc",
"start": "node dist/index.js",
"dev": "concurrently \"nodemon\" \"nodemon -x tsoa spec\"",
"swagger": "tsoa spec",
...
},
src/index.ts
import express, { Application, Request, Response } from "express";
import morgan from "morgan";
import swaggerUi from "swagger-ui-express";
import Router from "./routes";
const PORT = process.env.PORT || 8000;
const app: Application = express();
app.use(express.json());
app.use(morgan("tiny"));
app.use(express.static("public"));
app.use(
"/docs",
swaggerUi.serve,
swaggerUi.setup(undefined, {
swaggerOptions: {
url: "/swagger.json",
},
})
);
app.use(Router);
src/controllers/ping.ts
import { Get, Route } from "tsoa";
interface PingResponse {
message: string;
}
@Route("ping")
export default class PingController {
@Get("/")
public async getMessage(): Promise<PingResponse> {
return {
message: "hello",
};
}
}
After making all the changes and running the server, visit http://localhost:3000/docs/ to access the APIs documentation.