T
TanStack2mo ago
rare-sapphire

loader fn not setting response headers like in 1.131.43

The following was working before I upgraded to 1.133.13. The expectation is that upon navigating to this route, the server should set the session cookie in the response headers:
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setHeaders(Object.fromEntries(headers.entries()));
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
loaderDeps: ({ search }) => ({ token: search.token }),
loader: async ({ deps }) => loader({ data: { token: deps.token } }),
component: () => {
useEffect(() => {
// some client-side code
}, []);
return null;
},
});
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setHeaders(Object.fromEntries(headers.entries()));
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
loaderDeps: ({ search }) => ({ token: search.token }),
loader: async ({ deps }) => loader({ data: { token: deps.token } }),
component: () => {
useEffect(() => {
// some client-side code
}, []);
return null;
},
});
In 1.133.13 I changed this bit and it's no longer working:
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setResponseHeaders(headers);
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setResponseHeaders(headers);
3 Replies
equal-aqua
equal-aqua2mo ago
setheaders should be setResponseHeaders
rare-sapphire
rare-sapphireOP2mo ago
@Manuel Schiller yeah. i mentioned i changed to that and it's no longer working i kind of feel the response headers from the server fn are not being bubbled up by the loader callback, if that makes sense @Manuel Schiller i feel there's an issue with setResponseHeaders. I tried calling the server fn directly in a useEffect, and the response header is not being set either:
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setResponseHeaders(headers);
return { success: true };
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
component: () => {
const { token } = Route.useSearch();
useEffect(() => {
void (async () => {
const { success } = await loader({ data: { token } }); // NO response header
// rest of logic
})();
}, []);
return null;
},
});
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
setResponseHeaders(headers);
return { success: true };
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
component: () => {
const { token } = Route.useSearch();
useEffect(() => {
void (async () => {
const { success } = await loader({ data: { token } }); // NO response header
// rest of logic
})();
}, []);
return null;
},
});
however, if i return a raw response like this, it does work as expected:
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
return json(
{ success: true },
{
headers,
},
);
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
component: () => {
const { token } = Route.useSearch();
useEffect(() => {
void (async () => {
const { success } = await loader({ data: { token } }).then((r) => r.json()); // DOES include response header
// rest of logic
})();
}, []);
return null;
},
});
const loader = createServerFn()
.inputValidator(
z.object({
token: z.string(),
}),
)
.handler(async ({ data: { token } }) => {
// simplified
const headers = new Headers();
headers.set('Set-Cookie', renderCookie(accessToken));
return json(
{ success: true },
{
headers,
},
);
});

export const Route = createFileRoute('/auth/callback/magic')({
validateSearch: zodValidator(
z.object({
token: z.string(),
}),
),
component: () => {
const { token } = Route.useSearch();
useEffect(() => {
void (async () => {
const { success } = await loader({ data: { token } }).then((r) => r.json()); // DOES include response header
// rest of logic
})();
}, []);
return null;
},
});
@Manuel Schiller setResponseHeader singular does work, however. i think i found the issue. i'll work on a PR
rare-sapphire
rare-sapphireOP2mo ago
GitHub
fix(start-server-core): use Headers.entries() to iterate headers co...
Summary This PR fixes a critical bug in setResponseHeaders() where no headers were being set at all. The Bug The previous code used Object.entries(headers) to iterate over a Headers object: for (co...

Did you find this page helpful?