how to manually invalidate everything in cache in a server function.

I want to send a redirect and invalidate everything
61 Replies
Brendonovich
Brendonovich•5mo ago
Are you not able to make the server function an action?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
It is already an action, but it doesn't seem to invalidate the functions, I can confirm the code works if I remove the cache wrapper from the relevant functions
Brendonovich
Brendonovich•5mo ago
what do your cache and action functions look like?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
Sign in action https://github.com/vanillacode314/kanban/blob/main/src/routes/(auth)/signin.tsx#L14 Relevant route_guard https://github.com/vanillacode314/kanban/blob/main/src/routes/(auth).tsx#L5 The component where the issues is happening, (serverBoards returns Error) https://github.com/vanillacode314/kanban/blob/main/src/routes/(protected)/index.tsx#L14 The get-boards function, if I remove cache from here, it works https://github.com/vanillacode314/kanban/blob/main/src/db/utils/boards.ts#L7 My assumption is that getUser would be defined always because of the routeguard, so I think getBoards cache is the issue
Brendonovich
Brendonovich•5mo ago
ohh you want the cache function to trigger the revalidation i assume because you want the guard to rerun if an auth error is thrown?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
no the auth guard works correct, but once it redirects to / after signin, the auth guard has user != null but in getBoards() there is still Error('Unauthorized') which I think is because of cache since If I refresh the page it works also getBoards() doesn't run on the client again all of this works if I turn off js, but I think that is because it refreshes on form post
Brendonovich
Brendonovich•5mo ago
i think i'd model your guards a bit differently
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
sure can you recommend something
Brendonovich
Brendonovich•5mo ago
stepping back a bit, this redirect won't actually do anything, as redirect is ignored inside load functions
No description
Brendonovich
Brendonovich•5mo ago
you need to depend on load in a createAsync for it to actually have effect
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
the redirect works
const load = cache(async () => {
const user = await getUser();
if (!user) {
return redirect('/test');
}
}, '(protected) load');
const load = cache(async () => {
const user = await getUser();
if (!user) {
return redirect('/test');
}
}, '(protected) load');
this redirects to test
Brendonovich
Brendonovich•5mo ago
ah nvm i think it got changed so that works i think the next thing i'd do is remove the protected guard entirely
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
can I ask the reasoning and the alternative?
Brendonovich
Brendonovich•5mo ago
yeah i'll get into it instead, i'd have an ensureAuthenticated function which getBoards, createBoard, etc can call, which would call getUser and throw the /signin redirect itself that way you're not relying on a separate guard to do the redirect, it's just baked in to your data
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
I'll try that, seems weird coming from prev paradigms though usually auth is middleware afaik
Brendonovich
Brendonovich•5mo ago
exactly, ensureAuthenticated is basically a middleware in this scenario
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
so basically instead of if (!user) return Error() you want me to if (!user) return redirect() ? correct?
Brendonovich
Brendonovich•5mo ago
pretty much
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
Thanks, I'll try that appreciate the help it still has the same issue, now instead of erroring it redirects back to the signin page until refresh
Brendonovich
Brendonovich•5mo ago
yeah imma get there next thing i'd do would be wrap getUser in cache that then becomes your source of truth for the frontend for if a user exists or not
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
I am not sure if you remember me from the other thread yesterday, if I add cache to getUser then it breaks the app till refresh everywhere. Can confirm that is still happening
Brendonovich
Brendonovich•5mo ago
breaks in what way?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
for example if I sign in, it won't redirect till refresh, same with signout etc..., basically getUser won't update till I refresh it sticks to the cache value the action doesn't invalidate it I think
Brendonovich
Brendonovich•5mo ago
it might be because you're not depending on it anywhere yet
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
do layout loads don't consider as depending?
Brendonovich
Brendonovich•5mo ago
nah load/preload is just considered a one off thing realistically your whole app should work without load/preload they're just an optimisation to apply on top
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
got it, coming from svelte I thought they do have deps
Brendonovich
Brendonovich•5mo ago
createAsync and the like do, they're the reactive part preload is just to help you start fetching data as early as possible
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
what is the diff b/w preload and load?
Brendonovich
Brendonovich•5mo ago
load is the deprecated name, they're the same thing preload more accurately conveys that it's an optimization, rather than a dedicated route loader
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
can a route component be async btw?
Brendonovich
Brendonovich•5mo ago
no
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
thank you so much, I think I will refactor the code and see if that helps
Brendonovich
Brendonovich•5mo ago
for (auth) i'd do something like this
import { cache, redirect } from '@solidjs/router';
import { JSXElement } from 'solid-js';
import { getUser } from '~/db/utils/users';

export const route = { load: () => getUser() };

