본문으로 건너뛰기 Express.js Complete Guide | Fast Node.js Web Framework

Express.js Complete Guide | Fast Node.js Web Framework

Express.js Complete Guide | Fast Node.js Web Framework

이 글의 핵심

Express.js is the most popular web framework for Node.js. It provides a minimal, flexible, and robust foundation for building web applications and REST APIs.

Introduction

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It’s the de facto standard for Node.js backends.

Why Express?

Native Node.js HTTP:

const http = require('http');

http.createServer((req, res) => {
  if (req.url === '/users' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ users: [] }));
  }
  // ... manual routing for every endpoint
}).listen(3000);

With Express:

const express = require('express');
const app = express();

app.get('/users', (req, res) => {
  res.json({ users: [] });
});

app.listen(3000);

Real-World Adoption

Express is the industry standard for Node.js web development:

Enterprise Usage:

  • Uber: Powers backend services handling millions of ride requests with Express
  • IBM: Uses Express for enterprise APIs and cloud services
  • PayPal: Migrated from Java to Node.js/Express, reducing response time by 35% and development time by 50%
  • Netflix: Employs Express for internal tools and microservices supporting 260M+ subscribers
  • Twitter: Uses Express for internal dashboards and analytics platforms
  • NASA: Built mission-critical systems with Express for spacecraft operations

Market Dominance:

  • 25+ million weekly npm downloads (April 2026) - one of the most downloaded npm packages ever
  • 65,000+ GitHub stars - the most popular Node.js framework
  • Used in 4+ million public GitHub repositories
  • 10+ years in production - first released in 2010, continuously maintained
  • Powers an estimated 30-40% of all Node.js web servers globally

Why Express Leads:

  • Minimal & unopinionated: You choose your architecture and tools
  • Massive ecosystem: 20,000+ compatible middleware packages
  • Battle-tested: Proven in production for over a decade
  • Easy learning curve: Simple API that feels natural to JavaScript developers
  • Performance: Handles 10,000+ requests/second on modest hardware
  • Community support: Millions of StackOverflow answers and tutorials

Framework Evolution: Express has inspired modern alternatives:

  • Fastify: Faster (2-3x), schema-based validation
  • Koa: From Express creators, modern async/await
  • Hapi: Enterprise-focused, configuration-driven
  • NestJS: Built on top of Express, Angular-style architecture

Despite new frameworks, Express remains the default choice for Node.js APIs due to its simplicity, stability, and ecosystem maturity.

1. Installation

npm install express

# TypeScript
npm install express
npm install -D @types/express typescript ts-node

2. Basic Server

const express = require('express');
const app = express();

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

// Routes
app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.get('/api/users', (req, res) => {
  res.json({ users: ['Alice', 'Bob'] });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

3. Routing

Basic Routes

// GET request
app.get('/users', (req, res) => {
  res.json({ users: [] });
});

// POST request
app.post('/users', (req, res) => {
  const user = req.body;
  res.status(201).json({ user });
});

// PUT request
app.put('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ message: `Updated user ${id}` });
});

// DELETE request
app.delete('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ message: `Deleted user ${id}` });
});

// Multiple methods
app.route('/users/:id')
  .get((req, res) => { /* ... */ })
  .put((req, res) => { /* ... */ })
  .delete((req, res) => { /* ... */ });

Route Parameters

// URL params
app.get('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ userId: id });
});

// Multiple params
app.get('/posts/:postId/comments/:commentId', (req, res) => {
  const { postId, commentId } = req.params;
  res.json({ postId, commentId });
});

// Optional params
app.get('/users/:id?', (req, res) => {
  const { id } = req.params;
  if (id) {
    res.json({ user: { id } });
  } else {
    res.json({ users: [] });
  }
});

Query Parameters

// /users?page=1&limit=10
app.get('/users', (req, res) => {
  const { page = 1, limit = 10 } = req.query;
  res.json({ page, limit });
});

Router

// routes/users.js
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.json({ users: [] });
});

router.post('/', (req, res) => {
  res.status(201).json({ user: req.body });
});

router.get('/:id', (req, res) => {
  res.json({ user: { id: req.params.id } });
});

module.exports = router;

// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);

4. Middleware

Built-in Middleware

// Parse JSON bodies
app.use(express.json());

// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

// Serve static files
app.use(express.static('public'));

// Serve static files with prefix
app.use('/static', express.static('public'));

Custom Middleware

// Logger middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Pass to next middleware
});

