← Back to Examples

REST API Design

REST = Representational State Transfer. A convention for designing web APIs using HTTP methods and URLs.

REST Conventions

Method URL Action Status
GET /api/books List all books 200
GET /api/books/:id Get one book 200 / 404
POST /api/books Create a book 201
PUT /api/books/:id Update a book 200 / 404
DELETE /api/books/:id Delete a book 204 / 404

Complete REST API: Book Store

Save this as bookstore-api.js and run with node bookstore-api.js
// bookstore-api.js const express = require('express'); const app = express(); const PORT = 3000; // Middleware app.use(express.json()); // Request logger app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }); // ============================================ // DATA STORE (In-memory - resets on restart) // ============================================ let books = [ { id: 1, title: 'JavaScript: The Good Parts', author: 'Douglas Crockford', year: 2008, genre: 'Programming' }, { id: 2, title: 'Eloquent JavaScript', author: 'Marijn Haverbeke', year: 2018, genre: 'Programming' }, { id: 3, title: 'Node.js Design Patterns', author: 'Mario Casciaro', year: 2020, genre: 'Programming' } ]; let nextId = 4; // ============================================ // ROUTES // ============================================ // GET /api/books - List all books app.get('/api/books', (req, res) => { // Support filtering by genre const { genre, author } = req.query; let result = books; if (genre) { result = result.filter(b => b.genre.toLowerCase() === genre.toLowerCase() ); } if (author) { result = result.filter(b => b.author.toLowerCase().includes(author.toLowerCase()) ); } res.json({ count: result.length, data: result }); }); // GET /api/books/:id - Get one book app.get('/api/books/:id', (req, res) => { const id = parseInt(req.params.id); const book = books.find(b => b.id === id); if (!book) { return res.status(404).json({ error: 'Book not found', id: id }); } res.json(book); }); // POST /api/books - Create a book app.post('/api/books', (req, res) => { const { title, author, year, genre } = req.body; // Validation if (!title || !author) { return res.status(400).json({ error: 'Title and author are required' }); } const book = { id: nextId++, title, author, year: year || new Date().getFullYear(), genre: genre || 'General' }; books.push(book); res.status(201).json(book); }); // PUT /api/books/:id - Update a book app.put('/api/books/:id', (req, res) => { const id = parseInt(req.params.id); const book = books.find(b => b.id === id); if (!book) { return res.status(404).json({ error: 'Book not found' }); } // Update fields const { title, author, year, genre } = req.body; if (title) book.title = title; if (author) book.author = author; if (year) book.year = year; if (genre) book.genre = genre; res.json(book); }); // DELETE /api/books/:id - Delete a book app.delete('/api/books/:id', (req, res) => { const id = parseInt(req.params.id); const index = books.findIndex(b => b.id === id); if (index === -1) { return res.status(404).json({ error: 'Book not found' }); } books.splice(index, 1); res.status(204).send(); }); // ============================================ // ERROR HANDLING // ============================================ // 404 handler (no route matched) app.use((req, res) => { res.status(404).json({ error: 'Route not found', path: req.url, method: req.method }); }); // Error handler app.use((err, req, res, next) => { console.error('Server error:', err.message); res.status(500).json({ error: 'Internal server error' }); }); // ============================================ // START SERVER // ============================================ app.listen(PORT, () => { console.log(`Book Store API running at http://localhost:${PORT}`); console.log(''); console.log('Endpoints:'); console.log(' GET /api/books - List all books'); console.log(' GET /api/books/:id - Get one book'); console.log(' POST /api/books - Create a book'); console.log(' PUT /api/books/:id - Update a book'); console.log(' DELETE /api/books/:id - Delete a book'); });

Testing the API

# List all books $ curl http://localhost:3000/api/books # Get one book $ curl http://localhost:3000/api/books/1 # Create a book $ curl -X POST http://localhost:3000/api/books \ -H "Content-Type: application/json" \ -d '{"title":"Learning Node.js","author":"Shelley Powers","year":2024}' # Update a book $ curl -X PUT http://localhost:3000/api/books/1 \ -H "Content-Type: application/json" \ -d '{"year":2024}' # Delete a book $ curl -X DELETE http://localhost:3000/api/books/1 # Filter books $ curl "http://localhost:3000/api/books?genre=Programming"

HTTP Status Codes Reference

CodeMeaningUse Case
200OKSuccessful GET, PUT
201CreatedSuccessful POST
204No ContentSuccessful DELETE
400Bad RequestInvalid input data
401UnauthorizedMissing/invalid auth
403ForbiddenNo permission
404Not FoundResource doesn't exist
500Server ErrorUnexpected error

← Back to Examples