0

I want to chain 2 middlewares functions in deno oak like this:

const addProductPanier = async(req:any,res:any) => {...}
const showPanier = async(ctx:any) => {...}

router.post('/OBV/panier',addProductPanier).post('/OBV/panier',showPanier);

I've tried in so many ways and searched for a solution in the oak documentation, but string paths in .post can not be the same, I need to have for example:

router.post('/OBV/panier',addProductPanier).post('/OBV/panier/one',showPanier);

I also tried to merge the 2 middlewares in one, it worked few days ago, but for some unknown reason it don't work anymore replying me that response.render() is not a function. As you can see I separated both of them to have addProductPanier sending data to my database and showPanier to fetch this data and display it on my html page using ctx.render().

So do you know how to chain multiples middlewares to one route ?

jps
  • 20,041
  • 15
  • 75
  • 79

1 Answers1

2

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" }.

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • Thanks for all these explainations man ! i'm trying to follow these two methods, but i saw in your middlewares functions that you're using next() at the end of the functions, is it mandatory ? –  Jul 06 '22 at 08:13
  • because one of my middleware function, the first one "addProductPanier" using async (request, response) and i can not add a third paramater like next, i get this error message : Argument of type '({ request, response, next }: { request: any; response: any; next: any; }) => Promise' is not assignable to parameter of type 'RouterMiddleware<"/OBV/panier", Record, Record>'. Types of parameters '__0' and 'context' are incompatible –  Jul 06 '22 at 08:29
  • @Fefe "_...is it mandatory?_": Yes (unless you want to terminate the entire middleware chain at that function). The middleware signature `(req, res) => any` is not supported by Oak. Oak's middleware signature is always `(ctx, next) => unknown`. Reading the [documentation](https://deno.land/x/oak@v10.6.0/README.md?code#application-middleware-and-context) might be helpful to you. – jsejcksn Jul 06 '22 at 18:20
  • ok i've understood, right now i'm using (ctx,next) signature. –  Jul 07 '22 at 17:48