How to refetch a "use server" function / RPC?
I've created a server function that I can confirm works as expected, now I'm trying to figure out how to "correctly" re-run this function on a timed interval.
Using the documentation (https://start.solidjs.com/core-concepts/data-loading), it first mentions
createResource()
for requests, which makes sense since it comes with a mutate and refetch function. Then it goes on to give a "Data loading always on the server" example, now moving to createAsync
and cache
(both /router imports), but from my attempts to use those examples, they work for initial page load but do not appear to contain mechanisms for re-running the server function and updating the UI accordingly. Additionally, the example listed above doesn't seem to actually run anything async (there's no await
in that async server function), so I'm wondering if maybe the example is still missing something (like when is the route
object used?! Is it a magic object / used somehow when exported from a page? Not listed in API Routes tab).
Am I missing something?
Thanks!
(Code examples in thread)SolidStart Beta Documentation
SolidStart Beta Documentation
Early release documentation and resources for SolidStart Beta
63 Replies
Initial example using the createAsync example from the docs:
My attempt to use
createResource
instead, running into weird problems, see comments:
Invalidating
cachedPing
will cause the cache to refetch and run the server functionHow do I do that?
Is there any more documentation for
cache
and createAsync
?Check the docs:
You can revalidate the cache using the revalidate method or you can set revalidate keys on your response from your actions. If you pass the whole key it will invalidate all the entries for the cache (ie "users" in the example above). You can also invalidate a single entry by using keyFor.So import
revalidate
from solid-router and give it a cache keyI see, I was wondering where to find these kinds of specs. Linking them from the start.solidjs.com docs would probably be helpful. (Github readme's arent usually very high on the SEO)
Probably, but they're not really anything to do with solid-start
Any reason why I'm getting the errors shown in the 2nd example? Based purely on the documentation for the associated calls (and even seeing the RCP run correctly before solid crashes), it looks like it should work
Have you tried
createResource(() => pingServers())
?also be careful on setTimeout, that thing runs on the server
I tried wrapping it with a full function that basically did the same thing:
Though on a second look, your example was straight calling pingServers synchronously. I can try that
Noted. Since I've confirmed that the
invalidate()
approach works, I've since turned it to a 1 minute interval that then invalidates every 15 minutes based on a "last fetched" Date.now() associated with the response.
I assumed that this was client side, hence the interval starting at any point in the ideal "don't invalidate cache until 15 minutes has passed", but if this is running server-side too... does it create a new interval each time the page is refreshed?!it creates an interval every request. Better call this in a createEffect
and also make sure to clean it up
Cleanup already done
but I'll do the createeffect for clientside. Thanks
Hmm... still seeing some odd behavior. RPC is run twice on each refresh (presumably once for SSR, once for client?), and the cache doesn't appear to be saved between page reloads. Is this an artifict of Dev mode (and I should test with
npm run start
instead of npm run dev
, or something else not working as I would think it should?
derp, found the source of the second rpc... my fault for uncommenting the createResource when trying to test @Brendonovich 's ideait's possible for it to run at least twice because of the Suspense mechanism
so you might want to wrap the return of Home with a Suspense
Done, but that isn't changing the fact that it's reloading the RCP on each page reload (not caching like I would have assumed).
Reading the cache docs now.
By 'reload' do you mean refreshing the page or calling
reload()
?refreshing the page
Ah
cache
is just for the current sessiongotcha
it's not a persistent cache
global server state stuff... I think i saw something about how that's not recommended
it's not a big deal, this is mostly only running at home
From the docs:
This cache accomplishes the following: It does just deduping on the server for the lifetime of the request. It does preload cache in the browser which lasts 10 seconds. When a route is preloaded on hover or when load is called when entering a route it will make sure to dedupe calls. We have a reactive refetch mechanism based on key. So we can tell routes that aren't new to retrigger on action revalidation. It will serve as a back/forward cache for browser navigation up to 5 mins. Any user based navigation or link click bypasses it. Revalidation or new fetch updates the cache.
if it was going to scale to 100's or 1000s of users, I'd def need a more performant mechanism, but that would also probably mean a Redis cache somewhere in there.
I doubt 1000s of users would require a server-side cache, but even then it's got nothing to do with the purpose of solid router's cache
1000s of users all triggering tcp pings to check server status? I could see that being an issue.
If that's all they're doing I think you may underestimate how powerful computers are 😅
perhaps
I'm maybe overarchitecting too
thansk for the help
One machine can power tens or hundreds of thousands of connections (depending on what you build with), just focus on making something that works
My comments about the SolidStart docs still stand though. The Server Only example is confusing.
Which one was that?
Oh also does
createResource(() => pingServers())
workLink in OP
(Checking)
No. Wait... yes, but refetch causes a flash / reload of the page (scrolls back to top). (Maybe a side effect of Suspense? Going to try that now)
const [status, { mutate, refetch}] = createResource(() => pingServers())
Not caused by suspenseI ask bc my guess with this one is that
pingServers
was being called with arguments from the createResource
which may have been causing a problempingServers doesn't take arguments though
so sending some wouldn't do anything
(shouldn't)
Well yeah idk if it will or not but I do know that createResource will give it arguments
Like I said, I had tried
But this caused the same error as sending PingServers directly. (
createResource(pingServers)
)
The main difference in your suggestion was not having an async function but fundamentally returning the promise instead of the result to createResource
which might explain why it worked, but also it's creating a new promise every time (potentially causing the flash?)Well yours is also creating a new promise each time
if it's the entire page, have you followed my Suspense suggestion?
if you mean wrap the
return(<main>...</main>)
with a <Suspence>
tag like return(<Suspence><main>...</main></Suspence>
), yes I did (had already added it when I tried @Brendonovich's suggestion, then also tried without)
I suppose that's true, but doesn't explain why an async/await function wrapper (or sending the server async function directly) causes the stack error, but a function that isn't async works...
Continuing this thread instead of creating a new one:
I'm trying to pipe the results of the createAsync
into a Context provider so I don't have to prop-drill it. So far I am only getting undefined for the result of the contextProvider, even though the
My other failed attempts were:
* export the result of the cache and import in another file, using a second createAsync
: export const cachedPing = cache(pingServers, "mycache")
* move the createAsync
out of the component function and export / import that instead
Any tips?from the docs:
The value passed to provider is passed to useContext as is. That means wrapping as a reactive expression will not work. You should pass in Signals and Stores directly instead of accessing them in the JSX.
I also tried passing the accessor function itself (
status
) instead of the value (status()
), and am getting the same result
I need to create an effect to update a signal every time the createAsync changes?you'll need to do so either way
I guess I assumed that a createAsync would be a signal
yea it is
Ok, went through the rest of the docs.
If the result of createAsync is a signal, theoretically it should have worked to move that out of the component definition, allowing me to export and import it, no?
(removing the need for Context?)
technically yeah
if you're using ssr you might get into some trouble but other than that should be fine
would encourage trying to pass the whole signal down through context again tho
I get a "pingStatus is not defined" error
when exporting
export const pingStatus = createAsync(cachedPing)
in my routes/index.tsx, and importing in my Component filehmm might be because you're exporting it from a route file
Should I swap, export in the component, import in the route?
hmm
that didn't work either
Cannot use 'in' operator to search for 'data' in undefined
in the SolidStart server.js filesurely there's one of your own files in that stack trace
oh sure, that's just the last point of failure
i find it's usually more useful to look at where errors were thrown in my own files rather than the internals
I'd copy the stack trace but it blows my mind that there's CSS styles prevent copying the text
copy from cli?
ah nah it's client side
output to the browser is different
you're doing
import { pingStatus } from "..."
yeah?yep
¯\_(ツ)_/¯
Joy
at this point... I'm about to just setup a separate API definition instead XD
I feel like that would be easier
which is unfortunate, as the whole point of going the RPC route was to prevent that
lol. I tried one last thing, moving the cache definition to the same node file the RPC call is defined in, and NOW it works XD
¯\_(ツ)_/¯
maybe it was one of those recursive dependency problems (though I don't see why it would have been)
ah, but of course now I'm getting errors saying that computations created outside a "createRoot" or "render" will never be disposed... because it's outside the Solid lifecycle 🤦♂️
seems like createAsync needs to be within the lifecycle of a component, is my guess
yep, confirmed
yes, createResource has that requirement, thanks to createUniqueId
createResource or createAsync?
createResource
, which createAsync
is built fromAh
Wasn't clear from the docs I read. I feel like if I'm going to learn Solid proper, I need to be in the source