H
Hono•2mo ago
thibault

Beginner: infer context type

Hey everyone, this might seem like a simple question but I couldn't find an answer online for it. In the following snipet how could I change the type of session from (Session | null) to (Session) since I have a middleware that checks if it's null before the controller ? here is the code:
export type Ctx = { Variables: { session: Session | null } };

export const app = new Hono<Ctx>()
.use(async (c, n) => {
const token = getCookie(c, "session") ?? "";

const session = await validateSession(token);

c.set("session", session);

await n();
})
.post(
"/sign-out",
async (c, n) => {
if (!c.var.session) {
throw new HTTPException(401, { message: "unauthorized" });
}

await n();
},
async (c) => {
c.var.session; // currently Session | null but it should be Session
},
);
export type Ctx = { Variables: { session: Session | null } };

export const app = new Hono<Ctx>()
.use(async (c, n) => {
const token = getCookie(c, "session") ?? "";

const session = await validateSession(token);

c.set("session", session);

await n();
})
.post(
"/sign-out",
async (c, n) => {
if (!c.var.session) {
throw new HTTPException(401, { message: "unauthorized" });
}

await n();
},
async (c) => {
c.var.session; // currently Session | null but it should be Session
},
);
13 Replies
32cls
32cls•2mo ago
hi, i'm not really sure that's relevant but I think I did something a bit similar for handling ory authentication / registration:
import { createMiddleware } from "@hono/hono/factory";
import * as sdk from "@ory/client-fetch";
import { findUserById, insertUser } from "./db/db.ts";
import { Context } from "@hono/hono";

export type SessionEnv = {
Variables: {
identity: sdk.Identity;
};
};

export const authMiddleware = createMiddleware(
async (c: Context<SessionEnv>, next) => {
try {
const session = await ory.toSession({ cookie: c.req.header("cookie") });
if (!session.identity) {
throw Error("Identity couldn't be found in session");
}
c.set("identity", session.identity);
const foundUser = await findUserById(session.identity?.id);
if (!foundUser) {
await insertUser({
id: session.identity.id,
username: session.identity.traits.username,
email: session.identity.traits.email,
});
}
return await next();
} catch (_error) {
c.status(401);
return c.text("401 Unauthorized");
}
},
);
import { createMiddleware } from "@hono/hono/factory";
import * as sdk from "@ory/client-fetch";
import { findUserById, insertUser } from "./db/db.ts";
import { Context } from "@hono/hono";

export type SessionEnv = {
Variables: {
identity: sdk.Identity;
};
};

export const authMiddleware = createMiddleware(
async (c: Context<SessionEnv>, next) => {
try {
const session = await ory.toSession({ cookie: c.req.header("cookie") });
if (!session.identity) {
throw Error("Identity couldn't be found in session");
}
c.set("identity", session.identity);
const foundUser = await findUserById(session.identity?.id);
if (!foundUser) {
await insertUser({
id: session.identity.id,
username: session.identity.traits.username,
email: session.identity.traits.email,
});
}
return await next();
} catch (_error) {
c.status(401);
return c.text("401 Unauthorized");
}
},
);
let me know if that helps!
thibault
thibaultOP•2mo ago
Thanks for the help, but unfortunatly that doesn't really solve my issue as your type is marked as sdk.Identity
32cls
32cls•2mo ago
and can't you type your session in Ctx as Session? what is the retun type of await validateSession(token)?
thibault
thibaultOP•2mo ago
it would be Session | null as it's a global middleware, and the token could be invalid
32cls
32cls•2mo ago
OK so from my understanding, you're applying a global middleware to all your routes, but want to ensure authentification only for some of them (hence the null support)? Or for all routes? From my basic understanding of Hono, I think the envs you provide as a type to your Hono are designed in case you wish to pass extra args between middlewares / handlers / routes. For me, you could imagine using a Session type and rather than checking the c.var.session in your post route just do something like:
export type Ctx = { Variables: { session: Session } };

export const app = new Hono<Ctx>()
.use(async (c, n) => {
const token = getCookie(c, "session") ?? "";

const session = await validateSession(token);
if (!session){
return c.text("401 Unauthorized");
}
c.set("session", session);

return await n();
})
...

export type Ctx = { Variables: { session: Session } };

export const app = new Hono<Ctx>()
.use(async (c, n) => {
const token = getCookie(c, "session") ?? "";

const session = await validateSession(token);
if (!session){
return c.text("401 Unauthorized");
}
c.set("session", session);

return await n();
})
...

and in your routes c.get("session") would never be null
thibault
thibaultOP•2mo ago
ok nice that makes sense, thanks 🙂
32cls
32cls•2mo ago
and if you want to have some routes that don't need authentication, just specify a restricting path for the protected ones or provide the middleware explicitly to each route
thibault
thibaultOP•2mo ago
true shame that hono isn't cascading the types to the other routes / middleware though but that works as well
ambergristle
ambergristle•2mo ago
it does, sort of
const auth = createMiddleware<{ session: Session }>(
async (c, next) => {}
)

const app = new Hono<{ session: Session | null }>()
.use('*', auth)
.get('/', async (c) => {
const session = c.var.session; // Session
})
const auth = createMiddleware<{ session: Session }>(
async (c, next) => {}
)

const app = new Hono<{ session: Session | null }>()
.use('*', auth)
.get('/', async (c) => {
const session = c.var.session; // Session
})
only inline handlers + middleware can infer the types though; otherwise they need to be generically typed i don't think you get anything out of the Session | null type, unless there are optionally-authed routes or something, but you can definitely use inference to narrow the type
thibault
thibaultOP•2mo ago
Thanks @ambergristle this makes a lot more sense
ambergristle
ambergristle•2mo ago
it's worth taking a look at these: - https://hono.dev/docs/guides/middleware - https://hono.dev/docs/guides/best-practices - https://hono.dev/docs/helpers/factory i also wrote this article about (validation) middleware in hono, which touches some on how typing works: https://dev.to/fiberplane/hacking-hono-the-ins-and-outs-of-validation-middleware-2jea
thibault
thibaultOP•2mo ago
awesome, thanks for that
ambergristle
ambergristle•2mo ago
np

Did you find this page helpful?