Databases, errors & JWT auth
Connecting to a database, central error handling, environment secrets and protecting routes with JWT.
Production APIs store data in a database. Popular choices with Node are PostgreSQL/MySQL (with Prisma, Knex or the pg driver) and MongoDB (with Mongoose). Whatever you use, always pass user input as query parameters, never by joining strings — that prevents SQL injection.
Error handling
Add one error-handling middleware with four arguments (err, req, res, next) at the end. Routes pass errors to it with next(err) (Express 5 does this automatically for async handlers), so every error returns a consistent JSON response.
Authentication with JWT
- On login, check the password against a bcrypt hash (never store plain passwords).
- Sign a JWT with a secret from
process.envand send it to the client. - The client sends
Authorization: Bearer <token>; middleware verifies it before protected routes.
Example code
Run with Node.js 20+// auth.js (npm install jsonwebtoken)
import jwt from "jsonwebtoken";
const SECRET = process.env.JWT_SECRET; // never hard-code secrets
export function requireAuth(req, res, next) {
const header = req.headers.authorization || "";
const token = header.startsWith("Bearer ") ? header.slice(7) : null;
if (!token) return res.status(401).json({ error: "Login required" });
try {
req.user = jwt.verify(token, SECRET);
next();
} catch {
res.status(401).json({ error: "Invalid or expired token" });
}
}
// app.js
app.get("/api/me/applications", requireAuth, async (req, res) => {
const { rows } = await db.query(
"SELECT id, job_title FROM applications WHERE user_id = $1",
[req.user.id]
);
res.json(rows);
});
// last middleware: central error handler
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Something went wrong" });
});
Output
$ curl localhost:3000/api/me/applications
{"error":"Login required"} (401)
$ curl localhost:3000/api/me/applications -H "Authorization: Bearer eyJhbGciOi..."
[{"id":12,"job_title":"Node.js Developer"}] (200)
Save the code in a file such as app.js and run node app.js. For Express lessons, run npm install express first. Names and data are fictional.
Key points
- Use parameterised queries to prevent SQL injection.
- Hash passwords with bcrypt; never store them in plain text.
- Keep secrets in environment variables.
- One error-handling middleware gives consistent error responses.
- Verify JWTs in middleware before protected routes.
Quick check
Which status code should a protected route return when no token is sent?
401 Unauthorized means the request lacks valid authentication. 403 Forbidden is for a logged-in user who isn't allowed.
Practice task
Write a POST /api/login route that checks an email and password against a hard-coded user with a bcrypt hash, and returns a JWT that expires in 1 hour.
Interview corner
How does JWT authentication work?
After login the server signs a token containing the user's id and roles. The client sends it on each request in the Authorization header; the server verifies the signature and expiry, so it doesn't need to store sessions. Don't put secrets in the payload — it's only encoded, not encrypted.
How do you handle errors centrally in Express?
Add error-handling middleware with four parameters (err, req, res, next) after all routes. Routes call next(err) (or throw in Express 5 async handlers), and the handler logs the error and returns a consistent JSON response with the right status.
Further reading
Official documentation, linked for deeper study. These are outside sites; this lesson's text is our own.
Original lesson written by the InfoparkDaily team · © 2026 InfoparkDaily · Free to learn — no paid course or subscription.