Intro to Express
Express.js is the most popular Node.js web framework. It simplifies routing, middleware, and building APIs.
Setting Up
mkdir my-express-app
cd my-express-app
npm init -y
npm install express
Your First Express Server
// app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
Run it:
node app.js
Compare this to the raw HTTP server - so much simpler!
Routing Made Easy
const express = require('express');
const app = express();
// Different HTTP methods
app.get('/', (req, res) => {
res.send('GET request to homepage');
});
app.post('/submit', (req, res) => {
res.send('POST request received');
});
app.put('/update', (req, res) => {
res.send('PUT request received');
});
app.delete('/remove', (req, res) => {
res.send('DELETE request received');
});
// Multiple routes
app.get('/about', (req, res) => {
res.send('About page');
});
app.get('/contact', (req, res) => {
res.send('Contact page');
});
app.listen(3000);
Route Parameters
const express = require('express');
const app = express();
// :id is a parameter
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`User ID: ${userId}`);
});
// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.send(`User ${userId}, Post ${postId}`);
});
app.listen(3000);
Test it:
curl http://localhost:3000/users/42
curl http://localhost:3000/users/1/posts/5
Output:
User ID: 42
User 1, Post 5
Query Strings
// Query strings: /search?q=nodejs&limit=10
app.get('/search', (req, res) => {
const query = req.query.q;
const limit = req.query.limit || 10;
res.send(`Searching for: ${query} (limit: ${limit})`);
});
Test it:
curl "http://localhost:3000/search?q=nodejs&limit=5"
Output:
Searching for: nodejs (limit: 5)
Sending JSON
const express = require('express');
const app = express();
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
// res.json() automatically sets Content-Type and stringifies
app.get('/api/users', (req, res) => {
res.json(users);
});
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'User not found' });
}
});
app.listen(3000);
Middleware
Middleware functions run before your route handlers:
const express = require('express');
const app = express();
// Middleware that runs on EVERY request
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next(); // Pass control to next middleware/route
});
// Built-in middleware for parsing JSON bodies
app.use(express.json());
// Built-in middleware for parsing URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Your routes
app.get('/', (req, res) => {
res.send('Home');
});
app.post('/api/data', (req, res) => {
console.log('Body:', req.body); // Now accessible thanks to middleware
res.json({ received: req.body });
});
app.listen(3000);
Test it:
curl -X POST http://localhost:3000/api/data \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
Server log:
2024-01-15T10:30:00.000Z - POST /api/data
Body: { name: 'Alice' }
Complete REST API Example
// api.js
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
// In-memory database
let todos = [
{ id: 1, text: 'Learn Express', done: false },
{ id: 2, text: 'Build an API', done: false }
];
let nextId = 3;
// GET all todos
app.get('/api/todos', (req, res) => {
res.json(todos);
});
// GET single todo
app.get('/api/todos/:id', (req, res) => {
const todo = todos.find(t => t.id === parseInt(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'Todo not found' });
}
res.json(todo);
});
// CREATE todo
app.post('/api/todos', (req, res) => {
const { text } = req.body;
if (!text) {
return res.status(400).json({ error: 'Text is required' });
}
const todo = {
id: nextId++,
text,
done: false
};
todos.push(todo);
res.status(201).json(todo);
});
// UPDATE todo
app.put('/api/todos/:id', (req, res) => {
const todo = todos.find(t => t.id === parseInt(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'Todo not found' });
}
const { text, done } = req.body;
if (text !== undefined) todo.text = text;
if (done !== undefined) todo.done = done;
res.json(todo);
});
// DELETE todo
app.delete('/api/todos/:id', (req, res) => {
const index = todos.findIndex(t => t.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'Todo not found' });
}
todos.splice(index, 1);
res.json({ message: 'Todo deleted' });
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
// Error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
app.listen(3000, () => {
console.log('API running at http://localhost:3000/');
});
Test the full CRUD:
# List all
curl http://localhost:3000/api/todos
# Create
curl -X POST http://localhost:3000/api/todos \
-H "Content-Type: application/json" \
-d '{"text":"New todo"}'
# Update
curl -X PUT http://localhost:3000/api/todos/1 \
-H "Content-Type: application/json" \
-d '{"done":true}'
# Delete
curl -X DELETE http://localhost:3000/api/todos/2
Express Router (Organizing Routes)
For larger apps, organize routes in separate files:
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
router.get('/:id', (req, res) => {
res.json({ id: req.params.id, name: 'Alice' });
});
router.post('/', (req, res) => {
res.status(201).json({ id: 2, ...req.body });
});
module.exports = router;
// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const app = express();
app.use(express.json());
// Mount the router at /api/users
app.use('/api/users', usersRouter);
app.listen(3000);
Now /api/users and /api/users/:id are handled by the router.
Static Files
Serve HTML, CSS, JS, and images:
const express = require('express');
const path = require('path');
const app = express();
// Serve everything in the 'public' folder
app.use(express.static(path.join(__dirname, 'public')));
// API routes
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from API' });
});
app.listen(3000);
Now put index.html, style.css, etc. in a public/ folder and they're served automatically.
Key Takeaways
- express() creates an app instance
- app.get/post/put/delete define route handlers
- req.params for URL parameters (
:id) - req.query for query strings (
?name=value) - req.body for POST/PUT body (needs middleware)
- res.json() sends JSON response
- res.status() sets HTTP status code
- app.use() adds middleware
- express.Router() organizes routes
What's Next?
You've learned Node.js fundamentals:
- How Node.js works (runtime, event loop)
- Modules and npm
- File system operations
- Async/await
- Building HTTP servers
- Express basics
- Add a database (MongoDB, PostgreSQL)
- Authentication (JWT, sessions)
- Validation (Joi, Zod)
- Testing (Jest)
- Deployment (Docker, cloud platforms)
Build Full-Stack Projects
Continue learning with real projects. Build APIs, connect databases, and deploy on pre-configured cloud workspaces.
Take the quiz to test your knowledge!