Cookie Management
Mount the first-party cookie() middleware to get a Cookie Jar Instance in your handlers. Read, set, sign, and delete HTTP cookies.
Cookies are not available by default. Mount the first-party cookie() middleware from @taserjs/router/middleware/cookie on a root or scoped layout. Handlers under that layout receive a Cookie Jar Instance as { cookies }.
Mount cookie() on a Layout
Root layout (src/routes/$.ts)
import { cookie } from "@taserjs/router/middleware/cookie";
import { t } from "@taserjs/router";
export default t.layout("/*").use(
cookie({
secret: process.env.COOKIE_SECRET || "your-secure-secret-key-32-chars-long",
httpOnly: true,
sameSite: "Lax",
secure: process.env.NODE_ENV === "production",
path: "/",
}),
);Scoped directory layout
Mount only where cookies are needed so sibling branches stay free of cookie headers:
import { cookie } from "@taserjs/router/middleware/cookie";
import { t } from "@taserjs/router";
export default t.layout("/auth/*").use(
cookie({
secret: process.env.COOKIE_SECRET!,
path: "/auth",
}),
);Mount before you use
After you mount cookie() on a layout, handlers under that layout can destructure {cookies}.
Without the middleware, cookies are not available on the handler.
Reading Cookies
import { json } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/me").handler(({ cookies }) => {
const theme = cookies.get("theme") ?? "light";
const allCookies = cookies.get(); // all parsed request cookies
return json({ theme, allCookies });
});Setting Cookies
import { ok } from "@taserjs/router/reply";
import { z } from "zod";
import { t } from "@taserjs/router";
export default t
.post("/preferences")
.body(
z.object({
theme: z.enum(["light", "dark", "system"]),
}),
)
.handler(({ cookies, req }) => {
cookies.set("theme", req.body.theme, {
path: "/",
maxAge: 60 * 60 * 24 * 365,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "Lax",
});
return ok({ success: true });
});Set-Cookie headers are applied on the response automatically — you do not append headers manually.
Cryptographically Signed Cookies
Pass secret when constructing cookie({ secret }). Signed helpers then use that default:
import { json } from "@taserjs/router/reply";
import { z } from "zod";
import { t } from "@taserjs/router";
export default t
.post("/auth/login")
.body(z.object({ userId: z.string() }))
.handler(async ({ cookies, req }) => {
await cookies.setSigned("session_user", req.body.userId);
return json({ message: "Logged in successfully" });
});import { json, unauthorized } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/auth/session").handler(async ({ cookies, ctx }) => {
const userId = await cookies.getSigned("session_user");
if (!userId) {
return unauthorized({ message: "Invalid or expired session cookie" });
}
const user = await ctx.db.getUser(userId);
return json(user);
});Deleting Cookies
import { json } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.post("/auth/logout").handler(({ cookies }) => {
const previousSession = cookies.delete("session_user", { path: "/" });
return json({ message: "Logged out", previousSession });
});Secure Cookie Prefixes (__Secure- and __Host-)
prefix: "secure": Prepends__Secure-and enforcessecure: true.prefix: "host": Prepends__Host-, enforcessecure: true,path: "/", and prohibitsdomain.
cookies.set("session", sessionId, { prefix: "host" });
const sessionId = cookies.get("session", "host");CookieJarOptions Reference
Defaults passed to cookie(options) merge into every set / setSigned / delete call:
Prop
Type
Cookie Jar Methods
Prop
Type
Related Guides
Reply Helpers
Send clean, status-discriminated HTTP responses using tree-shakeable reply helpers: json(), ok(), notFound(), and redirect().
Response Contracts
Enforce compile-time return shape safety and runtime response validation with .returns(). Eliminate response drift between backend and frontend.