Middlewares
Learning objectives
- You know the concept of middleware.
- You can create and use a middleware function.
- You know of the order in which middlewares are processed.
Middleware are functions that process requests made to the server. They are used to wrap the routing functionality and consequently can influence both incoming requests and outgoing responses.
Starting with middlewares
In Hono, middlewares are defined as functions that receive the context and a function called next
as parameters. The context is the same Hono context that we have already worked with, while the function next
is used to pass the request to the next middleware (or to the routing functionality, if no subsequent middlewares are defined). Middlewares are added to a Hono server using the use
method.
The following example demonstrates how to create a middleware that processes every request, but doesn't do anything with them.
import { Hono } from "https://deno.land/x/hono@v3.12.11/mod.ts";
const app = new Hono();
app.use("*", async (c, next) => {
await next();
});
app.get("/", (c) => c.text("Hello middlewares!"));
Deno.serve(app.fetch);
When we run the application, we do not see any difference in the behavior of the application when compared to an application that would not have the middleware function in place. This is to be expected, as the middleware function solely passes the request forward by calling the next
function.
Processing requests with middleware
To demonstrate how middlewares can process and influence the request, we create a middleware that redirects all non-GET /
requests to making a GET request to /
. This can be achieved by checking the path and the method of the request, and calling the redirect
method of the context if the path is not /
or the method is not GET.
On the other hand, if the method is GET and the path is /
, we call the next
function in the middleware, passing the request to the next middleware or the routing functionality.
import { Hono } from "https://deno.land/x/hono@v3.12.11/mod.ts";
const app = new Hono();
app.use("*", async (c, next) => {
if (c.req.path !== "/" || c.req.method !== "GET") {
return c.redirect("/");
}
await next();
});
app.get("/", (c) => c.text("Hello middlewares!"));
Deno.serve(app.fetch);
When we launch the above application, we can see that all non-GET requests to /
are redirected to do a GET request to /
. This is because the middleware function is called before the routing functionality, and the middleware function redirects each request before the routing functionality can process the request.
curl localhost:8000
Hello middlewares!%
curl -v localhost:8000/hello
...
< HTTP/1.1 302 Found
< location: /
%
curl -v -X POST localhost:8000
...
< HTTP/1.1 302 Found
< location: /
%
Middleware processing order
Middlewares are processed in the order in which they are added to the application. This means that the first middleware added to the application is the first middleware to be called when a request is made to the server, followed by the next middleware, and so on. The following example demonstrates this behavior by adding two middlewares to the application, both of which print information to the console log.
import { Hono } from "https://deno.land/x/hono@v3.12.11/mod.ts";
const app = new Hono();
app.use("*", async (c, next) => {
console.log("Middleware 1 - before");
await next();
console.log("Middleware 1 - after");
});
app.use("*", async (c, next) => {
console.log("Middleware 2 - before");
await next();
console.log("Middleware 2 - after");
});
app.get("/", (c) => c.text("Hello middlewares!"));
Deno.serve(app.fetch);
When we make a request to the server, the following messages are logged to the console.
Middleware 1 - before
Middleware 2 - before
Middleware 2 - after
Middleware 1 - after
Question not found or loading of the question is still in progress.
Middlewares and paths
Middlewares can also be implemented so that they apply only to specific subpaths. As an example, the following middleware is applied only to requests to path /secrets
and its subpaths.
import { Hono } from "https://deno.land/x/hono@v3.12.11/mod.ts";
const app = new Hono();
app.use("/secrets/*", async (c, next) => {
console.log("Secret middleware - before");
await next();
console.log("Secret middleware - after");
});
app.get("/", (c) => c.text("Hello middlewares!"));
Deno.serve(app.fetch);
Next, we look at some examples of middleware and their use.