Solid's Resource is great!
Tags: solidjs, webdev, javascript
Published: 2024-05-07 09:21:52

When you say Solid, Signal usually comes to mind, but there are many more great APIs there. One of them is [Resource](https://www.solidjs.com/tutorial/async_resources):

```js
const fetchPosts = () => 
  fetch(`https://jsonplaceholder.typicode.com/posts`)
    .then(r => r.json());
};

const App = () => {
  const [posts] = createResource(fetchPosts);

  return <span>Number of posts: {posts()?.length}</span>;
}
```

As you can see, Resource basically does 2 things:
- Manages an async operation, like a fetch
- Binds the result of that operation to Solid's reactive model

Therefore, it makes an async operation appear synchronous. See? I haven't used `await` anywhere in the App component.

You can also connect the Resource to a Signal. Every time the latter changes, Resource performs the async operation again.

```js
const fetchPost = async (id) => (
    await fetch(`https://jsonplaceholder.typicode.com/posts/${id}/`)
  ).json();

const App = () => {
  const [postId, setPostId] = createSignal();
  const [post] = createResource(postId, fetchPost);

  function onOpenPost(id) {
    setPostId(id);
  }
```

Pretty neat. However, sometimes you need a bit more control over the encapsulated data, and that's where `refetch` and `mutate` come in.

```js
const App = () => {
  const [postId, setPostId] = createSignal();

  const [post, { mutate }] = createResource(postId, fetchPost);
  const [posts, { refetch }] = createResource(fetchPosts);

  async function onCreatePost() {
    const newPost = await createPostInDB();
    refetch();
  }

  async function updatePost(updatedPost) {
    await updatePostInDB(updatedPost);
    mutate(oldPost => ({ ...oldPost, ...updatedPost });
  }
```

As their names tell, `refetch` makes the Resource, in our case, call the backend API again. This can be handy anytime it's more consistent to get fresh new data from the backend rather than modify it in place on the frontend.
`mutate` on the other hand lets you modify the data in place without calling backend API. The one thing I'd love and I am missing at the moment would be to have the same capabilities to mutate the Resource as with Solid [Stores](https://docs.solidjs.com/concepts/stores#modifying-store-values) as that would make the mutations much easier to perform.

To wrap the nice features up, there are `loading` and `error` properties on the Signal produced by Resource which make it super easy to track the fetch's status:

```js
const App = () => {
  const [postId, setPostId] = createSignal();
  const [post] = createResource(postId, fetchPost);
  
  return <span>{post.loading && 'Loading...'}</span>
```

There are more interesting Resource options and properties like `state` which you can find in the [docs](https://docs.solidjs.com/reference/basic-reactivity/create-resource).

## Alternatives
When I was listing all those advantages of Solid's Resource, I bet many of you thought Tanstack Query. And they are indeed similar in many ways, although the latter is much more powerful. That's why it's so nice there is an official [distribution for Solid](https://tanstack.com/query/v4/docs/framework/solid/overview) as well!

```shell
yarn add @tanstack/solid-query
```

I'd say use the built-in Resource as long as you can and only when you need to manage your queries in a more complex fashion switch to Solid Query.

There was some [talk of Solid + MobX](https://www.reddit.com/r/solidjs/comments/170xcnv/solidjs_mobx_is_amazing/) in the past but to me, in case of async resources, I still prefer Solid Resource since it encapsulates both the fetch and the data in one easy to manipulate object.

## Architecture
That's all nice, you might say, but having those Resources in components is not great, I'd like to extract them outside to make them more testable and maintainable. Well, there is nothing stopping you form doing exactly that!

I like to put them outside components like this:
`post-resource.ts`
```ts
const [postId, setPostId] = createSignal<number>();
const postResource = createResource(postId, api.fetchPost);

export const getPost = (id: number) => {
  setPostId(id);
  return postResource[0];
};

export const updatePost = async (
  postPatch: Partial<Post>
) => {
  const success = await api.updatePost(postPatch);

  if (success) {
    const { mutate } = postResource[1];
    mutate(p => ({ ...p!, ...postPatch }));
  }
}
```

And then in the component I just use those exposed functions:
```ts
const PostDetail = () => {
  const postId= +params.id;
  const post = getPost(postId);
  ...
}
```

That means you can build an entire frontend architecture around the Resources! No need to sync API calls with Redux or other complicated solutions, you can just use the Resource and expose as much or little as you want.

One benefit of extracting the Resources from components is that they can then be [easily tested](https://www.solidjs.com/guides/testing) on their own. The only thing you should be careful about is not to forget `createRoot`. It sets up reactive context inside which updates to reactive state are detected. This context normally gets set up when calling `render`, but we are not rendering anything when testing just Resources. Therefore, you should wrap the part that creates Resources in `createRoot`.

```ts
test('resource fetches from api', async () => {
  const { postResource } = setup();
  ...
})

function setup() {
  return createRoot(() => initResources(testApi));
}
```

If you are wondering what the `setup` function is about, check out my article [5 steps to better unit/integration testing](https://dev.to/daelmaak/5-steps-for-better-unitintegration-testing-4ndc).

## That's it folks
How do you like Solid Resource so far, and do you use it yourself? Which other architecture do you use in your Solid apps? I'd love to know!