Im using nextjs app router, trpc, prisma, and trying to implement login functionality with setting a cookie. Im trying to set cookie in res headers, but i get this error. I cannot figure out why?
<code>login.tsx:29 Error: TRPCClientError: Cannot read properties of undefined (reading 'set')
at TRPCClientError.from (TRPCClientError.mjs:40:20)
at eval (httpBatchStreamLink.mjs:125:106)
</code>
<code>login.tsx:29 Error: TRPCClientError: Cannot read properties of undefined (reading 'set')
at TRPCClientError.from (TRPCClientError.mjs:40:20)
at eval (httpBatchStreamLink.mjs:125:106)
</code>
login.tsx:29 Error: TRPCClientError: Cannot read properties of undefined (reading 'set')
at TRPCClientError.from (TRPCClientError.mjs:40:20)
at eval (httpBatchStreamLink.mjs:125:106)
Here is my code:
my user.ts // user router
<code>import { db } from "@/server/db";
import { createTRPCRouter, publicProcedure } from "../trpc";
import { UserCreateInputSchema } from "prisma/generated/zod";
import { hashPassword, verifyPassword } from "@/util/hashing";
import { z } from "zod";
import { encrypt, login } from "@/lib";
export const userRouter = createTRPCRouter({
create: publicProcedure
.input(UserCreateInputSchema)
.mutation(async ({ input }) => {
const { email, password, username, first_name, last_name } = input;
const hashedPassword = await hashPassword(password);
await db.user.create({
data: {
email,
password: hashedPassword,
username,
first_name,
last_name,
},
});
}),
getByUsername: publicProcedure
.input(z.object({ username: z.string() }))
.query(async ({ input }) => {
return db.user.findUnique({ where: { username: input.username } });
}),
login: publicProcedure
.input(
z.object({
username: z.string(),
password: z.string().min(6),
})
)
.mutation(async ({ input, ctx }) => {
const { username, password } = input;
const user = await db.user.findUnique({
where: { username },
});
if (!user || !(await verifyPassword(password, user.password))) {
throw new Error("Invalid username or password");
}
const session = encrypt(username);
console.log("session", session);
ctx.resHeaders.set("Set-Cookie", session);
return { success: true, username: user.username };
}),
});
</code>
<code>import { db } from "@/server/db";
import { createTRPCRouter, publicProcedure } from "../trpc";
import { UserCreateInputSchema } from "prisma/generated/zod";
import { hashPassword, verifyPassword } from "@/util/hashing";
import { z } from "zod";
import { encrypt, login } from "@/lib";
export const userRouter = createTRPCRouter({
create: publicProcedure
.input(UserCreateInputSchema)
.mutation(async ({ input }) => {
const { email, password, username, first_name, last_name } = input;
const hashedPassword = await hashPassword(password);
await db.user.create({
data: {
email,
password: hashedPassword,
username,
first_name,
last_name,
},
});
}),
getByUsername: publicProcedure
.input(z.object({ username: z.string() }))
.query(async ({ input }) => {
return db.user.findUnique({ where: { username: input.username } });
}),
login: publicProcedure
.input(
z.object({
username: z.string(),
password: z.string().min(6),
})
)
.mutation(async ({ input, ctx }) => {
const { username, password } = input;
const user = await db.user.findUnique({
where: { username },
});
if (!user || !(await verifyPassword(password, user.password))) {
throw new Error("Invalid username or password");
}
const session = encrypt(username);
console.log("session", session);
ctx.resHeaders.set("Set-Cookie", session);
return { success: true, username: user.username };
}),
});
</code>
import { db } from "@/server/db";
import { createTRPCRouter, publicProcedure } from "../trpc";
import { UserCreateInputSchema } from "prisma/generated/zod";
import { hashPassword, verifyPassword } from "@/util/hashing";
import { z } from "zod";
import { encrypt, login } from "@/lib";
export const userRouter = createTRPCRouter({
create: publicProcedure
.input(UserCreateInputSchema)
.mutation(async ({ input }) => {
const { email, password, username, first_name, last_name } = input;
const hashedPassword = await hashPassword(password);
await db.user.create({
data: {
email,
password: hashedPassword,
username,
first_name,
last_name,
},
});
}),
getByUsername: publicProcedure
.input(z.object({ username: z.string() }))
.query(async ({ input }) => {
return db.user.findUnique({ where: { username: input.username } });
}),
login: publicProcedure
.input(
z.object({
username: z.string(),
password: z.string().min(6),
})
)
.mutation(async ({ input, ctx }) => {
const { username, password } = input;
const user = await db.user.findUnique({
where: { username },
});
if (!user || !(await verifyPassword(password, user.password))) {
throw new Error("Invalid username or password");
}
const session = encrypt(username);
console.log("session", session);
ctx.resHeaders.set("Set-Cookie", session);
return { success: true, username: user.username };
}),
});
my trpc api route
<code>import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextResponse, type NextRequest } from "next/server";
import { env } from "@/env";
import { appRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a HTTP request (e.g. when you make requests from Client Components).
*/
const createContext = async (req: NextRequest, res: NextResponse) => {
return createTRPCContext({
headers: req.headers,
resHeaders: res.headers,
});
};
const handler = (req: NextRequest, res: NextResponse) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createContext(req, res),
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
);
}
: undefined,
});
export { handler as GET, handler as POST };
</code>
<code>import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextResponse, type NextRequest } from "next/server";
import { env } from "@/env";
import { appRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a HTTP request (e.g. when you make requests from Client Components).
*/
const createContext = async (req: NextRequest, res: NextResponse) => {
return createTRPCContext({
headers: req.headers,
resHeaders: res.headers,
});
};
const handler = (req: NextRequest, res: NextResponse) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createContext(req, res),
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
);
}
: undefined,
});
export { handler as GET, handler as POST };
</code>
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextResponse, type NextRequest } from "next/server";
import { env } from "@/env";
import { appRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a HTTP request (e.g. when you make requests from Client Components).
*/
const createContext = async (req: NextRequest, res: NextResponse) => {
return createTRPCContext({
headers: req.headers,
resHeaders: res.headers,
});
};
const handler = (req: NextRequest, res: NextResponse) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createContext(req, res),
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
);
}
: undefined,
});
export { handler as GET, handler as POST };
my createTRPCContext
<code>xport const createTRPCContext = async (opts: {
headers: Headers;
resHeaders: Headers;
}) => {
const session = cookies().get("session");
if (!session) {
return {
db,
...opts,
session: null,
};
}
interface MyJwtPayload extends JwtPayload {
username: string;
}
const decoded = jwt.verify(
session?.value,
process.env.JWT_SECRET!
) as MyJwtPayload;
const user = await db.user.findUnique({
where: { username: decoded.username },
});
return {
db,
...opts,
session: { user },
};
};
</code>
<code>xport const createTRPCContext = async (opts: {
headers: Headers;
resHeaders: Headers;
}) => {
const session = cookies().get("session");
if (!session) {
return {
db,
...opts,
session: null,
};
}
interface MyJwtPayload extends JwtPayload {
username: string;
}
const decoded = jwt.verify(
session?.value,
process.env.JWT_SECRET!
) as MyJwtPayload;
const user = await db.user.findUnique({
where: { username: decoded.username },
});
return {
db,
...opts,
session: { user },
};
};
</code>
xport const createTRPCContext = async (opts: {
headers: Headers;
resHeaders: Headers;
}) => {
const session = cookies().get("session");
if (!session) {
return {
db,
...opts,
session: null,
};
}
interface MyJwtPayload extends JwtPayload {
username: string;
}
const decoded = jwt.verify(
session?.value,
process.env.JWT_SECRET!
) as MyJwtPayload;
const user = await db.user.findUnique({
where: { username: decoded.username },
});
return {
db,
...opts,
session: { user },
};
};
I get the error on this line in user router
<code> ctx.resHeaders.set("Set-Cookie", session);
</code>
<code> ctx.resHeaders.set("Set-Cookie", session);
</code>
ctx.resHeaders.set("Set-Cookie", session);
also this is how i call the login from client
<code>
const loginUser = api.user.login.useMutation({
onSuccess: (data) => {
console.log("data", data);
router.push("/dashboard");
},
onError: (error) => {
console.error("Error: ", error);
},
});
const handleFormSubmit = (values: loginInputFormTypes) => {
console.log(values);
try {
loginUser.mutate(values);
} catch (error) {
console.error("Error: ", error);
}
form.reset();
};
</code>
<code>
const loginUser = api.user.login.useMutation({
onSuccess: (data) => {
console.log("data", data);
router.push("/dashboard");
},
onError: (error) => {
console.error("Error: ", error);
},
});
const handleFormSubmit = (values: loginInputFormTypes) => {
console.log(values);
try {
loginUser.mutate(values);
} catch (error) {
console.error("Error: ", error);
}
form.reset();
};
</code>
const loginUser = api.user.login.useMutation({
onSuccess: (data) => {
console.log("data", data);
router.push("/dashboard");
},
onError: (error) => {
console.error("Error: ", error);
},
});
const handleFormSubmit = (values: loginInputFormTypes) => {
console.log(values);
try {
loginUser.mutate(values);
} catch (error) {
console.error("Error: ", error);
}
form.reset();
};