Node.js · Lesson 7 of 7

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

  1. On login, check the password against a bcrypt hash (never store plain passwords).
  2. Sign a JWT with a secret from process.env and send it to the client.
  3. 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?

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.