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
Contents
- Express
- Typescript
- Express Typescript example
- Create Node.js Typescript application
- Add Express and Typescript into Node.js Project
- Create Express Typescript server
- Working with Express Router in Typescript
- Handling GET-POST-PUT-DELETE requests with Express Typescript
- Run and Check
- Conclusion
- Further Reading
- Source code
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:
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:
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
– GET http://localhost:8080/api/tutorials/[id]
– POST http://localhost:8080/api/tutorials
– PUT http://localhost:8080/api/tutorials/[id]
– DELETE http://localhost:8080/api/tutorials/[id]
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