Express Typescript example

Tien Nguyen - Aug 2 '23 - - Dev Community

Express is one of the most popular web frameworks for Node.js that supports routing, middleware, view system... In this tutorial, I will show you how to build Node.js Rest Api example using Express and Typescript.

Related Posts:

Express Typescript example

We will build Node.js Rest Api using Typescript that handles GET/POST/PUT/DELETE Http requests.

First, we start with an Express web server. Next, we write the controller. Then we define routes for handling all CRUD operations:

The following table shows overview of the Rest APIs that will be exported:

Methods Urls Actions
GET api/tutorials get all Tutorials
GET api/tutorials/:id get Tutorial by id
POST api/tutorials add new Tutorial
PUT api/tutorials/:id update Tutorial by id
DELETE api/tutorials/:id remove Tutorial by id

Finally, we're gonna test the Express Typescript Rest Api using Postman.

Our project structure will be like this:

express-typescript-example-project

Create Node.js Typescript application

Open terminal/console, then create a folder for our application:



$ mkdir express-typescript-example
$ cd express-typescript-example


Enter fullscreen mode Exit fullscreen mode

Initialize the Node.js application with a package.json file:



npm init

package name: (express-typescript-example) express-typescript-example
version: (1.0.0)
description: Rest API using Node.js, TypeScript, Express
entry point: (index.js) server.js
test command:
git repository:
keywords: nodejs, typescript, express, restapi, rest api, crud
author: bezkoder
license: (ISC)
About to write to D:\Projects\NodeTs\node-js-typescript-express-mysql\package.json:

{
  "name": "express-typescript-example",
  "version": "1.0.0",
  "description": "Rest API using Node.js, TypeScript, Express",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "nodejs",
    "typescript",
    "express",
    "restapi",
    "rest",
    "api",
    "crud"
  ],
  "author": "bezkoder",
  "license": "ISC"
}

Is this OK? (yes)


Enter fullscreen mode Exit fullscreen mode

Add Express and Typescript into Node.js Project

We need to install necessary modules: express, typescript, cors, ts-node, @types/node, @types/express and @types/cors.

Run the command:



npm install typescript ts-node @types/node @types/express @types/cors --save-dev
npm install express cors


Enter fullscreen mode Exit fullscreen mode

The package.json file should look like this:



{
  "name": "express-typescript-example",
  "version": "1.0.0",
  "description": "Rest API using Node.js, TypeScript, Express",
  "main": "server.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "express",
    "typescript",
    "rest",
    "api",
    "restapi",
    "node",
    "nodejs",
    "crud"
  ],
  "author": "bezkoder",
  "license": "ISC",
  "devDependencies": {
    "@types/cors": "^2.8.13",
    "@types/express": "^4.17.17",
    "@types/node": "^20.3.3",
    "ts-node": "^10.9.1",
    "typescript": "^5.1.6"
  },
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.2"
  }
}


Enter fullscreen mode Exit fullscreen mode

Next, we generate a tsconfig.json file with command:



./node_modules/.bin/tsc --init


Enter fullscreen mode Exit fullscreen mode

Open tsconfig.json and modify the content like this:



{
  "compilerOptions": {
    /* Language and Environment */
    "target": "es2016",                               /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
    "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */

    /* Modules */
    "module": "commonjs",                             /* Specify what module code is generated. */
    "resolveJsonModule": true,                        /* Enable importing .json files. */

    /* Emit */
    "outDir": "./build",                              /* Specify an output folder for all emitted files. */

    /* Interop Constraints */
    "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. */

    /* Type Checking */
    "strict": true,                                   /* Enable all strict type-checking options. */

    /* Completeness */
    "skipLibCheck": true                              /* Skip type checking all .d.ts files. */
  }
}


Enter fullscreen mode Exit fullscreen mode

To work with TypeScript, we need TypeScript compiler (tsc), which converts TypeScript code into JavaScript (inside ./build folder). TypeScript files have the .ts extension. Once compiled to JavaScript, we can run the resulting JavaScript files using a JavaScript runtime environment or include them in web applications.

So we modify "scripts" property of package.json file by adding build, dev and start like this:



{
  ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "dev": "node ./build/server.js",
    "start": "tsc && npm run dev"
  }
  ...
}


Enter fullscreen mode Exit fullscreen mode

Create Express Typescript server

In src folder, create index.ts file that export Server class.



import express, { Application } from "express";
import cors, { CorsOptions } from "cors";

export default class Server {
  constructor(app: Application) {
    this.config(app);
  }

  private config(app: Application): void {
    const corsOptions: CorsOptions = {
      origin: "http://localhost:8081"
    };

    app.use(cors(corsOptions));
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
  }
}


Enter fullscreen mode Exit fullscreen mode

What we do are:

  • import express, and cors modules:
    • Express is for building the Rest Apis
    • cors provides Express middleware to enable CORS with various options.
  • define constructor() method that receives Express Application object as parameter.
  • in constructor(), we call config() method that adds body-parser (json and urlencoded) and cors middlewares using app.use() method. Notice that we set origin: http://localhost:8081.

We continue to create server.ts outside the src folder.



import express, { Application } from "express";
import Server from "./src/index";

const app: Application = express();
const server: Server = new Server(app);
const PORT: number = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080;

app
  .listen(PORT, "localhost", function () {
    console.log(`Server is running on port ${PORT}.`);
  })
  .on("error", (err: any) => {
    if (err.code === "EADDRINUSE") {
      console.log("Error: address already in use");
    } else {
      console.log(err);
    }
  });


Enter fullscreen mode Exit fullscreen mode

