S
SolidJS2mo ago
Merdot

Throw redirects in data APIs with "use server" at the top of the file not working.

Hi everyone! 👋 I'm working on a Solid Start project and have a question about query behavior and redirects. I have a query called getIdentity, which fetches user identity from cookies and db. The goal is to call this query on the /account page to check the user's identity. If no identity is found, I throw a redirect to /signin. Here's the issue: When I use "use server" in the query, the redirects work perfectly. However, when I try to move the "use server"; to the top of the file (to prevent the identity-fetching logic from being bundled to the client), the redirects stop working. I’d like to keep this logic at the top of the file, but I can’t figure out how to ensure the redirect still works. Any ideas on how to handle this better? Is there a recommended pattern for managing such server-only logic and redirects in Solid Start? Thanks in advance for your help! 🙏
4 Replies
peerreynders
peerreynders2mo ago
I'm having a hard time visualizing what you are changing. But just to get started: there isn't anything server side about query; it's an entirely client side mechanism (SSR aside). So
// This is client side
export const getIdentity = query(async () => {
// This is client side
// …
return value;
}, 'identity');
// This is client side
// This is client side
export const getIdentity = query(async () => {
// This is client side
// …
return value;
}, 'identity');
// This is client side
"use server" acts on the function that is wrapped by query:
// This is client side
export const getIdentity = query(async () => {
"use server"
// This is server side
// …
return value;
}, 'identity');
// This is client side
// This is client side
export const getIdentity = query(async () => {
"use server"
// This is server side
// …
return value;
}, 'identity');
// This is client side
query is still calling a client side function but that function happens to be an RPC and the "use server" section only runs and exists on the server side as an RPC function (which short of a compilation bug should not appear in the client side bundle). Given that I'm unsure what you mean by
I try to move the query to the top of the file
Because I'm just imagining you moving the whole
export const getIdentity = query(async () => {
"use server"
// …
return value;
}, 'identity');
export const getIdentity = query(async () => {
"use server"
// …
return value;
}, 'identity');
block which wouldn't move the redirects inside the "use server" block.
Merdot
MerdotOP2mo ago
I think I incorrectly explained it, I'll fix the initial post, I mean when I put the "use server"; at the top of the file, the redirect does not occur but when I put in in the function itself the redirect occurs. I notice that on the client, the /_server route is called with some headers that point to the wrapped function, but that call does not return a redirect but a 200. ie. Redirect works in SSR and on client interactions but bundle all my private function and it dependencies in the cliente, I don't want that code to land on the client.
// /lib/auth.ts

funtion authenticateService(request) {
// private function with authentication logic, not to be bundled.
// throw redirect may happen here.
}

funtion authenticateUser(request) {
// private function with authentication logic, not to be bundled.
// throw redirect may happen here.
}


export const getIdentity = query(async () => {
"use server"
// the query function gets wrapped in a server reference (RPC)
// call private functions
return value;
}, 'identity');
// /lib/auth.ts

funtion authenticateService(request) {
// private function with authentication logic, not to be bundled.
// throw redirect may happen here.
}

funtion authenticateUser(request) {
// private function with authentication logic, not to be bundled.
// throw redirect may happen here.
}


export const getIdentity = query(async () => {
"use server"
// the query function gets wrapped in a server reference (RPC)
// call private functions
return value;
}, 'identity');
Redirect do not work on SSR and client interaction call /_server and return a 200 no private code is bundled.
// /lib/auth.ts

"use server"

funtion authenticateService(request) {}

funtion authenticateUser(request) {}

// all exported function gets wrappep in a server reference
export const getIdentity = query(async () => {
// call private functions
return value;
}, 'identity');
// /lib/auth.ts

"use server"

funtion authenticateService(request) {}

funtion authenticateUser(request) {}

// all exported function gets wrappep in a server reference
export const getIdentity = query(async () => {
// call private functions
return value;
}, 'identity');
I imagine now that I think more of this, that because I'm marking the entire module as server, the query itself also is being executed in the server when is intended to occur on the client. How do I do redirect in code I want only to run on the server without bundling dependencies of that code to the client. If throw redirects in the route.load/preload functions the system just crash.
peerreynders
peerreynders2mo ago
I notice that on the client, the /_server route is called with some headers that point to the wrapped function, but that call does not return a redirect but a 200.
That is the "use server" RPC request; you won't see an HTTP redirect there. Seroval serializes a redirect result. That is then thrown on the client side so that the router can catch it and initiate the navigation to the specified route.
I don't want that code to land on the client.
The reason they are bundled is because you defined them in the "client space" of the module. To get around that move them to a server-only module (which doesn't need "use server" anywhere) and import them. During the build process the functions with "use server" wrapped by query/action are replaced, making it easy for the bundler to tree shake the unnecessary server imports out. "use server" at the top of the file is generally discouraged even though it can be made to work-but you are correct; when "use server" is at the top there shouldn't be any query/action in the module as those are strictly client-side concepts.
Merdot
MerdotOP2mo ago
This is helpful, thanks for the answers!

Did you find this page helpful?