Express Typescript example

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:
Node.js Typescript Express with MySQL example
Node.js Typescript Express with Postgres example


Express

Express is a popular web application framework for Node.js, which is built on top of Node.js’s HTTP module. It provides a simple and minimalistic approach to building web applications and APIs.

Here are some key features and concepts of the Express framework:

  • Routing: Express allows you to define routes to handle different HTTP methods (GET, POST, PUT, DELETE, etc.) and URL patterns. Routes are used to map specific URLs to specific handler functions, allowing you to handle incoming requests and generate appropriate responses.
  • Middleware: Middleware functions are a core concept in Express. They are functions that have access to the request and response objects and can perform actions, modify the request/response, or pass control to the next middleware function in the chain. Middleware functions can be used for tasks like logging, authentication, error handling, parsing request bodies, and more.
  • Request and Response: Express provides a simplified API for handling HTTP requests and constructing HTTP responses. The req object represents the incoming request and contains properties such as the request method, URL, headers, and query parameters. The res object represents the response that will be sent back to the client and provides methods to set response headers, status codes, and send data.
  • Templating: Express supports integration with various templating engines like EJS, Handlebars, Pug (formerly Jade), and others. Templating engines allow you to generate dynamic HTML pages by rendering templates with data.
  • Static Files: Express makes it easy to serve static files like HTML, CSS, images, and JavaScript files using the express.static middleware. You can specify a directory where your static files are located, and Express will handle serving them directly without the need for additional routing.
  • Error Handling: Express provides mechanisms to handle errors that occur during request processing. You can define error handling middleware that catches errors and handles them in a centralized manner, allowing you to send appropriate error responses or perform specific actions.
  • Integration with Other Middleware and Libraries: Express is designed to be extensible and can be easily integrated with a wide range of middleware and third-party libraries. This allows you to add additional functionality to your applications, such as authentication, session management, database integration, and more.

Express has a large and active community, which has resulted in the development of numerous plugins, middleware packages, and frameworks built on top of Express. This ecosystem provides a rich set of tools and resources to enhance the development experience and extend the capabilities of Express.

To get started with Express, you need to install it as a dependency in your Node.js project using npm or yarn. Once installed, you can create an Express application, define routes and middleware, and start the server to listen for incoming requests.

Typescript

TypeScript is an open-source programming language developed and maintained by Microsoft. It is a superset of JavaScript, which means that any valid JavaScript code is also valid TypeScript code. However, TypeScript adds static typing and other features to JavaScript to enhance its development experience and help catch errors at compile-time rather than runtime.

Here are some key features and concepts of TypeScript:

  • Static Typing: TypeScript introduces static typing, allowing you to explicitly declare the types of variables, function parameters, and return types. This helps catch type-related errors during development and provides better code documentation and editor support.
  • Type Inference: TypeScript has a powerful type inference system that can automatically infer the types of variables based on their initial values. This reduces the need for explicit type annotations while still providing the benefits of static typing.
  • Interfaces: TypeScript supports the definition of interfaces, which are used to define contracts for object structures. Interfaces specify the names and types of properties or methods that an object must have to conform to the interface.
  • Classes: TypeScript introduces classes, allowing you to use object-oriented programming concepts such as inheritance, encapsulation, and polymorphism. Classes in TypeScript can have properties, methods, constructors, and support for access modifiers like public, private, and protected.
  • Modules: TypeScript provides a module system that helps organize and encapsulate code into reusable units. Modules allow you to define public and private members and provide a way to structure larger applications.
  • Generics: TypeScript supports generics, which enable the creation of reusable components that can work with different types. Generics allow you to write code that is more flexible and type-safe by parameterizing types and functions.
  • Decorators: TypeScript supports decorators, which are a way to add metadata or modify the behavior of classes, methods, or properties at design time. Decorators are heavily used in frameworks like Angular for features like dependency injection, component declaration, and more.
  • Tooling and Integration: TypeScript integrates well with modern development tools and workflows. It provides excellent editor support with features like autocompletion, type checking, and refactoring. TypeScript code is transpiled to JavaScript, allowing it to run in any JavaScript environment.

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

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)

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

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"
  }
}

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

./node_modules/.bin/tsc --init

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. */
  }
}

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"
  }
  ...
}

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 }));
  }
}

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);
    }
  });

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

> [email protected] start
> tsc && npm run dev

> [email protected] dev
> node ./build/server.js

Server is running on port 8080.

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;

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." });
}

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);
  }
}

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 {
    // ...
  }
}

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);
  }
}

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;

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!"
      });
    }
  }
}

Run and Check

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

– GET http://localhost:8080/api/tutorials

express-typescript-example-get

– GET http://localhost:8080/api/tutorials/[id]

express-typescript-example-get-one

– POST http://localhost:8080/api/tutorials

express-typescript-example-post

– PUT http://localhost:8080/api/tutorials/[id]

express-typescript-example-put

– DELETE http://localhost:8080/api/tutorials/[id]

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:
Node.js Express File Upload Rest API example using Multer
Google Cloud Storage with Node.js: File Upload example

Source code

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

With Database:
Node.js Typescript Express with MySQL example
Node.js Typescript Express with Postgres example