Introduction
Building robust Node.js applications with Express often requires more than just writing code — it demands a clear architectural vision and quick problem-solving. Whether you're designing REST APIs, implementing authentication, or debugging middleware, having the right prompt can save hours of trial and error. This collection of 12 ready-to-use prompts helps you generate production-quality code, explain complex concepts, and test your endpoints efficiently. Each prompt includes a specific use case, an example of expected output, and a code snippet illustrating the result.
1. Generate a REST API with CRUD Operations
Task: Create a complete RESTful API for a resource (e.g., users) with endpoints for Create, Read, Update, Delete.
Prompt:
"Generate an Express.js REST API for managing 'products' with the following endpoints: GET /products (list all), GET /products/:id (single), POST /products (create with name, price, category), PUT /products/:id (update), DELETE /products/:id (delete). Use an in-memory array as storage. Include proper HTTP status codes (201 for create, 404 for not found, 200 for success). Add error handling middleware."
Example Output:
const express = require('express');
const app = express();
app.use(express.json());
let products = [
{ id: 1, name: 'Laptop', price: 999, category: 'Electronics' },
{ id: 2, name: 'Shirt', price: 29, category: 'Clothing' }
];
let nextId = 3;
app.get('/products', (req, res) => {
res.json(products);
});
app.get('/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) return res.status(404).json({ error: 'Product not found' });
res.json(product);
});
app.post('/products', (req, res) => {
const { name, price, category } = req.body;
const newProduct = { id: nextId++, name, price, category };
products.push(newProduct);
res.status(201).json(newProduct);
});
app.put('/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) return res.status(404).json({ error: 'Product not found' });
Object.assign(product, req.body);
res.json(product);
});
app.delete('/products/:id', (req, res) => {
const index = products.findIndex(p => p.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'Product not found' });
products.splice(index, 1);
res.status(200).json({ message: 'Deleted' });
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
2. Build Authentication Middleware with JWT
Task: Implement middleware that validates JWT tokens for protected routes.
Prompt:
"Create Express middleware that checks for a valid JWT in the Authorization header (Bearer token). Use the 'jsonwebtoken' library. If token is missing or invalid, return 401 Unauthorized. If valid, attach the decoded user payload to req.user and call next(). Also provide a login route that returns a token."
Example Output:
const jwt = require('jsonwebtoken');
const SECRET = 'your_secret_key_here';
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Login route
app.post('/login', (req, res) => {
const { username, password } = req.body;
// In a real app, verify against database
if (username === 'admin' && password === 'secret') {
const token = jwt.sign({ username, role: 'admin' }, SECRET, { expiresIn: '1h' });
return res.json({ token });
}
res.status(401).json({ error: 'Invalid credentials' });
});
3. Create a Rate Limiting Middleware
Task: Prevent abuse by limiting requests per IP address.
Prompt:
"Write Express middleware that limits requests to 100 per hour per IP address. Use an in-memory store (object). If limit exceeded, return 429 Too Many Requests with a Retry-After header. Include the remaining requests count in the response header X-RateLimit-Remaining."
Example Output:
const rateLimit = {};
const MAX_REQUESTS = 100;
const WINDOW_MS = 60 * 60 * 1000; // 1 hour
function rateLimiter(req, res, next) {
const ip = req.ip;
const now = Date.now();
if (!rateLimit[ip] || now - rateLimit[ip].start > WINDOW_MS) {
rateLimit[ip] = { count: 1, start: now };
} else {
rateLimit[ip].count++;
}
const remaining = Math.max(0, MAX_REQUESTS - rateLimit[ip].count);
res.set('X-RateLimit-Remaining', remaining);
if (rateLimit[ip].count > MAX_REQUESTS) {
return res.status(429).json({ error: 'Too many requests' });
}
next();
}
app.use(rateLimiter);
4. Implement Error Handling Middleware
Task: Centralize error responses with structured JSON.
Prompt:
"Create a custom error-handling middleware for Express that catches all errors. Define a custom AppError class with statusCode and message. In the middleware, check if error is an instance of AppError; if not, default to 500. Return JSON with error message and status."
Example Output:
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
}
}
function errorHandler(err, req, res, next) {
const statusCode = err.statusCode || 500;
const message = err.message || 'Internal server error';
console.error(`[Error] ${statusCode}: ${message}`);
res.status(statusCode).json({ error: message, status: statusCode });
}
// Usage in route
app.get('/data', (req, res, next) => {
try {
throw new AppError('Data not found', 404);
} catch (err) {
next(err);
}
});
app.use(errorHandler);
5. Design a WebSocket Chat Server with Socket.io
Task: Build a real-time chat feature using Socket.io with Express.
Prompt:
"Integrate Socket.io into an Express server. Create events: 'connection', 'join_room' (client sends room name), 'send_message' (client sends {room, message}), and 'receive_message'. Broadcast messages to all clients in the same room. Include a simple HTML client for testing."
Example Output:
const http = require('http');
const express = require('express');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
io.on('connection', (socket) => {
console.log('New client connected:', socket.id);
socket.on('join_room', (room) => {
socket.join(room);
});
socket.on('send_message', (data) => {
io.to(data.room).emit('receive_message', {
user: data.user,
message: data.message,
time: new Date().toISOString()
});
});
socket.on('disconnect', () => console.log('Client disconnected'));
});
server.listen(3000, () => console.log('Server on port 3000'));
6. Validate Request Data with Joi
Task: Ensure incoming request bodies meet expected schema.
Prompt:
"Create a validation middleware using Joi. Define a schema for a user registration endpoint: username (alphanumeric, 3-30 chars), email (valid email), password (min 8 chars, at least one number). If validation fails, return 400 with detailed error messages. Apply middleware to the POST /register route."
Example Output:
const Joi = require('joi');
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
password: Joi.string().min(8).pattern(/\d/).required()
});
function validateUser(req, res, next) {
const { error } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
next();
}
app.post('/register', validateUser, (req, res) => {
res.json({ message: 'User registered successfully' });
});
7. Set Up CORS for Cross-Origin Requests
Task: Configure CORS to allow requests from specific domains.
Prompt:
"Use the 'cors' package to allow only https://myfrontend.com and https://admin.myfrontend.com. Allow methods GET, POST, PUT, DELETE. Enable credentials (cookies). For preflight OPTIONS requests, return 204."
Example Output:
const cors = require('cors');
const corsOptions = {
origin: ['https://myfrontend.com', 'https://admin.myfrontend.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
optionsSuccessStatus: 204
};
app.use(cors(corsOptions));
8. Create a Logging Middleware
Task: Log all HTTP requests with timestamp, method, URL, and response time.
Prompt:
"Write a middleware that logs each request in the format: '[2026-07-17 14:30:00] GET /api/products 200 12ms'. Use res.on('finish') to capture the response status and calculate duration. Output to console."
Example Output:
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
console.log(`[${timestamp}] ${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`);
});
next();
}
app.use(requestLogger);
9. Build a File Upload Endpoint with Multer
Task: Accept single file upload with size and type restrictions.
Prompt:
"Configure Multer to store uploaded files in 'uploads/' directory with a random filename. Limit file size to 5MB. Allow only image/jpeg and image/png MIME types. Create a POST /upload route that returns the file URL."
Example Output:
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1E9) + path.extname(file.originalname);
cb(null, uniqueName);
}
});
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(new Error('Only JPEG and PNG allowed'), false);
}
}
});
app.post('/upload', upload.single('file'), (req, res) => {
res.json({ url: `/uploads/${req.file.filename}` });
});
10. Implement Role-Based Access Control (RBAC)
Task: Restrict routes based on user roles (admin, user).
Prompt:
"Create a middleware function 'authorize(roles)' that takes an array of allowed roles. It checks req.user.role (set by auth middleware). If role not allowed, return 403 Forbidden. Use it to protect DELETE /products (admin only) and GET /products (any authenticated user)."
Example Output:
function authorize(...allowedRoles) {
return (req, res, next) => {
if (!req.user || !allowedRoles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
// Usage
app.delete('/products/:id', authMiddleware, authorize('admin'), (req, res) => {
// delete logic
});
11. Set Up Environment Configuration
Task: Load environment variables securely using dotenv with validation.
Prompt:
"Create a config.js file that loads .env variables with dotenv. Define required variables: PORT, DB_URL, JWT_SECRET. If any missing, throw an error with a clear message. Export a frozen config object."
Example Output:
require('dotenv').config();
const required = ['PORT', 'DB_URL', 'JWT_SECRET'];
required.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
const config = Object.freeze({
port: parseInt(process.env.PORT, 10),
dbUrl: process.env.DB_URL,
jwtSecret: process.env.JWT_SECRET
});
module.exports = config;
12. Create a Health Check Endpoint
Task: Provide a simple endpoint for monitoring service health.
Prompt:
"Create GET /health that returns JSON: { status: 'ok', uptime: process.uptime(), timestamp: new Date().toISOString() }. Set status 200. Also add a readiness check that pings the database (simulate with a promise) and returns 503 if DB is down."
Example Output:
app.get('/health', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime(), timestamp: new Date().toISOString() });
});
app.get('/ready', async (req, res) => {
try {
// Simulate DB ping
await new Promise((resolve) => setTimeout(resolve, 100));
res.json({ status: 'ready' });
} catch (err) {
res.status(503).json({ status: 'not ready' });
}
});
13. Add Pagination to List Endpoints
Task: Implement pagination for a GET endpoint returning many items.
Prompt:
"Modify GET /products to accept query params page (default 1) and limit (default 10, max 100). Return total count, total pages, current page, and data array. Use array slice for in-memory data."
Example Output:
app.get('/products', (req, res) => {
const page = Math.max(1, parseInt(req.query.page) || 1);
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 10));
const start = (page - 1) * limit;
const total = products.length;
const totalPages = Math.ceil(total / limit);
const data = products.slice(start, start + limit);
res.json({ total, totalPages, currentPage: page, data });
});
14. Integrate Swagger for API Documentation
Task: Use swagger-jsdoc and swagger-ui-express to auto-document API.
Prompt:
"Set up Swagger for an Express app. Define basic info (title 'My API', version '1.0.0'). Use swagger-jsdoc to read JSDoc comments. Serve documentation at /api-docs. Add a sample route comment for GET /products."
Example Output:
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: { title: 'My API', version: '1.0.0' }
},
apis: ['./routes/*.js']
};
const swaggerSpec = swaggerJsDoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
15. Handle Async Errors Gracefully
Task: Wrap async route handlers to catch errors without try-catch blocks.
Prompt:
"Create a wrapper function 'asyncHandler' that takes an async route handler and returns a function that catches any rejected promise and passes it to next(). Use it for all async routes."
Example Output:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/async-data', asyncHandler(async (req, res) => {
const data = await fetchSomeData();
res.json(data);
}));
16. Create a Simple WebSocket with Native ws Library
Task: Implement a basic WebSocket server without Socket.io.
Prompt:
"Use the 'ws' library to create a WebSocket server alongside Express. On connection, log client. On message, echo back the same message prefixed with 'Echo: '. Handle close event. Use the 'upgrade' event on HTTP server."
Example Output:
const WebSocket = require('ws');
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
console.log('WebSocket connected');
ws.on('message', (message) => {
ws.send(`Echo: ${message}`);
});
ws.on('close', () => console.log('WebSocket disconnected'));
});
Conclusion
These 16 prompts cover the most common tasks in Node.js and Express development — from building REST APIs and authentication middleware to handling WebSockets and file uploads. Each prompt is production-ready and can be adapted to your specific project. Bookmark this list for your next Express project, and remember: the best way to master these patterns is to experiment with them in real applications. Start with a simple API and gradually add middleware, validation, and real-time features.
Last updated: July 2026
Comments