Data fetching
Edit this pageSolidStart is built on top of Solid and uses Solid Router by default. This means you can leverage their respective data-fetching primitives within SolidStart. Since SolidStart itself provides minimal data-fetching APIs, most functionality comes from Solid and Solid Router.
This guide provides practical examples of common data-fetching tasks using these primitives.
For detailed API information, refer to the Solid and Solid Router documentation.
Here's a simple example:
// src/routes/index.tsximport { For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </ul> );}
// src/routes/index.jsximport { For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </ul> );}
In this example, a query
is created.
In order to access it's data within the component, the createAsync
primitive was used.
Showing loading UI
To show a loading UI during data-fetching:
- Import
Suspense
fromsolid-js
. - Wrap your data rendering in
<Suspense>
, and use thefallback
prop to show a component during data fetching.
// src/routes/index.tsximport { Suspense, For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ul> );}
// src/routes/index.jsximport { Suspense, For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ul> );}
Handling errors
To show a fallback UI if the data-fetching fails:
- Import
ErrorBoundary
fromsolid-js
. - Wrap the data rendering in
<ErrorBoundary>
, and use thefallback
prop to show a component if an error occurs.
// src/routes/index.tsximport { ErrorBoundary, Suspense, For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ErrorBoundary> </ul> );}
// src/routes/index.jsximport { ErrorBoundary, Suspense, For } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ErrorBoundary> </ul> );}
Preloading data
Data fetching can be optimized during user navigation by preloading the data:
- Export a
route
object with apreload
function. - Run your query inside the
preload
function. - Use the query as usual in your component.
// src/routes/index.tsximport { ErrorBoundary } from "solid-js";import { query, createAsync, type RouteDefinition } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export const route = { preload: () => getPosts(),} satisfies RouteDefinition;
export default function Page() { const post = createAsync(() => getPosts()); return ( <div> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <h1>{post().title}</h1> </ErrorBoundary> </div> );}
// src/routes/index.jsximport { ErrorBoundary } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPosts = query(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json();}, "posts");
export const route = { preload: () => getPosts(),};
export default function Page() { const post = createAsync(() => getPosts()); return ( <div> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <h1>{post().title}</h1> </ErrorBoundary> </div> );}
Passing parameters to queries
When creating a query that accepts parameters, define your query function to take any number of arguments:
// src/routes/posts/[id]/index.tsximport { ErrorBoundary } from "solid-js";import { query, createAsync, type RouteDefinition } from "@solidjs/router";
const getPost = query(async (id: string) => { const post = await fetch(`https://my-api.com/posts/${id}`); return await post.json();}, "post");
export const route = { preload: ({ params }) => getPost(params.id),} satisfies RouteDefinition;
export default function Page() { const postId = 1; const post = createAsync(() => getPost(postId)); return ( <div> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <h1>{post().title}</h1> </ErrorBoundary> </div> );}
// src/routes/posts/[id]/index.jsximport { ErrorBoundary } from "solid-js";import { query, createAsync } from "@solidjs/router";
const getPost = query(async (id) => { const post = await fetch(`https://my-api.com/posts/${id}`); return await post.json();}, "post");
export const route = { preload: ({ params }) => getPost(params.id),};
export default function Page() { const postId = 1; const post = createAsync(() => getPost(postId)); return ( <div> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <h1>{post().title}</h1> </ErrorBoundary> </div> );}
Using a database or an ORM
To safely interact with your database or ORM in a query, ensure it's server-only by adding "use server"
as the first line of your query:
// src/routes/index.tsximport { For, ErrorBoundary } from "solid-js";import { query, createAsync, type RouteDefinition } from "@solidjs/router";import { db } from "~/lib/db";
const getPosts = query(async () => { "use server"; return await db.from("posts").select();}, "posts");
export const route = { preload: () => getPosts(),} satisfies RouteDefinition;
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </ErrorBoundary> </ul> );}
// src/routes/index.jsximport { For, ErrorBoundary } from "solid-js";import { query, createAsync } from "@solidjs/router";import { db } from "~/lib/db";
const getPosts = query(async () => { "use server"; return await db.from("posts").select();}, "posts");
export const route = { preload: () => getPosts(),};
export default function Page() { const posts = createAsync(() => getPosts()); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </ErrorBoundary> </ul> );}
Fetching data on the client
To fetch data only on the client, use the createResource
primitive:
// src/routes/index.tsximport { createResource, ErrorBoundary, Suspense, For } from "solid-js";
export default function Page() { const [posts] = createResource(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json(); }); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ErrorBoundary> </ul> );}
// src/routes/index.jsximport { createResource, ErrorBoundary, Suspense, For } from "solid-js";
export default function Page() { const [posts] = createResource(async () => { const posts = await fetch("https://my-api.com/posts"); return await posts.json(); }); return ( <ul> <ErrorBoundary fallback={<div>Something went wrong!</div>}> <Suspense fallback={<div>Loading...</div>}> <For each={posts()}>{(post) => <li>{post.title}</li>}</For> </Suspense> </ErrorBoundary> </ul> );}
See the createResource
API reference for more information.
For advanced features like automatic background re-fetching or infinite queries, you can use Tanstack Query.