Node.js RESTFul API
Node.js - Building RESTful APIs
Section titled “Node.js - Building RESTful APIs”What is REST?
Section titled “What is REST?”REST stands for REpresentational State Transfer. It’s an architectural style, not a specific technology or protocol, for designing networked applications. RESTful systems (systems adhering to REST principles) typically use the HTTP protocol for communication.
Key principles of REST include:
- Client-Server Architecture: Separation of concerns between the client (requesting data) and the server (managing data).
- Statelessness: Each request from a client to the server must contain all the information needed to understand and process the request. The server does not store any client context between requests.
- Cacheability: Responses must define themselves as cacheable or non-cacheable to improve performance.
- Layered System: A client cannot ordinarily tell whether it is connected directly to the end server or to an intermediary along the way.
- Uniform Interface: A common interface simplifies and decouples the architecture. This often involves:
- Resource Identification: Resources (e.g., users, products) are identified using URIs (Uniform Resource Identifiers), like
/users/123. - Resource Manipulation Through Representations: Clients interact with resources via their representations (commonly JSON or XML).
- Self-descriptive Messages: Each message includes enough information to describe how to process it (e.g., using HTTP methods and headers like
Content-Type). - Hypermedia as the Engine of Application State (HATEOAS): Responses can include links to related actions or resources, allowing clients to navigate the API dynamically (less commonly implemented in practice).
Standard HTTP Methods in REST
Section titled “Standard HTTP Methods in REST”RESTful APIs leverage standard HTTP methods (verbs) to perform operations on resources:
| HTTP Method | Action | Example URI | Description |
|---|---|---|---|
| GET | Read | /users | Retrieve a list of users. |
| GET | Read | /users/123 | Retrieve a specific user with ID 123. |
| POST | Create | /users | Create a new user (data sent in request body). |
| PUT | Update/Replace | /users/123 | Update/replace the user with ID 123 (entire resource data sent in request body). |
| PATCH | Partial Update | /users/123 | Partially update the user with ID 123 (only changed fields sent in request body). |
| DELETE | Delete | /users/123 | Delete the user with ID 123. |
RESTful Web Services
Section titled “RESTful Web Services”Web services designed according to REST principles are called RESTful web services or RESTful APIs. They provide a standardized way for different applications (e.g., a web frontend, a mobile app, another backend service) to communicate and exchange data, typically using HTTP and JSON.
Creating a Simple RESTful API with Express
Section titled “Creating a Simple RESTful API with Express”We’ll use the popular Express.js framework to build a simple API for managing a list of users. For simplicity, we’ll store the user data in a JSON file (users.json) acting as a basic database.
1. Setup Project
Section titled “1. Setup Project”Create a new directory for your project, navigate into it, and initialize npm:
mkdir simple-rest-apicd simple-rest-apinpm init -yInstall Express:
npm install express2. Create Data File (users.json)
Section titled “2. Create Data File (users.json)”Create a file named users.json with some initial data:
{ "1": { "name": "Alice", "profession": "Engineer", "id": 1 }, "2": { "name": "Bob", "profession": "Designer", "id": 2 }, "3": { "name": "Charlie", "profession": "Manager", "id": 3 }}3. Create Server File (server.js)
Section titled “3. Create Server File (server.js)”Create a file named server.js and set up the basic Express server:
const express = require('express');const fs = require('fs').promises; // Use promises API for async operationsconst path = require('path');
const app = express();const port = 3000;const usersFilePath = path.join(__dirname, 'users.json');
// Middleware to parse JSON request bodiesapp.use(express.json());
// --- API Endpoints ---
// Helper function to read users dataasync function getUsersData() { try { const data = await fs.readFile(usersFilePath, 'utf8'); return JSON.parse(data); } catch (error) { // If file doesn't exist or other read error, return empty object if (error.code === 'ENOENT') { return {}; } console.error("Error reading users file:", error); throw error; // Re-throw other errors }}
// Helper function to write users dataasync function writeUsersData(data) { try { await fs.writeFile(usersFilePath, JSON.stringify(data, null, 2), 'utf8'); } catch (error) { console.error("Error writing users file:", error); throw error; }}
// GET /api/users - List all usersapp.get('/api/users', async (req, res) => { try { const users = await getUsersData(); // Return array of users instead of object keyed by ID res.json(Object.values(users)); } catch (error) { res.status(500).send('Error retrieving user data'); }});
// GET /api/users/:id - Get a single user by IDapp.get('/api/users/:id', async (req, res) => { const userId = req.params.id; try { const users = await getUsersData(); const user = users[userId]; if (user) { res.json(user); } else { res.status(404).send('User not found'); } } catch (error) { res.status(500).send('Error retrieving user data'); }});
// POST /api/users - Add a new userapp.post('/api/users', async (req, res) => { const newUserInfo = req.body; if (!newUserInfo || !newUserInfo.name || !newUserInfo.profession) { return res.status(400).send('Missing user name or profession in request body'); }
try { const users = await getUsersData(); const existingIds = Object.keys(users).map(id => parseInt(id, 10)); const newId = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1;
const newUser = { ...newUserInfo, // Spread operator to copy properties id: newId };
users[newId] = newUser; await writeUsersData(users);
res.status(201).json(newUser); // 201 Created status } catch (error) { res.status(500).send('Error adding user'); }});
// DELETE /api/users/:id - Delete a userapp.delete('/api/users/:id', async (req, res) => { const userId = req.params.id; try { const users = await getUsersData(); if (users[userId]) { delete users[userId]; await writeUsersData(users); res.status(204).send(); // 204 No Content status (success, no body) } else { res.status(404).send('User not found'); } } catch (error) { res.status(500).send('Error deleting user'); }});
// PUT /api/users/:id - Update/Replace a user (Example)app.put('/api/users/:id', async (req, res) => { const userId = req.params.id; const updatedUserInfo = req.body;
if (!updatedUserInfo || !updatedUserInfo.name || !updatedUserInfo.profession) { return res.status(400).send('Missing user name or profession in request body for update'); }
try { let users = await getUsersData(); if (!users[userId]) { return res.status(404).send('User not found'); }
// Completely replace the user info users[userId] = { ...updatedUserInfo, id: parseInt(userId, 10) // Ensure ID remains correct type };
await writeUsersData(users); res.json(users[userId]);
} catch (error) { res.status(500).send('Error updating user'); }});
// Start the serverapp.listen(port, () => { console.log(`Server listening at http://localhost:${port}`);});4. Run the Server
Section titled “4. Run the Server”Open your terminal in the project directory and run:
$ node server.jsYou should see: Server listening at http://localhost:3000
5. Testing the API
Section titled “5. Testing the API”You can use tools like curl in your terminal, Postman, Insomnia, or even your web browser (for GET requests) to interact with your API:
- List Users (GET): Open
http://localhost:3000/api/usersin your browser or usecurl http://localhost:3000/api/users - Get User 2 (GET): Open
http://localhost:3000/api/users/2or usecurl http://localhost:3000/api/users/2 - Add User (POST): Use
curl -X POST -H "Content-Type: application/json" -d '{"name":"David", "profession":"Tester"}' http://localhost:3000/api/users - Delete User 1 (DELETE): Use
curl -X DELETE http://localhost:3000/api/users/1 - Update User 3 (PUT): Use
curl -X PUT -H "Content-Type: application/json" -d '{"name":"Charlie Jr.", "profession":"Senior Manager"}' http://localhost:3000/api/users/3
After making changes with POST, PUT, or DELETE, you can use GET requests again to see the updated list in users.json.
This example demonstrates the basics of creating a RESTful API in Node.js using Express. Real-world applications would typically involve more robust error handling, input validation, authentication, authorization, and often a proper database instead of a JSON file.