Data loading
Edit this pageSolidStart aims to make it easy to load data from your data sources to keep your UI updated with your data. For most of your data requirements, routes will likely be used to decide what data to load. SolidStart includes nested routing to help structure your application's UI in a hierarchical way, so that you can share layouts.
Data loading on the client
Solid provides a way to load data from your data sources using the createResource
primitive.
It takes an async function and returns a signal from it.
createResource
integrates with Suspense
and ErrorBoundary
to help manage lifecycle and error states.
import { For, createResource } from "solid-js";
type Users = { name: string; house: string };
export default function Page() { const [users] = createResource(async () => { const response = await fetch("https://example.com/users"); return (await response.json()) as User[]; });
return <ul>{users() && users()!.map((user) => <li>{user.name}</li>)}</ul>;}
When fetching inside components, you can encounter unnecessary waterfalls, especially when nested under lazy loaded sections. To solve this, it can be valuable to introduce a hoist and cache mechanism.
Using a library like Tanstack Query can help with this.
for the example below we will be using the data in APIs in solid-router
Using some of the features of solid-router
, we can create a cache for our data:
import { For } from "solid-js";import { createAsync, cache } from "@solidjs/router";
type User = { name: string; email: string };
const getUsers = cache(async () => { const response = await fetch("https://example.com/users"); return (await response.json()) as User[];}, "users");
export const route = { load: () => getUsers(),};
export default function Page() { const users = createAsync(() => getUsers());
return <ul>{users() && users()!.map((user) => <li>{user.name}</li>)}</ul>;}
With this method, however, there are some caveats to be aware of:
- The
load
function is called once per route, which is the first time the user comes to that route. Following that, the fine-grained resources that remain alive synchronize with state/url changes to refetch data when needed. If the data needs a refresh, therefetch
function returned in thecreateResource
can be used. - Before the route is rendered, the
load
function is called. It does not share the samecontext
as the route. The context tree that is exposed to theload
function is anything above theRoutes
component. - On both the server and the client, the
load
function is called. The resources can avoid refetching if they serialized their data in the server render. The server-side render will only wait for the resources to fetch and serialize if the resource signals are accessed under aSuspense
boundary.
Data loading always on the server
An advantage of being a full-stack JavaScript framework is that it is easy to write data loading code that can run both on the server and client.
SolidStart offers a way to do that and more.
Through the "use server"
comment you can tell the bundler to create an RPC and not include the code in the clients bundle.
This lets you write code that only runs on the server without needing to create an API route for it.
For example, it could be database access or internal APIs, or when you sit within your function and need to use your server.
import { For } from "solid-js";import { createAsync, cache } from "@solidjs/router";
type User = { name: string; email: string };
const getUsers = cache(async () => { "use server"; return store.users.list();}, "users");
export const route = { load: () => getUsers(),};
export default function Page() { const users = createAsync(() => getUsers());
return <ul>{users() && users()!.map((user) => <li>{user.name}</li>)}</ul>;}