Answer summary
You can use Oak's middleware composition function (composeMiddlware
) or you can simply provide each middleware function as a positional argument to the router method.
Guided explanation
Because there's not a minimal, reproducible example in your question, I'll provide one below in the form of a simple greeting app and use it to address your question, detailing two ways to compose middleware on the same route.
Useful reference documentation:
Example app description
Let's say we want to create a web server that should respond to GET
requests at /greet
, and also allow an optional route parameter name
for the name to greet, so the route will look like this: /greet/:name?
. When that route is matched, the server should use individual middleware to:
- log the value of the
name
route parameter (in the server console), and then
- respond with a plaintext greeting message.
The middleware functions described above might look like this:
./middleware.ts
:
import { type RouterMiddleware } from "https://deno.land/x/oak@v10.6.0/mod.ts";
export const logName: RouterMiddleware<"/greet/:name?"> = (ctx, next) => {
const { name } = ctx.params;
console.log({ name });
return next();
};
export const sendGreeting: RouterMiddleware<"/greet/:name?"> = (ctx, next) => {
const name = ctx.params.name ?? "World";
const msg = `Hello ${name}!`;
ctx.response.body = msg;
ctx.response.type = "text/plain";
return next();
};
Now let's create a module where the routes will be defined. For now, we'll just initialize a router and export it so that there aren't type errors as we continue setup, but we'll come back here to explore the two composition methods:
./routes.ts
:
import { Router } from "https://deno.land/x/oak@v10.6.0/mod.ts";
export const router = new Router();
Let's also create a module where we initialize and export the app (and a function for printing a startup message to the console when the server starts):
./app.ts
:
import { Application } from "https://deno.land/x/oak@v10.6.0/mod.ts";
import { router } from "./routes.ts";
// This is not necessary, but is potentially helpful to see in the console
function printStartupMessage({ hostname, port, secure }: {
hostname: string;
port: number;
secure?: boolean;
}): void {
const address = new URL(
`http${secure ? "s" : ""}://${
hostname === "0.0.0.0" ? "localhost" : hostname
}:${port}/`,
).href;
console.log(`Listening at ${address}`);
console.log("Use ctrl+c to stop");
}
export const app = new Application();
app.addEventListener("listen", printStartupMessage);
app.use(router.routes());
app.use(router.allowedMethods());
For the last part of the setup, we'll create the main app entrypoint module where the server is started:
./main.ts
:
import { app } from "./app.ts";
await app.listen({ port: 8000 });
Now, let's return to ./routes.ts
to explore the composition methods:
Composing middleware functions
The first way that middleware can be composed is to use a function exported by Oak for exactly this purpose: composeMiddlware
The modified version of our routes module would look like this:
./routes.ts
:
import {
composeMiddleware,
Router,
} from "https://deno.land/x/oak@v10.6.0/mod.ts";
import { logName, sendGreeting } from "./middleware.ts";
export const router = new Router();
const greetMiddleware = composeMiddleware([logName, sendGreeting]);
router.get("/greet/:name?", greetMiddleware);
Or, more simply, each middleware function can just be supplied as a positional argument to the router method in order:
./routes.ts
:
import { Router } from "https://deno.land/x/oak@v10.6.0/mod.ts";
import { logName, sendGreeting } from "./middleware.ts";
export const router = new Router();
router.get("/greet/:name?", logName, sendGreeting);
Both of these produce the same result.
Testing the app
Start the app in the terminal console with the appropriate permissions for network access:
% deno run --allow-net=0.0.0.0:8000 main.ts
Listening at http://localhost:8000/
Use ctrl+c to stop
If you navigate to http://localhost:8000/greet in your browser, you should see the text Hello World!
in the viewport, and back in the terminal console a line with { name: undefined }
.
Again, if you navigate to http://localhost:8000/greet/visitor, you should see the text Hello visitor!
in the viewport, and back in the terminal console a line with { name: "visitor" }
.