DEV Community

Cover image for Day 41 of #100DaysOfCode — TypeScript on the Backend (Node.js + Express)
M Saad Ahmad
M Saad Ahmad

Posted on

Day 41 of #100DaysOfCode — TypeScript on the Backend (Node.js + Express)

JavaScript was originally built for browsers (frontend). But with Node.js and Express.js, you can run JavaScript on the server (backend) too.

TypeScript is essentially JavaScript with an added type system. This means that you can write your server-side code using TypeScript, ensuring safer code compared to plain JavaScript.

Today, for Day 41, the goal was to understand the advantages of using TypeScript with Node.js and Express.js in the backend and how to use it.


🤨 Why Bother?

Let's look at a common issue with plain JavaScript.

const user = {
  name: "David",
  age: "25"
};


function getUser(id) {
  return id.toUpperCase();
}
getUser(123);
Enter fullscreen mode Exit fullscreen mode

Here, age should be a number and the function getUser must receive a string, but JavaScript won't warn us.

This can cause bugs later in the application, or worse, can even crash the application.

TypeScript Solution

interface User {
  name: string
  age: number
}


function getUser(id: string) {
  return id.toUpperCase();
}
getUser(123); // ❌ Error: Argument of type 'number' is not assignable to 'string'
Enter fullscreen mode Exit fullscreen mode

Now TypeScript will immediately detect incorrect data types.

Benefits of TypeScript

  • Prevent runtime errors
  • Strong type safety
  • Better code readability
  • Improved IDE autocomplete
  • Safer refactoring
  • Easier collaboration in large teams

Because of these advantages, many modern Node.js backends are built with TypeScript.


Setting Up TypeScript in a Node.js Project

To start using TypeScript, install the required dependencies.

npm install typescript ts-node @types/node --save-dev
Enter fullscreen mode Exit fullscreen mode

Initialize the TypeScript configuration:

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

This creates the configuration file:

tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Important tsconfig.json Options

Option Purpose
target JavaScript version to compile to
module Module system (CommonJS / ESModules)
rootDir Source code directory
outDir Compiled JavaScript output
strict Enables strict type checking

Example project structure:

project
 ├── src
 │   └── server.ts
 ├── dist
 ├── tsconfig.json
 └── package.json
Enter fullscreen mode Exit fullscreen mode

Installing Type Definitions for Express

TypeScript needs type definitions for libraries written in JavaScript.

Install them using:

npm install @types/express
Enter fullscreen mode Exit fullscreen mode

Now you can safely import Express with full type support:

import express from "express";
Enter fullscreen mode Exit fullscreen mode

Converting an Express Server to TypeScript

Rename your server file:

server.js → server.ts
Enter fullscreen mode Exit fullscreen mode

Example Express server written in TypeScript:

import express, { Request, Response } from "express";

const app = express();

app.get("/", (req: Request, res: Response) => {
  res.send("Hello from TypeScript backend");
});

app.listen(5000, () => {
  console.log("Server running on port 5000");
});
Enter fullscreen mode Exit fullscreen mode

Key Concepts

  • Request → represents the incoming HTTP request
  • Response → used to send a response
  • Type imports ensure safer route handling

Creating Interfaces for Data

Interfaces define the structure of objects.

Example:

interface User {
  name: string
  email: string
  age: number
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const user: User = {
  name: "Saad",
  email: "saad@test.com",
  age: 23
};
Enter fullscreen mode Exit fullscreen mode

Why This Matters

  • Prevents invalid object structures
  • Improves readability
  • Helps maintain consistent data models

Typing Request Body in Express

Instead of using type assertions (as), Express allows typing request bodies using generics.

Example login request structure:

interface LoginRequest {
  email: string
  password: string
}
Enter fullscreen mode Exit fullscreen mode

Now type the request body:

app.post(
  "/login",
  (req: Request<{}, {}, LoginRequest>, res: Response) => {

    console.log(req.body.email);

    res.send("Login request received");

});
Enter fullscreen mode Exit fullscreen mode

Request Generic Structure

Request<Params, ResBody, ReqBody, Query>
Enter fullscreen mode Exit fullscreen mode

Example:

Request<{id: string}, {}, LoginRequest, {page: number}>
Enter fullscreen mode Exit fullscreen mode

This allows full type safety for params, body, and query values.


Typing API Responses

It's also helpful to define a consistent response structure.

Example:

interface ApiResponse {
  success: boolean
  message: string
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const response: ApiResponse = {
  success: true,
  message: "Login successful"
};
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Consistent API responses
  • Easier frontend integration
  • Better documentation

TypeScript with Controllers

Controllers can also be typed.

Example controller:

import { Request, Response } from "express";

export const getUsers = (req: Request, res: Response) => {

  res.json({
    message: "Users list"
  });

};
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Strongly typed API handlers
  • Better maintainability
  • Easier debugging

TypeScript with Middleware

Middleware functions can also be typed.

Example authentication middleware:

import { Request, Response, NextFunction } from "express";

export const authMiddleware = (
  req: Request,
  res: Response,
  next: NextFunction
) => {

  console.log("Auth middleware executed");

  next();

};
Enter fullscreen mode Exit fullscreen mode

Key Concept

NextFunction allows middleware to pass control to the next function in the stack.


Running a TypeScript Backend

Run TypeScript directly using ts-node:

npx ts-node src/server.ts
Enter fullscreen mode Exit fullscreen mode

Or compile TypeScript into JavaScript:

npx tsc
Enter fullscreen mode Exit fullscreen mode

Output example:

dist/server.js
Enter fullscreen mode Exit fullscreen mode

Then run:

node dist/server.js
Enter fullscreen mode Exit fullscreen mode

Key Benefits of Using TypeScript in Backend Development

TypeScript offers several advanced advantages with Node.js and Express.js for backend development.

1. TypeScript Project Setup

  • Advanced tsconfig.json
  • Using nodemon with TypeScript
  • Scalable folder structure

2. Advanced Express Typing

  • Typing route params
  • Typing query parameters
  • Full generic usage with Request<>

3. Typing Middleware

  • Extending Request
  • Adding custom properties like req.user

4. Mongoose + TypeScript

  • Typing schemas
  • Typing models
  • Handling database documents safely

5. TypeScript Utility Types

Some utility types are extremely useful:

  • Partial<T>
  • Pick<T>
  • Omit<T>

These help manipulate existing types without rewriting them.


Final Thoughts

Learning TypeScript on the backend significantly improves code reliability and maintainability, especially for large Node.js applications. Even though JavaScript works well, TypeScript helps catch bugs before your code even runs, which is extremely valuable in production systems.

Thanks for reading. Feel free to share your thoughts!

Top comments (0)