The Complete Guide to API Security Best Practices in 2026
APIs are the front door to your data. A single vulnerability can expose millions of records. Here's how to lock it down.
Authentication: JWT vs OAuth2 vs API Keys
JWT — Good for Stateless Auth
// Sign
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h', issuer: 'your-api' }
);
// Verify middleware
function verifyToken(req, res, next) {
const auth = req.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token' });
}
try {
req.user = jwt.verify(auth.slice(7), process.env.JWT_SECRET);
next();
} catch (e) {
return res.status(401).json({ error: 'Invalid token' });
}
}
Store secrets securely: Use environment variables, never in code.
OAuth2 for Third-Party Access
// Authorization Code flow with PKCE
const crypto = require('crypto');
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}
const { verifier, challenge } = generatePKCE();
// Redirect user to OAuth provider with challenge
Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => req.ip,
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retryAfter: Math.ceil(15 * 60 * (req.rateLimit.remaining / 100))
});
}
});
app.use('/api/', limiter);
Input Validation — Never Trust User Input
const { z } = require('zod');
const userSchema = z.object({
email: z.string().email().max(255),
age: z.number().int().min(13).max(120),
role: z.enum(['user', 'admin']).default('user')
});
app.post('/users', async (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.flatten()
});
}
// Proceed with result.data
});
CORS: Lock It Down
const cors = require('cors');
app.use(cors({
origin: ['https://yourdomain.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
Security Headers
const helmet = require('helmet');
app.use(helmet());
// Adds: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, etc.
Audit Logging
function auditLog(action, userId, resource, details) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
action, userId, resource, details,
ip: req.ip
}));
}
// Log every mutation
app.post('/api/users', async (req, res) => {
auditLog('CREATE_USER', req.user.id, 'users', req.body);
// ...
});
Conclusion
API security is layers: auth, rate limiting, input validation, CORS, headers, and audit logs. Implement all of them. A breach costs far more than the development time.
Protect your API with enterprise-grade security — built-in auth, rate limiting, and monitoring on a single platform.
Top comments (0)