Securing a Node.js REST API: A Practical Checklist
Most API breaches come from a few well-known mistakes. Work through this checklist for every Node.js service you deploy.
1. Validate every input
import { z } from 'zod';
const CreateOrder = z.object({
productId: z.uuid(),
quantity: z.number().int().min(1).max(100),
});
app.post('/orders', async (req, res) => {
const input = CreateOrder.parse(req.body); // reject anything unexpected
// ...
});
2. Use parameterised queries
// Never build SQL with string concatenation
await pool.query('SELECT * FROM users WHERE email = $1', [email]);
3. Authorise every request, per resource
const order = await db.orders.find(req.params.id);
if (!order || order.userId !== req.user.id) throw new HttpError(404, 'Not found');
Checking "is logged in" isn't enough. Check that this user may access this record.
4. Security headers and CORS
import helmet from 'helmet';
import cors from 'cors';
app.use(helmet());
app.use(cors({ origin: ['https://app.example.com'], credentials: true }));
5. Rate limiting
import rateLimit from 'express-rate-limit';
app.use('/auth', rateLimit({ windowMs: 15 * 60 * 1000, limit: 20 })); // slow down brute force
6. Limit request size
app.use(express.json({ limit: '100kb' }));
7. Passwords and tokens
- Hash passwords with bcrypt, scrypt or Argon2id; never store them in plain text.
- Keep JWT secrets long and random; set short expiry times.
- Use
crypto.timingSafeEqualwhen comparing secrets.
8. Dependencies and secrets
- Run
npm auditin CI and keep dependencies updated. - Commit the lockfile and use
npm ciin builds. - Keep secrets in environment variables or a secret manager, never in the repo.
9. Fail safely
Return generic messages for 500 errors and log details server-side. Stack traces in API responses help attackers.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.