// Timing middleware
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} - ${duration}ms`);
  });
  next();
});

// Authentication middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  // Verify token
  try {
    req.user = verifyToken(token);
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Use authentication
app.get('/api/profile', authenticate, (req, res) => {
  res.json({ user: req.user });
});

Third-Party Middleware

npm install cors helmet morgan compression cookie-parser
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
const cookieParser = require('cookie-parser');

// Security headers
app.use(helmet());

// CORS
app.use(cors({
  origin: 'https://example.com',
  credentials: true,
}));

// Logging
app.use(morgan('combined'));

// Compression
app.use(compression());

// Cookie parser
app.use(cookieParser());

// Body parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

5. Error Handling

// 404 handler (must be after all routes)
app.use((req, res) => {
  res.status(404).json({ error: 'Not found' });
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  res.status(err.status || 500).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
    },
  });
});

// Async error handling
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    throw new Error('User not found');
  }
  res.json({ user });
}));

6. Request and Response

Request Object

app.get('/example', (req, res) => {
  // URL params
  const { id } = req.params;
  
  // Query params
  const { page, limit } = req.query;
  
  // Body
  const data = req.body;
  
  // Headers
  const contentType = req.get('Content-Type');
  const auth = req.headers.authorization;
  
  // Cookies
  const sessionId = req.cookies.sessionId;
  
  // IP
  const ip = req.ip;
  
  // Path
  const path = req.path;
  
  // Method
  const method = req.method;
});

Response Methods

// JSON response
res.json({ message: 'Success' });

// Text response
res.send('Hello World');

// Status code
res.status(201).json({ created: true });

// Redirect
res.redirect('/login');
res.redirect(301, '/new-url');

// Download file
res.download('/files/document.pdf');

// Send file
res.sendFile('/path/to/file.html');

// Set headers
res.set('Content-Type', 'application/json');
res.set({
  'Content-Type': 'application/json',
  'X-Custom-Header': 'value',
});

// Cookies
res.cookie('sessionId', '123', { httpOnly: true, maxAge: 3600000 });
res.clearCookie('sessionId');

7. Authentication Example

npm install bcrypt jsonwebtoken
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

// Register
app.post('/api/register', async (req, res) => {
  try {
    const { email, password } = req.body;
    
    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);
    
    // Save user (example)
    const user = await db.users.create({
      email,
      password: hashedPassword,
    });
    
    res.status(201).json({ message: 'User created' });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// Login
app.post('/api/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    
    // Find user
    const user = await db.users.findOne({ email });
    if (!user) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    
    // Verify password
    const valid = await bcrypt.compare(password, user.password);
    if (!valid) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    
    // Generate token
    const token = jwt.sign(
      { userId: user.id, email: user.email },
      JWT_SECRET,
      { expiresIn: '7d' }
    );
    
    res.json({ token });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Authentication middleware
const authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;
  
  if (!authHeader) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  const token = authHeader.split(' ')[1]; // Bearer <token>
  
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Protected route
app.get('/api/profile', authenticate, async (req, res) => {
  const user = await db.users.findById(req.user.userId);
  res.json({ user });
});

8. File Upload

npm install multer
const multer = require('multer');
const path = require('path');

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
  fileFilter: (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif/;
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
    const mimetype = allowedTypes.test(file.mimetype);
    
    if (extname && mimetype) {
      cb(null, true);
    } else {
      cb(new Error('Only images allowed'));
    }
  },
});

// Single file
app.post('/api/upload', upload.single('image'), (req, res) => {
  res.json({ file: req.file });
});

// Multiple files
app.post('/api/upload-multiple', upload.array('images', 10), (req, res) => {
  res.json({ files: req.files });
});

9. TypeScript Integration

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

const app = express();

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

// Extend Request type
declare global {
  namespace Express {
    interface Request {
      user?: User;
    }
  }
}

app.get('/users/:id', async (req: Request, res: Response) => {
  const { id } = req.params;
  const user = await findUser(id);
  res.json({ user });
});

// Typed middleware
const authenticate = (req: Request, res: Response, next: NextFunction) => {
  // Authenticate and set req.user
  next();
};

app.get('/profile', authenticate, (req: Request, res: Response) => {
  res.json({ user: req.user });
});

app.listen(3000);

10. Validation

npm install express-validator
const { body, validationResult } = require('express-validator');

app.post(
  '/api/users',
  [
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 }),
    body('age').isInt({ min: 0, max: 120 }),
  ],
  (req, res) => {
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    
    // Process valid data
    res.json({ message: 'User created' });
  }
);

11. Best Practices

1. Environment Variables

require('dotenv').config();

const PORT = process.env.PORT || 3000;
const DB_URL = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;

2. Structured Folder Layout

src/
├── controllers/
│   └── userController.js
├── routes/
│   └── users.js
├── middleware/
│   ├── auth.js
│   └── errorHandler.js
├── models/
│   └── User.js
├── config/
│   └── database.js
└── app.js

3. Error Handling

class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    Error.captureStackTrace(this, this.constructor);
  }
}

const errorHandler = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';

  res.status(err.statusCode).json({
    status: err.status,
    message: err.message,
  });
};

app.use(errorHandler);

4. Rate Limiting

npm install express-rate-limit
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // 100 requests per window
  message: 'Too many requests',
});

app.use('/api/', limiter);

12. Production Setup

const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const morgan = require('morgan');
const cors = require('cors');

const app = express();

// Security
app.use(helmet());

// CORS
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(','),
  credentials: true,
}));

// Compression
app.use(compression());

// Logging
app.use(morgan('combined'));

// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// Trust proxy (for Nginx, etc.)
app.set('trust proxy', 1);

// Routes
app.use('/api', apiRouter);

// Error handling
app.use(errorHandler);

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

Summary

Express.js is the foundation for Node.js backends:

  • Minimal and unopinionated
  • Flexible routing and middleware
  • Robust ecosystem
  • Battle-tested in production
  • TypeScript support

Key Takeaways:

  1. Use middleware for cross-cutting concerns
  2. Organize routes with Router
  3. Handle errors centrally
  4. Validate input data
  5. Secure with Helmet and CORS

Next Steps:

Resources:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Complete Express.js guide for building web apps and REST APIs. Learn routing, middleware, authentication, error handling…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Express, Node.js, Backend, REST API, Web Framework, JavaScript.