iron-session
iron-session copied to clipboard
Implementing iron-session with TRPC
So I'm having issues trying to slam iron-session with TRPC. I'm using create-t3-app to start with and before v8, I just used to define (or redefine) IronSession when creating my TRPC context. I assumed V8 was pretty much the same but for some reason, running session.save() inside my procedures doesn't save anything. I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works. And yes, I can access the session values inside my procedures (but save doesn't work). Any ideas why?
My TRPC Context
export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = await getIronSession<{ user: string }>(cookies(), sessionOptions);
return {
db,
...opts,
session,
};
};
My procedure (from create-t3 boilerplate)
create: publicProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));
//This prints out the session.user variable I have set in my custom route
console.log('old session',ctx.session);
//this also works
ctx.session.user = 'new user';
// This doesn't work
await ctx.session.save();
return true;
})
Edit* To add, I already tried calling getIronSession inside the procedure instead. Still didn't work.
@Sealis04 Thank you, would you be able to create a very simple GitHub repository where I can npm install && npm run dev
and see the issue myself?
Are you running this locally? HTTP or HTTPS? What the sessionOptions in your case (hide the password 👍 )
I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works.
This is reassuring at least, let's see why in some other cases it doesn't.
@Sealis04 Thank you, would you be able to create a very simple GitHub repository where I can
npm install && npm run dev
and see the issue myself?Are you running this locally? HTTP or HTTPS? What the sessionOptions in your case (hide the password 👍 )
https://github.com/Sealis04/testironsession
Made two ways to change the session here, one is just the submit in home which goes to the procedure made with TRPC, the one that doesn't save the session. The other is with a separate route from TRPC, just visit /setSession. Also, made /getSession to check if the session values change (which doesn't).
This was with HTTP, as for sessionOptions it's just these ones
export const sessionOptions = {
password: my_super_secret_cookie_password,
cookieName: "Random Cookie Name",
cookieOptions: {
maxAge: undefined,
secure: false,
},
}
I am having the same problem, following
Was there any solution to this? I am attempting to run save
on the session in a tRPC mutation, and when I read the session from another tRPC mutation after I call save
, the session object remains unchanged.
I have a working example of this. I have a special loginProcedure
that is used on procedures that write cookies (login, signup, otp verify etc.)
export const loginProcedure = t.procedure.use(
t.middleware(async ({ ctx, next }) => {
const result = await next({ ctx });
const resultCtx = "ctx" in result ? (result.ctx as Context) : undefined;
if (resultCtx?.sessionUser) {
// resultCtx is not the ctx passed to `responseMeta` where we need
// `_session` to set the cookie. We would do it all in `responseMeta` but
// it is not and awaitable unfortunately.
ctx._session = await sealData(
{
email: resultCtx.sessionUser.email,
id: resultCtx.sessionUser.id,
} satisfies SessionUser,
{ password: ctx.env.SECRET, ttl: 60 * 60 * 24 * 365 }, // 1 year
);
}
return result;
}),
);
Then my fetchRequestHandler
has this responseMeta
:
responseMeta({ ctx, paths, errors }) {
const allOk = errors.length === 0;
if (
allOk &&
ctx?._session &&
paths?.find((path) =>
["auth.setPassword", "auth.login", "auth.verifyOtpCode"].includes(
path,
),
)
) {
return {
headers: {
"set-cookie": `__session=${
ctx._session
}; Max-Age=2592000; SameSite=Strict; Path=/; ${
ENV === "development" ? "" : "Secure; "
}HttpOnly`,
},
};
}
return {};
}
Not the most elegant way to set cookies, but it works.
So I'm having issues trying to slam iron-session with TRPC. I'm using create-t3-app to start with and before v8, I just used to define (or redefine) IronSession when creating my TRPC context. I assumed V8 was pretty much the same but for some reason, running session.save() inside my procedures doesn't save anything. I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works. And yes, I can access the session values inside my procedures (but save doesn't work). Any ideas why?
My TRPC Context
export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await getIronSession<{ user: string }>(cookies(), sessionOptions); return { db, ...opts, session, }; };
My procedure (from create-t3 boilerplate)
create: publicProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ ctx, input }) => { // simulate a slow db call await new Promise((resolve) => setTimeout(resolve, 1000)); //This prints out the session.user variable I have set in my custom route console.log('old session',ctx.session); //this also works ctx.session.user = 'new user'; // This doesn't work await ctx.session.save(); return true; })
Edit* To add, I already tried calling getIronSession inside the procedure instead. Still didn't work.
Hey to whoever is having this problem, simply add this to headers
in httpLink
or httpBatchLink
and you should be good
Gist here
I am facing the exact same issue.
for anyone that still have the problem, maybe you can try my solution.
after trying so many ways, and of course bcoz i'm too lazy to understand the TRPC concept, at the end i choose to use Server Action for doing mutation the session (save and destroy)
it is very easy to implement rather than trying to understand the whole TRPC concept and React Query (if you use t3 like me).
the cons is you have to manually setup all the necessary React Query stuff to get the benefit of using react query itself like onSuccess, onError, isPending, etc.
or if you're beginner in React Query and you're in hurry just manually setup hooks for handing state or callback after success and error.
you can watch this video from Lama Dev, if you don't know how to Server Action. https://www.youtube.com/watch?v=p_FiVGxyksI&t=967s