Learnixo

Node.js & Express · Lesson 1 of 5

Environment Setup & First Express Server

Node.js lets you run JavaScript on the server. Express is the most popular web framework for Node — minimal, unopinionated, and battle-tested. Together they power APIs at companies from small startups to Netflix.


Install Node.js

Use the LTS version. Check at nodejs.org or use a version manager:

Bash
# Using nvm (recommended  lets you switch Node versions)
nvm install --lts
nvm use --lts

# Verify
node --version   # v20.x.x or higher
npm --version    # 10.x.x

Create a Project

Bash
mkdir my-api && cd my-api
npm init -y

Install Express and TypeScript:

Bash
npm install express
npm install -D typescript ts-node-dev @types/node @types/express

Initialize TypeScript:

Bash
npx tsc --init

Update tsconfig.json with sensible defaults:

JSON
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Add scripts to package.json:

JSON
{
  "scripts": {
    "dev": "ts-node-dev --respawn --transpile-only src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

Your First Express Server

src/index.ts

TYPESCRIPT
import express, { Request, Response } from 'express';

const app = express();
const PORT = process.env.PORT ?? 3000;

// Middleware — parse JSON bodies
app.use(express.json());

// Routes
app.get('/', (req: Request, res: Response) => {
  res.json({ message: 'Hello from Express!' });
});

app.get('/health', (req: Request, res: Response) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

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

Run the dev server:

Bash
npm run dev

Open http://localhost:3000 — you'll see the JSON response.


Project Structure

my-api/
├── src/
│   ├── index.ts              ← entry point
│   ├── app.ts                ← Express app (without listen)
│   ├── routes/
│   │   ├── users.ts
│   │   └── products.ts
│   ├── controllers/
│   │   ├── user.controller.ts
│   │   └── product.controller.ts
│   ├── middleware/
│   │   ├── auth.middleware.ts
│   │   └── error.middleware.ts
│   ├── models/               ← TypeScript interfaces + DB models
│   └── services/             ← business logic
├── package.json
└── tsconfig.json

Separate app.ts from index.tsapp.ts creates and exports the Express instance, index.ts starts the server. This makes testing easier (import the app without starting a server).

src/app.ts

TYPESCRIPT
import express from 'express';
import { userRouter } from './routes/users';
import { productRouter } from './routes/products';

const app = express();

app.use(express.json());
app.use('/api/users', userRouter);
app.use('/api/products', productRouter);

export { app };

src/index.ts

TYPESCRIPT
import { app } from './app';

const PORT = process.env.PORT ?? 3000;
app.listen(PORT, () => console.log(`Server on port ${PORT}`));

Environment Variables

Never hardcode secrets. Use a .env file with dotenv:

Bash
npm install dotenv

.env (never commit this):

PORT=3000
DATABASE_URL=postgres://localhost:5432/mydb
JWT_SECRET=supersecretkey

src/index.ts:

TYPESCRIPT
import 'dotenv/config';   // load .env before anything else
import { app } from './app';

const PORT = process.env.PORT ?? 3000;
app.listen(PORT);

Add .env to .gitignore. Commit a .env.example with empty values so other developers know what's needed.


Node.js Module System

Node uses CommonJS (require/module.exports) by default. TypeScript compiles to CommonJS unless you configure otherwise. You'll also see ES Modules (.mjs or "type": "module" in package.json):

TYPESCRIPT
// TypeScript (compiles to CommonJS)
import express from 'express';
export function add(a: number, b: number): number { return a + b; }

// What Node actually runs (CommonJS):
const express = require('express');
function add(a, b) { return a + b; }
module.exports = { add };

Stick with CommonJS for Node.js + TypeScript unless you have a specific reason to use ES Modules.


Useful Dev Tools

Bash
# REST client for testing APIs
npm install -g httpie
http GET localhost:3000/health

# Or use curl
curl http://localhost:3000/health

# nodemon alternative (built into ts-node-dev)
# ts-node-dev auto-restarts when TypeScript files change

Install the Thunder Client VS Code extension or Postman for a GUI API client.


Quick Reference

Init project:   npm init -y
Install:        npm i express && npm i -D typescript ts-node-dev @types/node @types/express
Dev server:     npm run dev  (ts-node-dev --respawn src/index.ts)
Build:          tsc → dist/
Production:     node dist/index.js
Environment:    dotenv → process.env.VAR_NAME
Split app:      app.ts (express()) vs index.ts (app.listen())