In the code, we:

  • create an Express application using express().
  • initialize a Server object with an Application object
  • listen on port 8080 for incoming requests.

Let's run the app with command: npm run start.



$ npm run start

> express-typescript-example@1.0.0 start
> tsc && npm run dev

> express-typescript-example@1.0.0 dev
> node ./build/server.js

Server is running on port 8080.


Enter fullscreen mode Exit fullscreen mode

Working with Express Router in Typescript

To handle HTTP requests, we create a new Router object using express.Router() function.

In src/routes folder, create home.routes.ts file that exports Router object.



import { Router } from "express";
import { welcome } from "../controllers/home.controller";

class HomeRoutes {
  router = Router();

  constructor() {
    this.intializeRoutes();
  }

  intializeRoutes() {
    this.router.get("/", welcome);
  }
}

export default new HomeRoutes().router;


Enter fullscreen mode Exit fullscreen mode

router.get("/", welcome) is for handling Http GET requests with welcome as handler function.

In src/controllers/home.controller.ts, we export welcome function.



import { Request, Response } from "express";

export function welcome(req: Request, res: Response): Response {
  return res.json({ message: "Welcome to bezkoder application." });
}


Enter fullscreen mode Exit fullscreen mode

Next we create Routes class in src/routes/index.ts.



import { Application } from "express";
import homeRoutes from "./home.routes";

export default class Routes {
  constructor(app: Application) {
    app.use("/api", homeRoutes);
  }
}


Enter fullscreen mode Exit fullscreen mode

Then we import Routes into Server class's constructor() method and initialize a new Routes object.



// ...
import Routes from "./routes";

export default class Server {
  constructor(app: Application) {
    this.config(app);
    new Routes(app);
  }

  private config(app: Application): void {
    // ...
  }
}


Enter fullscreen mode Exit fullscreen mode

Let's stop and re-start the server. Open your browser with url http://localhost:8080/api, you will see:

express-typescript-example-server

Handling GET-POST-PUT-DELETE requests with Express Typescript

Now we implement more routes to Routes class that follows APIs:

Methods Urls Actions
GET api/tutorials get all Tutorials
GET api/tutorials/:id get Tutorial by id
POST api/tutorials add new Tutorial
PUT api/tutorials/:id update Tutorial by id
DELETE api/tutorials/:id remove Tutorial by id

In src/routes/index.ts, add middleware function for "/api/tutorials" endpoint.



import { Application } from "express";
import homeRoutes from "./home.routes";
import tutorialRoutes from "./tutorial.routes";

export default class Routes {
  constructor(app: Application) {
    app.use("/api", homeRoutes);
    app.use("/api/tutorials", tutorialRoutes);
  }
}


Enter fullscreen mode Exit fullscreen mode

We continue to define the above TutorialRoutes class which initializes a TutorialController object that provides CRUD operation methods. It exports Router object.

src/routes/tutorial.routes.ts



import { Router } from "express";
import TutorialController from "../controllers/tutorial.controller";

class TutorialRoutes {
  router = Router();
  controller = new TutorialController();

  constructor() {
    this.intializeRoutes();
  }

  intializeRoutes() {
    // Create a new Tutorial
    this.router.post("/", this.controller.create);

    // Retrieve all Tutorials
    this.router.get("/", this.controller.findAll);

    // Retrieve a single Tutorial with id
    this.router.get("/:id", this.controller.findOne);

    // Update a Tutorial with id
    this.router.put("/:id", this.controller.update);

    // Delete a Tutorial with id
    this.router.delete("/:id", this.controller.delete);
  }
}

export default new TutorialRoutes().router;


Enter fullscreen mode Exit fullscreen mode

In src/controllers/tutorial.controller.ts, we define and export TutorialController class that has create, findAll, findOne, update, delete methods.



import { Request, Response } from "express";

export default class TutorialController {
  async create(req: Request, res: Response) {
    try {
      res.status(201).json({
        message: "create OK",
        reqBody: req.body
      });
    } catch (err) {
      res.status(500).json({
        message: "Internal Server Error!"
      });
    }
  }

  async findAll(req: Request, res: Response) {
    try {
      res.status(200).json({
        message: "findAll OK"
      });
    } catch (err) {
      res.status(500).json({
        message: "Internal Server Error!"
      });
    }
  }

  async findOne(req: Request, res: Response) {
    try {
      res.status(200).json({
        message: "findOne OK",
        reqParamId: req.params.id
      });
    } catch (err) {
      res.status(500).json({
        message: "Internal Server Error!"
      });
    }
  }

  async update(req: Request, res: Response) {
    try {
      res.status(200).json({
        message: "update OK",
        reqParamId: req.params.id,
        reqBody: req.body
      });
    } catch (err) {
      res.status(500).json({
        message: "Internal Server Error!"
      });
    }
  }

  async delete(req: Request, res: Response) {
    try {
      res.status(200).json({
        message: "delete OK",
        reqParamId: req.params.id
      });
    } catch (err) {
      res.status(500).json({
        message: "Internal Server Error!"
      });
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

Run and Check

Run the Node.js Express Typescript Rest APIs with command:
npm run start

express-typescript-example-get

express-typescript-example-get-one

express-typescript-example-post

express-typescript-example-put

express-typescript-example-delete

Conclusion

Today, we've learned how to create Node.js Rest Apis with an Express Typescript web server. We also know way to write a controller and define routes for handling all CRUD operations.

Happy learning! See you again.

Further Reading

File Upload Rest API:

Source code

You can find the complete source code for this example on Github.

With Database:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .