Skip to content

Commit

Permalink
First draft
Browse files Browse the repository at this point in the history
  • Loading branch information
amirhhashemi committed Dec 21, 2024
1 parent d2978b5 commit 935a674
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/routes/solid-start/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"index.mdx",
"getting-started.mdx",
"building-your-application",
"advanced"
"advanced",
"guides"
]
}
266 changes: 266 additions & 0 deletions src/routes/solid-start/guides/data-fetching.mdx
Original file line number Diff line number Diff line change
@@ -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 (
<ul>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ul>
);
}
```

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).


<Callout type="info">
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.
</Callout>

## 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 (
<ul>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ul>
);
}
```

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 (
<ul>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ul>
);
}
```

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 <div>{post().title}</div>;
}
```

### Loading UI

You can use [Solid `<Suspense>`](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 (
<ul>
{/* Show a fallback while the data is fetching. */}
<Suspense fallback={<div>Loading...</div>}>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</Suspense>
</ul>
);
}
```

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 `<ErrorBoundary>`](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 (
<ul>
{/* Show a fallback while the data is fetching. */}
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ErrorBoundary>
</ul>
);
}
```

### 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 (
<ul>
{/* Optional: show a fallback while the data is fetching. */}
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
{/* Optional: Show a fallback while the data is fetching. */}
<Suspense fallback={<div>Loading...</div>}>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</Suspense>
</ErrorBoundary>
</ul>
);
}
```

You can use [`<Suspense>`](https://docs.solidjs.com/reference/components/suspense) and [`<ErrorBoundary>`](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`.

<Callout type="caution">
`query` should not be used with `useResource`.
</Callout>


## 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 (
<div>
{/* Optional: show a fallback while the data is fetching. */}
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
{/* Optional: Show a fallback while the data is fetching. */}
<Suspense fallback={<div>Loading...</div>}>
<div>{postQuery.data?.name}</div>
</Suspense>
</ErrorBoundary>
</div>
);
}
```
4 changes: 4 additions & 0 deletions src/routes/solid-start/guides/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Guides",
"pages": ["data-fetching.mdx"]
}

0 comments on commit 935a674

Please sign in to comment.