-
Notifications
You must be signed in to change notification settings - Fork 289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Data Fetching Guide #991
base: main
Are you sure you want to change the base?
Changes from 23 commits
935a674
d51d873
bbf5def
a73bf29
4b26cf6
9e34c58
ab07dd6
554c737
d41c75c
e444f1f
515c658
c8e62dd
96de833
9b12076
3591bf0
99a1797
fb1470c
49a0224
99ef369
4de7347
f6a6733
96b62f7
093fc06
9dd327d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
"index.mdx", | ||
"getting-started.mdx", | ||
"building-your-application", | ||
"advanced" | ||
"advanced", | ||
"guides" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
--- | ||
title: "Data Fetching" | ||
--- | ||
|
||
This guide will walk you through the basics of data fetching in SolidStart, providing practical examples and best practices. | ||
|
||
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/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`](/solid-router/reference/data-apis/query) is created. In order to access it's data within the component, the [`createAsync`](/solid-router/reference/data-apis/create-async) primitive was used. | ||
|
||
## Showing loading UI | ||
|
||
To display a loading UI while data is being fetched, you can use [Solid `<Suspense>`](/reference/components/suspense): | ||
|
||
1. Import `Suspense` from `solid-js` | ||
2. Wrap the data rendering in `<Suspense>` | ||
3. Provide a fallback component that will be displayed while the data is loading using the `fallback` prop | ||
|
||
```tsx {13} {15} 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/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 | ||
|
||
If the data fetching fails, a fallback UI can be displayed using an [`<ErrorBoundary>`](/reference/components/error-boundary): | ||
|
||
1. Import `ErrorBoundary` from `solid-js`. | ||
2. Wrap the data rendering in `<ErrorBoundary>` | ||
3. Provide a fallback component that will be displayed if an error occurs using the `fallback` prop. | ||
|
||
```tsx {13} {17} title="src/routes/index.tsx" | ||
import { 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 with the [`preload`](/solid-router/reference/preload-functions/preload) function: | ||
|
||
1. Export a `route` object with a `preload` function | ||
2. Run your query inside the `preload` function | ||
3. Use the query as usual in your component | ||
|
||
```tsx {9-11} title="src/routes/index.tsx" | ||
import { 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> | ||
); | ||
} | ||
``` | ||
|
||
## Passing parameters to queries | ||
|
||
When creating a query that accepts parameters, you can define your query function to take any number of arguments: | ||
|
||
```tsx {9-10} {15} {19-20} title="src/routes/posts/[id]/index.tsx" | ||
import { ErrorBoundary } from "solid-js"; | ||
import { | ||
query, | ||
createAsync, | ||
useParams, | ||
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 params = useParams(); | ||
const post = createAsync(() => getPost(params.id)); | ||
return ( | ||
<div> | ||
<ErrorBoundary fallback={<div>Something went wrong!</div>}> | ||
<h1>{post().title}</h1> | ||
</ErrorBoundary> | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
In this example, [`useParams`](/solid-router/reference/primitives/use-params) was used to get the `id` path parameter. You can then pass it to the `getPost` query to get the data for that specific post. | ||
|
||
## Using a database or an ORM | ||
|
||
To safely interact with your database or ORM in a query, ensure the query is server-only to prevent exposing your database credentials to the client. You can do this by adding [`"use server"`](/solid-start/reference/server/use-server) as the first line of your query function: | ||
|
||
```tsx {6-7} title="src/routes/index.tsx" | ||
import { 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> | ||
); | ||
} | ||
``` | ||
|
||
## Fetching data on the client | ||
|
||
When you only need to fetch data on the client side, you can use the [`createResource`](/reference/basic-reactivity/create-resource) primitive: | ||
|
||
```tsx {4-7} 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/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> | ||
); | ||
} | ||
``` | ||
|
||
You can [read more about `createResource` here](/reference/basic-reactivity/create-resource). | ||
Comment on lines
+192
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Nevermind, I just noticed that most of your There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the diataxis guide that we use as reference:
Keeping each section action-oriented, focusing on a practical action that the user might want to do offers better value to the user. If the user wants to learn about a specific tool, it can go to the API Reference. I believe the documentation should include a section discussing the pros and cons of using Solid.js versus Solid Router APIs for data fetching. Additionally, it would be helpful to clarify why some APIs are part of Solid Router while others belong to Solid.js. However, this page is not aimed at that type of content. Perhaps a tutorial on data fetching would be a more appropriate format for this discussion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my initial draft, I included a call-out that gave a little hint:
This may slightly disrupt the page's theme, but I think reintroducing a call-out like this could significantly help users navigate the material with greater clarity. At least until we add a comprehensive tutorial. |
||
|
||
<Callout type="info" title="Advanced Data Handling"> | ||
For advanced features like automatic background re-fetching or infinite queries, you can use [Tanstack Query.](https://tanstack.com/query/latest/docs/framework/solid/overview) | ||
</Callout> | ||
Comment on lines
+216
to
+218
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the callout, however I still think it would be helpful to show an example snippet here that shows using tanstack query with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I almost wonder if we need a tutorial geared towards advanced data fetching. Like something that goes into the weeds of the more complicated parts people may run into? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think showcasing the usage of Tanstack Query with
It's too much for this page. I think it's better to leave it to the user to go through the Tanstack Query docs and figure things out. If there's any ambiguity regarding the use of |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"title": "Guides", | ||
"pages": ["data-fetching.mdx"] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we explain why preloading is even needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be more worthwhile having that in a the other aspects of the docs (like the core parts of "learn" or a "tutorial". This is geared more towards people asking "how to" instead of "why" and i worry if we add the "why" here, it'd be assumed the rest of it should have it as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that makes sense, we probably need a separate document for explaining the concepts and issues around data fetching + mutation