Theory
Work that happens in the middle
When a request reaches your Express API, several things often need to happen before your route handler runs: parse the JSON body, check the caller is allowed, log the request. Rather than cramming all that into every handler, Express uses middleware, functions that run in the middle of request processing.
Middleware is one of Express's most important ideas, and it powers parsing, static files, cross-origin access, and your own custom checks. This closing backend lesson explains what middleware is and the common pieces you will always use, including the one that trips up every full-stack beginner: CORS.
Theory
What middleware is
A middleware is a function that receives (req, res, next) and runs during a request, before (or around) your route handler. It can read or change the request and response, and then it calls next() to pass control to the next middleware or handler (or it can end the response itself).
You register middleware with app.use(...). It runs in order, forming a pipeline every request flows through. This lets you factor out shared work, do it once, in one place, for many routes, instead of repeating it. Parsing, logging, authentication, and error handling are all naturally middleware.
Practical
Built-in middleware and a custom one
const express = require('express');
const cors = require('cors');
const app = express();
app.use(express.json()); // built-in: parse JSON bodies -> req.body
app.use(express.static('public')); // built-in: serve files from /public
app.use(cors()); // allow cross-origin requests (Angular -> API)
// custom middleware: log every request, then pass control on
app.use((req, res, next) => {
console.log(req.method, req.url);
next(); // MUST call next() to continue
});This example runs in Gri-Learn on the web, where you can edit it and see the output.
Formula
CORS: the full-stack beginner's stumbling block
Here is a problem you WILL hit. FestConnect's Angular front end runs on one origin (say localhost:4200) and the Express API on another (localhost:3000). For security, browsers block a page from calling an API on a different origin unless the API explicitly allows it. This is CORS (Cross-Origin Resource Sharing).
The symptom is a console error about a blocked cross-origin request, and the fix is to enable the cors middleware on the server (app.use(cors())), which sends the headers that tell the browser the API permits the call. Almost every full-stack beginner meets this once; now you will recognise it instantly.
Theory
Environment variables keep secrets out of code
Your backend needs configuration that should not be hard-coded: the database connection string, API keys, the port. Putting these in the source code is insecure and inflexible (recall the same lesson from web.config in .NET).
The Node convention is environment variables stored in a .env file, read through process.env (often with the dotenv package). So process.env.DB_URL holds the database address, kept out of the code and out of version control. This lets the same code run with different settings on your laptop and the live server, and keeps secrets from leaking. Configuration in the environment, not in the code.
Quiz
Your Angular app on localhost:4200 calls your Express API on localhost:3000, and the browser blocks it with a cross-origin error. What is the fix on the server?
- Move all the code into one file
- Enable the CORS middleware (app.use(cors())) so the API sends headers permitting cross-origin requests
- Switch from Express to Angular
- Delete the route parameters
Show the answer
Enable the CORS middleware (app.use(cors())) so the API sends headers permitting cross-origin requests
The browser blocks requests between different origins (localhost:4200 to localhost:3000) unless the server opts in via CORS (Cross-Origin Resource Sharing). Enabling the cors middleware with app.use(cors()) makes the API send the headers that tell the browser the cross-origin call is allowed, resolving the error. Option A does not address the browser's origin check; combining files does not change origins. Option C is nonsense: Angular is the front end making the call, not a replacement for the API. Option D is unrelated: route parameters have nothing to do with cross-origin blocking. CORS is a browser security feature, and the server enabling the cors middleware is the standard fix.
Think first
Why do browsers block cross-origin requests in the first place?
CORS causes beginners grief. What security problem is it actually protecting against? Then tap.
Show the answer
It protects users from MALICIOUS websites silently making requests to OTHER sites where the user is logged in, an attack the 'same-origin policy' is designed to prevent. Imagine you are logged into your bank in one tab. Without origin restrictions, a malicious page you visit in another tab could quietly send requests to your bank's API using your logged-in session, reading your data or moving money, all without your knowledge, because the browser would attach your bank cookies to those requests. To stop this, browsers enforce the SAME-ORIGIN POLICY: by default, JavaScript on one origin (scheme + host + port) may not read responses from a DIFFERENT origin. That safety default is exactly why your Angular app on port 4200 cannot, by default, call your API on port 3000, the browser treats them as different origins and blocks the read. CORS is the controlled way for a server to OPT IN: by sending specific headers (which the cors middleware adds), the API tells the browser 'I permit requests from this origin', so legitimate cross-origin calls like yours are allowed while random malicious ones are not. So CORS is not an obstacle invented to annoy you; it is the mechanism that lets you safely relax a security rule that exists to protect users. Understanding this turns the confusing error into an obvious, one-line fix, and teaches a real web-security principle. The block is the browser protecting the user; CORS is the server granting permission.
Summary
Key takeaways
- Middleware is a function (req, res, next) that runs during request processing; register it with app.use().
- It must call next() to pass control on, or it ends the response itself; middleware runs in a pipeline.
- Built-in: express.json() parses JSON bodies (enabling req.body); express.static() serves static files.
- CORS: browsers block cross-origin requests (Angular on one port to Express on another) unless the API allows it; the fix is app.use(cors()).
- The same-origin policy CORS relaxes exists to stop malicious sites calling other sites where you are logged in.
- Environment variables in a .env file (read via process.env) keep config and secrets out of the code.
- Memory hook: middleware runs in the middle; express.json parses, cors allows cross-origin, .env holds secrets.