TypeScript with Node.js

Node.js runs JavaScript on the server side. Adding TypeScript to Node.js gives server code the same type safety and auto-complete benefits that TypeScript brings to browser code. TypeScript catches bugs before the server starts, and the typed APIs make writing HTTP routes, file operations, and database queries predictable and readable.

Project Setup

# Step 1: Create a new project folder
mkdir my-node-app
cd my-node-app

# Step 2: Initialize npm
npm init -y

# Step 3: Install TypeScript and Node.js types
npm install typescript ts-node --save-dev
npm install @types/node --save-dev

# Step 4: Create TypeScript config
npx tsc --init

# Step 5: (Optional) Install nodemon for auto-restart
npm install nodemon --save-dev
// tsconfig.json — recommended settings for Node.js
{
    "compilerOptions": {
        "target": "ES2020",
        "module": "commonjs",
        "rootDir": "src",
        "outDir": "dist",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true
    }
}

File System Operations (Typed)

// src/fileUtils.ts
import * as fs from "fs";
import * as path from "path";

function readTextFile(filePath: string): string {
    let fullPath = path.resolve(filePath);

    if (!fs.existsSync(fullPath)) {
        throw new Error(`File not found: ${fullPath}`);
    }

    return fs.readFileSync(fullPath, "utf-8");
}

function writeTextFile(filePath: string, content: string): void {
    let dir = path.dirname(filePath);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(filePath, content, "utf-8");
    console.log(`File written: ${filePath}`);
}

try {
    let content = readTextFile("data/notes.txt");
    console.log(content);
} catch (error: unknown) {
    if (error instanceof Error) {
        console.log("Error:", error.message);
    }
}

Building an HTTP Server with Types

// src/server.ts
import * as http from "http";

interface RouteHandler {
    (req: http.IncomingMessage, res: http.ServerResponse): void;
}

interface Router {
    [method: string]: {
        [path: string]: RouteHandler;
    };
}

const routes: Router = {
    GET: {
        "/":        (req, res) => sendJSON(res, 200, { message: "Welcome to API" }),
        "/health":  (req, res) => sendJSON(res, 200, { status: "OK" }),
        "/users":   (req, res) => sendJSON(res, 200, { users: ["Tarun", "Simran"] }),
    }
};

function sendJSON(res: http.ServerResponse, statusCode: number, data: object): void {
    res.writeHead(statusCode, { "Content-Type": "application/json" });
    res.end(JSON.stringify(data));
}

const server = http.createServer((req, res) => {
    let method  = req.method  ?? "GET";
    let urlPath = req.url     ?? "/";

    let handler = routes[method]?.[urlPath];

    if (handler) {
        handler(req, res);
    } else {
        sendJSON(res, 404, { error: "Route not found" });
    }
});

const PORT: number = 3000;
server.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`);
});

Express with TypeScript

# Install Express and its types
npm install express
npm install @types/express --save-dev

// src/app.ts
import express, { Request, Response, NextFunction } from "express";

const app = express();
app.use(express.json());

interface User {
    id: number;
    name: string;
    email: string;
}

let users: User[] = [
    { id: 1, name: "Kavya",  email: "kavya@mail.com" },
    { id: 2, name: "Ashish", email: "ashish@mail.com" }
];

// GET all users
app.get("/users", (req: Request, res: Response) => {
    res.json({ users, total: users.length });
});

// GET user by ID
app.get("/users/:id", (req: Request, res: Response) => {
    let id   = parseInt(req.params.id);
    let user = users.find(u => u.id === id);

    if (!user) {
        return res.status(404).json({ error: "User not found" });
    }
    res.json(user);
});

// POST create user
app.post("/users", (req: Request, res: Response) => {
    let body = req.body as { name: string; email: string };

    if (!body.name || !body.email) {
        return res.status(400).json({ error: "name and email are required" });
    }

    let newUser: User = {
        id: users.length + 1,
        name:  body.name,
        email: body.email
    };

    users.push(newUser);
    res.status(201).json(newUser);
});

// DELETE user
app.delete("/users/:id", (req: Request, res: Response) => {
    let id    = parseInt(req.params.id);
    let index = users.findIndex(u => u.id === id);

    if (index === -1) {
        return res.status(404).json({ error: "User not found" });
    }

    users.splice(index, 1);
    res.json({ message: `User ${id} deleted` });
});

app.listen(3000, () => console.log("Server on port 3000"));

Environment Variables with Types

// src/config.ts
interface AppConfig {
    port: number;
    dbUrl: string;
    jwtSecret: string;
    environment: "development" | "production" | "test";
}

function loadConfig(): AppConfig {
    let port = parseInt(process.env.PORT ?? "3000");
    let dbUrl = process.env.DATABASE_URL;
    let jwtSecret = process.env.JWT_SECRET;

    if (!dbUrl) {
        throw new Error("DATABASE_URL environment variable is required");
    }
    if (!jwtSecret) {
        throw new Error("JWT_SECRET environment variable is required");
    }

    let env = (process.env.NODE_ENV ?? "development") as AppConfig["environment"];

    return {
        port,
        dbUrl,
        jwtSecret,
        environment: env
    };
}

export const config = loadConfig();

Running the TypeScript Node.js App

DEVELOPMENT (run directly with ts-node):
  npx ts-node src/app.ts

DEVELOPMENT (auto-restart on change):
  npx nodemon --exec ts-node src/app.ts

PRODUCTION (compile first, then run):
  npx tsc          → compiles .ts to .js in dist/
  node dist/app.js → run compiled JavaScript

package.json scripts:
{
    "scripts": {
        "dev":   "nodemon --exec ts-node src/app.ts",
        "build": "tsc",
        "start": "node dist/app.js"
    }
}

TypeScript Node.js Project Structure

my-node-app/
  ├── src/
  │   ├── app.ts         (Express app setup)
  │   ├── server.ts      (start server)
  │   ├── config.ts      (environment config)
  │   ├── routes/
  │   │   └── users.ts   (user route handlers)
  │   └── types/
  │       └── index.ts   (shared interfaces)
  ├── dist/              (compiled JS — auto-generated)
  ├── tsconfig.json
  └── package.json

Key Packages Summary

PackagePurposeInstall Command
typescriptTypeScript compilernpm i typescript -D
ts-nodeRun .ts files directly without compilingnpm i ts-node -D
@types/nodeTypes for Node.js built-in modulesnpm i @types/node -D
expressHTTP server frameworknpm i express
@types/expressTypes for Expressnpm i @types/express -D
nodemonAuto-restarts server on file changesnpm i nodemon -D

Leave a Comment

Your email address will not be published. Required fields are marked *