bugl
bugl
HomeLearnPatternsSearch
HomeLearnPatternsSearch

Loading lesson path

Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript with Node.js

Concept visual

TypeScript with Node.js

push / pop from the top({[← top

Why Use TypeScript with Node.js? TypeScript brings static typing to Node.js development, providing better tooling, improved code quality, and enhanced developer experience.

Key benefits include:

Type safety for JavaScript code

Better IDE support with autocompletion

Early error detection during development

Improved code maintainability and documentation

Easier refactoring

Prerequisites:

Formula

Install a recent Node.js LTS (v18 + recommended) and npm.
Verify with node - v and npm - v.

Setting Up a TypeScript Node.js Project This section walks through creating a new Node.js project configured for TypeScript.

Note:

You write TypeScript (.ts ) during development and compile it to JavaScript (.js ) for Node.js to run in production.

  1. Initialize a New Project

Example mkdir my-ts-node-app cd my-ts-node-app npm init -y npm install typescript @types/node --save-dev npx tsc --init

What these do:

typescript adds the TypeScript compiler ( tsc )

Formula

@types/node provides Node.js type definitions npx tsc -- init creates a tsconfig.json config file
  1. Create a Source Folder

Keep source code in src/

and compiled output in dist/. mkdir src

Formula

# later add files like: src/server.ts, src/middleware/auth.ts
  1. Configure TypeScript Edit the generated tsconfig.json

Example

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

Option highlights:

rootDir / outDir : keeps source ( src ) separate from build output ( dist ). strict : enables the safest type checking. esModuleInterop

Formula

: smoother interop with CommonJS/ES modules.

sourceMap : generate maps for debugging compiled code.

CommonJS vs ESM:

This guide uses module: "commonjs". If you use ESM ( type: "module" in package.json ), set module: "nodenext" or node16, and use import / export consistently.

  1. Install Runtime and Dev Dependencies Install Express for HTTP handling and helpful dev tools:

Example npm install express body-parser npm install --save-dev ts-node nodemon @types/express

Warning:

Formula

Use ts - node and nodemon only for development.
For production, compile with tsc and run Node on the JS output.

Project Structure

Keep your project organized:

Formula

my - ts - node - app/

src/ server.ts middleware/ auth.ts entity/ User.ts config/ database.ts dist/ node_modules/ package.json tsconfig.json

Basic TypeScript Server Example

This example shows a minimal Express server written in TypeScript, including a typed User model and a few routes.

src/server.ts import express, { Request, Response, NextFunction } from 'express';
import { json } from 'body-parser';
interface User {
id: number;
username: string;
email: string;
}
// Initialize Express app const app = express();
const PORT = process.env.PORT || 3000;
// Middleware app.use(json());

Formula

// In - memory database const users: User[] = [
{ id: 1, username: 'user1', email: 'user1@example.com' },
{ id: 2, username: 'user2', email: 'user2@example.com' }
];

Formula

// Routes app.get('/api/users', (req: Request, res: Response) => {
res.json(users);
});
app.get('/api/users/:id', (req: Request, res: Response) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ message: 'User not found' });
res.json(user);
});
app.post('/api/users', (req: Request, res: Response) => {
const { username, email } = req.body;
if (!username || !email) {
return res.status(400).json({ message: 'Username and email are required' });
}
const newUser: User = {

Formula

id: users.length + 1, username, email
};
users.push(newUser);
res.status(201).json(newUser);
});
// Error handling middleware app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).json({ message: 'Something went wrong!' });
});
// Start server app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

What TypeScript adds here:

Typed

Request, Response, and NextFunction for Express handlers. A User interface to guarantee the shape of user data. Safer refactoring and better autocompletion with typed route params and bodies.

Using TypeScript with Express Middleware

Middleware can be strongly typed. You can also extend Express types via declaration merging to store authenticated user data on the request.

src/middleware/auth.ts import { Request, Response, NextFunction } from 'express';
// Extend the Express Request type to include custom properties declare global {
namespace Express {
interface Request {
user?: { id: number; role: string };
}
}
}
export const authenticate = (req: Request, res: Response, next: NextFunction) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
try {
// In a real app, verify the JWT token here const decoded = { id: 1, role: 'admin' }; // Mock decoded token req.user = decoded;
next();
} catch (error) {
res.status(401).json({ message: 'Invalid token' });
}
};
export const authorize = (roles: string[]) => {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ message: 'Not authenticated' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ message: 'Not authorized' });
}
next();
};
};

Use the middleware in routes

// src/server.ts import { authenticate, authorize } from './middleware/auth';
app.get('/api/admin', authenticate, authorize(['admin']), (req, res) => {
res.json({ message: `Hello admin ${req.user?.id}` });
});
TypeScript with Database (TypeORM Example)

You can use ORMs like TypeORM with TypeScript decorators to map classes to tables.

Before you start:

Install packages:

Formula

npm install typeorm reflect - metadata pg

(use pg for PostgreSQL). Enable in tsconfig.json when using decorators:

{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}

Formula

Import reflect - metadata once at app startup.
src/entity/User.ts import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
username: string;
@Column({ unique: true })
email: string;
@Column({ select: false })
password: string;
@Column({ default: 'user' })
role: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
src/config/database.ts import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { User } from '../entity/User';
export const AppDataSource = new DataSource({
type: 'postgres', host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), username: process.env.DB_USERNAME || 'postgres', password: process.env.DB_PASSWORD || 'postgres', database: process.env.DB_NAME || 'mydb', synchronize: process.env.NODE_ENV !== 'production', logging: false, entities: [User], migrations: [], subscribers: [],
});

Initialize the Data Source before starting the server

// src/server.ts import { AppDataSource } from './config/database';
AppDataSource.initialize().then(() => {
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
}).catch((err) => {
console.error('DB init error', err);
process.exit(1);
});

Previous

TypeScript Configuration

Next

TypeScript Tooling