diff --git a/src/routes/solid-start/data.json b/src/routes/solid-start/data.json index 5adb2bd086..aac47ac2cb 100644 --- a/src/routes/solid-start/data.json +++ b/src/routes/solid-start/data.json @@ -4,6 +4,7 @@ "index.mdx", "getting-started.mdx", "building-your-application", - "advanced" + "advanced", + "guides" ] } diff --git a/src/routes/solid-start/guides/data-fetching.mdx b/src/routes/solid-start/guides/data-fetching.mdx new file mode 100644 index 0000000000..8fcf7aae2d --- /dev/null +++ b/src/routes/solid-start/guides/data-fetching.mdx @@ -0,0 +1,266 @@ +--- +title: "Data Fetching" +--- + +This guide will walk you through the basics of data fetching in SolidStart, providing practical examples and best practices. + +## Basics + +Here's a minimal example of data fetching in SolidStart: + +```tsx title="src/routes/index.tsx" +import { For } from "solid-js"; +import { query, createAsync } from "@solidjs/router"; + +const getPosts = query(async () => { + const posts = await fetch("https://my-api.com/blog"); + return await posts.json(); +}, "posts"); + +export default function Page() { + const posts = createAsync(() => getPosts()); + return ( + + ); +} +``` + +This example demonstrates a basic data fetching using [`query`](https://docs.solidjs.com/solid-router/reference/data-apis/query) and [`createAsync`](https://docs.solidjs.com/solid-router/reference/data-apis/create-async). + + + + Note that `query` and `createAsync` are imported from `@solidjs/router`. By default, SolidStart uses [Solid Router](https://docs.solidjs.com/solid-router) under the hood. That means we can take advantage of all Solid Router data fetching primitives in SolidStart. + + +## Reference + +- Solid Router [`query`](https://docs.solidjs.com/solid-router/reference/data-apis/query) +- Solid Router [`createAsync`](https://docs.solidjs.com/solid-router/reference/data-apis/create-async) +- Solid [`createResource`](https://docs.solidjs.com/reference/basic-reactivity/create-resource) + +## Examples + +### Preloading Data + +You can use the [`preload`](https://docs.solidjs.com/solid-router/reference/preload-functions/preload) function to preload a query: + +```tsx title="src/routes/index.tsx" +import { For } from "solid-js"; +import { query, createAsync, type RouteDefinition } from "@solidjs/router"; + +const getPosts = query(async () => { + const posts = await fetch("https://my-api.com/blog"); + return await posts.json(); +}, "posts"); + +export const route = { + preload: () => getPosts(), +} satisfies RouteDefinition; + +export default function Page() { + const posts = createAsync(() => getPosts()); + return ( + + ); +} +``` + +In this example, we call `getPosts` inside the `preload` function, and because we have used `query` the result is cached and becomes immediately available in the component. + +The `preload` function runs in parallel with route loading. This happens when: + +- A user visits the route for the first time +- A user hovers over a link to the route + +### Server-Only Queries + +By default, queries can run on both the server and client. Add [`"use server"`](https://docs.solidjs.com/solid-start/reference/server/use-server) to make a query run only on the server: + +```tsx {6} title="src/routes/index.tsx" +import { For } from "solid-js"; +import { query, createAsync, type RouteDefinition } from "@solidjs/router"; +import { db } from "~/lib/db"; + +const getPosts = query(async () => { + "use server"; + const posts = await db.from("posts").select(); + return await posts.json(); +}, "posts"); + +export const route = { + preload: () => getPosts(), +} satisfies RouteDefinition; + +export default function Page() { + const posts = createAsync(() => getPosts()); + return ( + + ); +} +``` + +Because `getPosts` only runs on the server, it's safe to access your database directly. + +### Parameterized Queries + +Here's how to create queries that accept parameters: + +```tsx title="src/routes/posts/[id]/index.tsx" +import { + query, + createAsync, + useParams, + type RouteDefinition, +} from "@solidjs/router"; + +const getPost = query(async (id: string) => { + const post = await fetch(`https://my-api.com/blog/posts/${id}`); + return await post.json(); +}, "post"); + +export const route = { + preload: ({ params }) => getPost(params.id), +} satisfies RouteDefinition; + +export default function Page() { + const params = useParams(); + const post = createAsync(() => getPost(params.id)); + return
{post().title}
; +} +``` + +### Loading UI + +You can use [Solid ``](https://docs.solidjs.com/reference/components/suspense) to show an instant loading state while SolidStart streams in the result. + +```tsx title="src/routes/index.tsx" +import { Suspense, For } from "solid-js"; +import { query, createAsync } from "@solidjs/router"; + +const getPosts = query(async () => { + const posts = await fetch("https://my-api.com/blog"); + return await posts.json(); +}, "posts"); + +export default function Page() { + const posts = createAsync(() => getPosts()); + return ( + + ); +} +``` + +This will prevent the whole component from being blocked by data requests, and the user will be able to interact with the parts of the page that are ready. + +### Error Handling + +You can use [Solid ``](https://docs.solidjs.com/reference/components/error-boundary) to show a fallback in case data fetching fails. + +```tsx title="src/routes/index.tsx" +import { ErrorBoundary, For } from "solid-js"; +import { query, createAsync, type RouteDefinition } from "@solidjs/router"; + +const getPosts = query(async () => { + const posts = await fetch("https://my-api.com/blog"); + return await posts.json(); +}, "posts"); + +export const route = { + preload: () => getPosts(), +} satisfies RouteDefinition; + +export default function Page() { + const posts = createAsync(() => getPosts()); + return ( + + ); +} +``` + +### Client-Side Data Fetching + +While `createAsync` is the recommended way to fetch data, sometimes you need client-side only fetching. For these cases, use the [`createResource`](https://docs.solidjs.com/reference/basic-reactivity/create-resource) primitive: + +```tsx title="src/routes/index.tsx" +import { createResource, ErrorBoundary, Suspense, For } from "solid-js"; + +export default function Page() { + const [posts] = createResource(async () => { + const posts = await fetch("https://my-api.com/blog"); + return await posts.json(); + }); + + return ( + + ); +} +``` + +You can use [``](https://docs.solidjs.com/reference/components/suspense) and [``](https://docs.solidjs.com/reference/components/error-boundary) for showing loading UI and handling errors. + +Be aware that you should not use `query` with `useResource`. + + + `query` should not be used with `useResource`. + + + +## Using Tanstack Query + +Consider [Tanstack Query](https://tanstack.com/query/latest) if you need advanced features like automatic background refetching or infinite queries. It's particularly appealing if you're already familiar with it from other projects. However, keep in mind that it will increase your bundle size slightly. + +Here is a basic example of using Tanstack Query: + +```tsx title="src/routes/posts/[id]/index.tsx" +import { ErrorBoundary, Suspense } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { createQuery } from "@tanstack/solid-query"; + +export default function Page() { + const params = useParams(); + const postQuery = createQuery(() => ({ + queryKey: ["posts", params.id], + queryFn: async (id: string) => { + const post = await fetch(`https://my-api.com/posts/${id}`); + return await post.json(); + }, + throwOnError: true, + })); + + return ( +
+ {/* Optional: show a fallback while the data is fetching. */} + Something went wrong!
}> + {/* Optional: Show a fallback while the data is fetching. */} + Loading...}> +
{postQuery.data?.name}
+
+
+ + ); +} +``` diff --git a/src/routes/solid-start/guides/data.json b/src/routes/solid-start/guides/data.json new file mode 100644 index 0000000000..109b58e92c --- /dev/null +++ b/src/routes/solid-start/guides/data.json @@ -0,0 +1,4 @@ +{ + "title": "Guides", + "pages": ["data-fetching.mdx"] +}