middleware on a router level

Right now, i have this setup, where canExperiment is also a middleware:
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.get('/:experimentId/statistics', canExperiment, significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', canExperiment, chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', canExperiment, currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.get('/:experimentId/statistics', canExperiment, significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', canExperiment, chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', canExperiment, currenciesHandler);
in express, i would do something like:
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.use(canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.use(canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
but i can't seem to make this work in Hono. to clarify: - i want all the routes that start with :experimentId to run through canExperiment where i need access to the experimentId parameter. - i have tried several ways, including creating a sub-router, but i can't seem to make it work Thank you
61 Replies
Nico
Nico6mo ago
Hi @rubberduckies, What you provided should work the .use() will apply to all routes defined below in this case. What are you seeing in your case that is making it not work?
rubberduckies
rubberduckiesOP6mo ago
i don' remember anymore i'll try to redo it
Nico
Nico6mo ago
So I just wrote this example and have no problems with it
// index.ts
import { Hono } from 'hono';
import test from "./routes";

const app = new Hono();

app.get('/', (c) => {
return c.text('Hello Hono');
});

app.route('/', test)

export default app;
// index.ts
import { Hono } from 'hono';
import test from "./routes";

const app = new Hono();

app.get('/', (c) => {
return c.text('Hello Hono');
});

app.route('/', test)

export default app;
// routes.ts
import { Hono } from "hono";

const test = new Hono();

test.get('/test', (c) => {
return c.text('Test');
});

test.use(async (c, next) => {
console.log(c.req.url);
await next();
});


test.get('/:test', (c) => {
return c.text('Test');
});

export default test;
// routes.ts
import { Hono } from "hono";

const test = new Hono();

test.get('/test', (c) => {
return c.text('Test');
});

test.use(async (c, next) => {
console.log(c.req.url);
await next();
});


test.get('/:test', (c) => {
return c.text('Test');
});

export default test;
rubberduckies
rubberduckiesOP6mo ago
so
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
statisticsRouter.use(canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
statisticsRouter.use(canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
and in my canExperiment
export const canExperiment = async (c, next) => {
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
if (!experimentId || isNaN(experimentId)) return next();
export const canExperiment = async (c, next) => {
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
if (!experimentId || isNaN(experimentId)) return next();
experimentId is undefined here (c.body comes from my own previous middleware but in this case, i need for c.req.param('experimentId') to be available @Nico
Artifex
Artifex6mo ago
in middleware you can also c.set() then call next()
Nico
Nico6mo ago
I was about to bring up the same point Are you using typescript?
rubberduckies
rubberduckiesOP6mo ago
i don't see how that's relevant here... i will use it in another setting no typescrypt
Artifex
Artifex6mo ago
you can use the one later on as c.var['objname']
Nico
Nico6mo ago
So c.body.experimentId is not called yet so that will never be there. It will also never fire because you have return next.
rubberduckies
rubberduckiesOP6mo ago
again, my problem here is that i need access to c.req.param('experimentId') in my canExperiment middleware
Nico
Nico6mo ago
You need to await next()
rubberduckies
rubberduckiesOP6mo ago
i don't think you guys are getting the picture, sorry or i'm a bit confused, it's also possible
Nico
Nico6mo ago
No I understand, can we see the whole middleware?
Artifex
Artifex6mo ago
let me recreate what you are trying to do lets see if we are on the same page
rubberduckies
rubberduckiesOP6mo ago
import { db, eq, desc, Experiments, ActivityLog } from 'pertentodb';

export const canExperiment = async (c, next) => {
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
if (!experimentId || isNaN(experimentId)) return next();

const experiment = await db.query.Experiments.findFirst({
where: eq(Experiments.id, experimentId),
with: {
website: {
with: {
company: true,
},
},
variants: { with: { changes: true, visitorCount: { columns: { count: true } }, experiment: {} } },
activityLog: { with: { user: true }, orderBy: desc(ActivityLog.createdAt) },
deviceTargeting: true,
urlTargeting: true,
},
});

const { id: companyId, parentCompanyId } = experiment.website.company;
if (![companyId, parentCompanyId].includes(c.user.companyId)) {
if (c.user.role !== 'Super Admin') {
return c.text('FORBIDDEN', 403);
}
}

c.experiment = experiment;

return next();
};
import { db, eq, desc, Experiments, ActivityLog } from 'pertentodb';

export const canExperiment = async (c, next) => {
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
if (!experimentId || isNaN(experimentId)) return next();

const experiment = await db.query.Experiments.findFirst({
where: eq(Experiments.id, experimentId),
with: {
website: {
with: {
company: true,
},
},
variants: { with: { changes: true, visitorCount: { columns: { count: true } }, experiment: {} } },
activityLog: { with: { user: true }, orderBy: desc(ActivityLog.createdAt) },
deviceTargeting: true,
urlTargeting: true,
},
});

const { id: companyId, parentCompanyId } = experiment.website.company;
if (![companyId, parentCompanyId].includes(c.user.companyId)) {
if (c.user.role !== 'Super Admin') {
return c.text('FORBIDDEN', 403);
}
}

c.experiment = experiment;

return next();
};
Nico
Nico6mo ago
That console log is not showing correct? And the route is working? Is there any other custom middleware besides this and the ones you added on the route
rubberduckies
rubberduckiesOP6mo ago
in express, i would do something like:
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
const byIdRouter = new Hono();
statisticsRouter.use('/:experimentId', canExperiment, byIdRouter);
byIdRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
byIdRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
byIdRouter.get('/:experimentId/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
const byIdRouter = new Hono();
statisticsRouter.use('/:experimentId', canExperiment, byIdRouter);
byIdRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
byIdRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
byIdRouter.get('/:experimentId/currencies', currenciesHandler);
and everything would work there are two middlewares, running fine.... a bodyParser and a jwt parser to get c.user and c.body
Nico
Nico6mo ago
Are you returning next in any?
rubberduckies
rubberduckiesOP6mo ago
my problem is here:
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
let experimentId = c.req.param('experimentId') || c.body.experimentId;
console.log(experimentId);
i'm getting undefined
Nico
Nico6mo ago
Make sure all is await next() or it will cause errors Like this
rubberduckies
rubberduckiesOP6mo ago
of course, those are working flawlessly, but i'm not trying to get any params there
Artifex
Artifex6mo ago
its all undefined here if you access this on middleware
Nico
Nico6mo ago
That's why I am asking if there is any other middleware. Becuase if it returned next, the context will not be passed to this middleware
rubberduckies
rubberduckiesOP6mo ago
i'm not sure where you're getting at, but c.user and c.body are reaching there, so that's not the problem at all
rubberduckies
rubberduckiesOP6mo ago
No description
rubberduckies
rubberduckiesOP6mo ago
the problem is ONLY with the :experimentId param not getting there forget about anything else
Artifex
Artifex6mo ago
idk why typescript is not shouting at you when you are accesing c.params('ex...') and c.body.... for better typesafety wrap middleware in createMiddleware
rubberduckies
rubberduckiesOP6mo ago
i don't care about typescript, not using it, even though the file is named .ts
Artifex
Artifex6mo ago
well if you enable strict true, you will see those things does not exist in its context yet
rubberduckies
rubberduckiesOP6mo ago
but i don't, please focus on the problem if you truly want to help
Artifex
Artifex6mo ago
We are trying to help
rubberduckies
rubberduckiesOP6mo ago
i know i'm sorry if i sounded like a dick not my intention at all but i feel you're focusing on things that have nothing to do with the problem at hand coming back to the origin: if i do this:
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.get('/:experimentId/statistics', canExperiment, significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', canExperiment, chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', canExperiment, currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', monitorHandler);
statisticsRouter.get('/:experimentId/statistics', canExperiment, significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', canExperiment, chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', canExperiment, currenciesHandler);
the :experimentId param gets to my canExperiment middleware where did you get c.params? i'm using c.req.params('experimentId')
Artifex
Artifex6mo ago
oh mybad
Nico
Nico6mo ago
You lost me with how you spoke when we tried to help. Sometimes issues come from other places, that's why I was checking. Looks like @Artifex has got you, another solution is stripping it from you URL. Good luck.
rubberduckies
rubberduckiesOP6mo ago
well, thanks for your help again, i didn't want to offend anyone, i was just getting frustrated @Nico , @Artifex i just found the solution
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
statisticsRouter.use('/:experimentId/*', canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler);
statisticsRouter.use('/:experimentId/*', canExperiment);
statisticsRouter.get('/:experimentId/statistics', significanceMiddleware, significanceHandler);
statisticsRouter.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler);
statisticsRouter.get('/:experimentId/currencies', currenciesHandler);
Nico
Nico6mo ago
You need to add the url to the middleware
rubberduckies
rubberduckiesOP6mo ago
so simple
Artifex
Artifex6mo ago
Are you not using rpc?
rubberduckies
rubberduckiesOP6mo ago
no
Artifex
Artifex6mo ago
might want to chain those later on for rpc
rubberduckies
rubberduckiesOP6mo ago
what if i don't want to use rpc?
Artifex
Artifex6mo ago
u can also chain it so you get a better readability
statisticsRouter
.basePath('/:experimentId')
.use(canExperiment)
.get('/statistics', significanceMiddleware, significanceHandler)
.get('/chart-data', chartDataMiddleware, chartDataHandler)
.get('/currencies', currenciesHandler);
statisticsRouter
.basePath('/:experimentId')
.use(canExperiment)
.get('/statistics', significanceMiddleware, significanceHandler)
.get('/chart-data', chartDataMiddleware, chartDataHandler)
.get('/currencies', currenciesHandler);
rubberduckies
rubberduckiesOP6mo ago
that looks good but it doesn't work for my first route /monitor i will definitely use it elsewhere
Artifex
Artifex6mo ago
u can append it before the basepath i think
rubberduckies
rubberduckiesOP6mo ago
i'll try it
Nico
Nico6mo ago
Are you ever planning on using typescript?
rubberduckies
rubberduckiesOP6mo ago
🙂 maybe one day, i don't like it but again, not trying to be a dick, that has nothing to do with the problem i was trying to solve i'm really thankful for the time you both put into this with me
Artifex
Artifex6mo ago
well c.body.experimentId does not exist totally tho
rubberduckies
rubberduckiesOP6mo ago
i know but it will in other cases
Nico
Nico6mo ago
I never mentioned about using it. I just asked because you will see an error to help solve it if you have it on. I was assuming you were not
rubberduckies
rubberduckiesOP6mo ago
and if i was using typescrypt it would be a nightmare to get it to work
Nico
Nico6mo ago
It was for my debugging purpose I was going to let you know that too. If you planned on using it I was going to show you two things to add now so it's not a nightmare
rubberduckies
rubberduckiesOP6mo ago
i',m sorry @Nico and @Artifex , sometimes written communication doesn't let on exactly the tone we want to convey again, i'm super thankful for your help and i hope i haven't gotten into any kind of black list for future inquiries
Nico
Nico6mo ago
Nope, you're good
rubberduckies
rubberduckiesOP6mo ago
@Artifex , i just tried
export const statisticsRouter = new Hono();
statisticsRouter
.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler)
.basePath('/:experimentId')
.use('/:experimentId/*', canExperiment)
.get('/:experimentId/statistics', significanceMiddleware, significanceHandler)
.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler)
.get('/:experimentId/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter
.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler)
.basePath('/:experimentId')
.use('/:experimentId/*', canExperiment)
.get('/:experimentId/statistics', significanceMiddleware, significanceHandler)
.get('/:experimentId/chart-data', chartDataMiddleware, chartDataHandler)
.get('/:experimentId/currencies', currenciesHandler);
and it's not working i'm gonna keep what i had and am more than good with it wait haha yes, this is perfect:
export const statisticsRouter = new Hono();
statisticsRouter
.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler)
.basePath('/:experimentId')
.use(canExperiment)
.get('/statistics', significanceMiddleware, significanceHandler)
.get('/chart-data', chartDataMiddleware, chartDataHandler)
.get('/currencies', currenciesHandler);
export const statisticsRouter = new Hono();
statisticsRouter
.get('/monitor', experimentsMonitorMiddleware, experimentsMonitorHandler)
.basePath('/:experimentId')
.use(canExperiment)
.get('/statistics', significanceMiddleware, significanceHandler)
.get('/chart-data', chartDataMiddleware, chartDataHandler)
.get('/currencies', currenciesHandler);
i'm also super tired, i've been at this for more hours than i should today
Artifex
Artifex6mo ago
just append the .get on new Hono()
Nico
Nico6mo ago
I would recommend you fix the return next() to await next() I promise you it can cause issues down the road. It can prevent middleware from firing. The only time in middleware a return should be used is if you need to return a response
rubberduckies
rubberduckiesOP6mo ago
have been dealing with Google, to aprove our extension and oauth app and yes, frustration permiates all in all, i'm really happy and excited with moving my services to hono i have worked with express for more than 10 years and it's just the growing pains maybe 10 years is a bit of a stretch hahah
Artifex
Artifex6mo ago
FallOutBoyVEVO
YouTube
Fall Out Boy - Thnks fr th Mmrs (Official Music Video)
Official video for Fall Out Boy “Thnks fr th Mmrs” from the 2007 album, ‘Infinity on High’. Listen to Fall Out Boy: https://Stream.lnk.to/FOB Watch more videos by Fall Out Boy: https://FallOutBoy.lnk.to/Socials/youtube New album ‘So Much (for) Stardust’ OUT NOW: https://falloutboy.lnk.to/somuchforstardust So Much for (Tour) Dust Tickets ON S...
rubberduckies
rubberduckiesOP6mo ago
thanks for the memories
rubberduckies
rubberduckiesOP6mo ago
but yeah, things are looking good. Statistics is ported, tomorrow, another 5
No description
Want results from more Discord servers?
Add your server