Middleware
Middlewares in Harpia are functions that run before your final route controller. They have access to the Request and Response objects, and the next function to pass control to the subsequent middleware or controller in the chain.
Middlewares are perfect for tasks like logging, authentication, parsing headers, or augmenting the request object.
Defining a Middleware
A middleware is just a function that accepts req, res, and next.
import type { Request, Response, NextFunction } from "@harpiats/core";
const logger = (req: Request, res: Response, next: NextFunction) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
// You MUST call next() to continue the request lifecycle
next();
};
Crucial Step
If you do not call next() inside a middleware, the request will hang indefinitely. Always ensure next() is called, unless you are deliberately ending the request (e.g., returning an error response).
Global Middlewares
You can register middlewares that run for every single request hitting your application using app.use().
import harpia from "@harpiats/core";
const app = harpia();
app.use(logger);
app.get("/", (req, res) => {
res.send("Hello with logging!");
});
Path-Specific Global Middlewares
You can also restrict a global middleware to a specific path prefix. It will run for any request whose URL starts with the given path.
// This middleware will only run for requests starting with "/api"
app.use("/api", apiAuthMiddleware);
Route-Specific Middlewares
If you only want a middleware to apply to a specific route, you can pass it directly into the route definition before the final controller.
const ensureAuthenticated = (req, res, next) => {
if (!req.headers.get("Authorization")) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
};
// Apply to a single route
app.get("/protected-data", ensureAuthenticated, (req, res) => {
res.json({ secret: "This is a secret" });
});
// You can chain multiple middlewares!
app.post("/upload", [ensureAuthenticated, validateUpload], (req, res) => {
res.send("Upload successful");
});
Breaking the Chain
To stop the execution chain and prevent subsequent middlewares or the controller from running, simply send a response and do not call next().
const blockSuspiciousIPs = (req, res, next) => {
if (req.ip === "192.168.1.100") {
// Send a response early. The chain stops here.
return res.status(403).send("Forbidden");
}
next();
};