export default function (props: { children: JSXElement }) {
const user = createAsync(() => getUser());
const navigate = useNavigate();

createEffect(() => {
if (user()) navigate("/");
})

return <>{props.children}</>;
}
import { cache, redirect } from '@solidjs/router';
import { JSXElement } from 'solid-js';
import { getUser } from '~/db/utils/users';

export const route = { load: () => getUser() };

export default function (props: { children: JSXElement }) {
const user = createAsync(() => getUser());
const navigate = useNavigate();

createEffect(() => {
if (user()) navigate("/");
})

return <>{props.children}</>;
}
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
redirect doesn't work on client?
Brendonovich
Brendonovich•5mo ago
redirect only works in cache/action functions
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
got it, thanks again 🙂
Brendonovich
Brendonovich•5mo ago
the idea with this is that it depends on getUser, rather than another cache function, so if it's invalidated it should hopefully refetch if necessary i'm not 100% sure if it'll work but i think it's a better design to go off of
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
I'll report back if it works or nto createRenderEffect works here and not createEffect. I assume that is because effect runs after children are run?
Brendonovich
Brendonovich•5mo ago
what do you mean by 'works'? createEffect usually just runs later than createRenderEffect since it won't run until any suspense boundaries above it resolve
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
for anyone struggling in the future with the same issue, the createEffect stuff also didn't work, in the end I just wrote a middleware and attached the user to event.locals.user and that worked also I had to remove cache from getUser
Brendonovich
Brendonovich•5mo ago
Did the effect just not run or something?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
actually the issue is something else, see this post: https://discord.com/channels/722131463138705510/1271832167588036650 even the middleware didn't work, it was a fluke the main issue is forwarding headers from client I think I would create a fetch wrapper but obv. I cannot control how server functions are called
Brendonovich
Brendonovich•5mo ago
You’re reading cookies from getRequestEvent right?
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
yes and passing the nativeevent to getCookies
Brendonovich
Brendonovich•5mo ago
Server functions are special in that you usually don’t have to pass through cookies Since they can use the same request data as the ssr invocation
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
do nativeEvent also has that quality?
Brendonovich
Brendonovich•5mo ago
Yeah You don’t even have to pass the event to getCookies It can pull it from the request lifecycle on its own
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
const event = getRequestEvent()!;
const user = event.locals.user;
const event = getRequestEvent()!;
const user = event.locals.user;
if this is in a server function, even if it's called on ssr in server, it should have the locals set from middleware? it doesn't have the headers as you mention
Brendonovich
Brendonovich•5mo ago
If you’re setting to locals in middleware then yeah it should be present in both components and server functions
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
let's say if I have a action signin, it calls setCookie and redirects, now I assume this would trigger ssr and call server functions in server, would the server function get the cookie then? the main issue happens to me on signin and signout otherwise everything works great
Brendonovich
Brendonovich•5mo ago
This won’t ssr, it’ll client navigate and call the server functions The cookie should be included tho The cookie not being present might be related to something else though As that action will be a single flight mutation which will call getUser after completing the action, and the cookie might only be set on the response at that point in time, not the request
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
let me send some logs
DEBUGPRINT[2]: middleware.ts:9: token= undefined
DEBUGPRINT[1]: signin.tsx:16 (after use server;)
DEBUGPRINT[1]: boards.ts:11: cookie= null
DEBUGPRINT[2]: middleware.ts:9: token= undefined
DEBUGPRINT[1]: signin.tsx:16 (after use server;)
DEBUGPRINT[1]: boards.ts:11: cookie= null
this is the chain of events on login this is the server logs to be clear the middleware isn't call after signin shouldn't it be called before boards.ts as it's a severfunction?
Brendonovich
Brendonovich•5mo ago
Ok yeah i think signIn is a single flight mutation here What Start is doing is performing the signIn action, and then because it knows that the route you're redirecting to needs getBoards it's fetching that before returning the action response So it's doing the action and data fetch in the same request
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
any fixes?
Brendonovich
Brendonovich•5mo ago
1. don't do auth in middleware 😅
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
the createEffect stuff didn't work too but I'll go back to that commit and get some debug logs as well
Brendonovich
Brendonovich•5mo ago
but the cookie not being present in getBoards seems to be a limitation of the single flight mutations implementation at the moment It might be necessary for response headers from the action to be set on the request before executing single flight data fetches
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
yup, maybe setCookie invocatoin can set a flag or something
Brendonovich
Brendonovich•5mo ago
You can also set singleFlight={false} on <Router> to disable SFM for now So the data fetch would be done in a second request by the client
Raqueebuddin Aziz
Raqueebuddin AzizOP•5mo ago
thanks for the info, appreciate it 🙂 That worked, thanks, this was baffling me for 2 days straight
Want results from more Discord servers?
Add your server