Skip to content

Commit 203cb66

Browse files
authored
Add Vanilla JS caching example (openapi-ts#1181)
1 parent 11701b1 commit 203cb66

File tree

1 file changed

+61
-1
lines changed

1 file changed

+61
-1
lines changed

docs/src/content/docs/openapi-fetch/examples.md

+61-1
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,72 @@ Beyond this, you’re better off using a prebuilt fetch wrapper in whatever JS l
9999
- **React**: [React Query](#react-query)
100100
- **Svelte**: (suggestions welcome — please file an issue!)
101101
- **Vue**: (suggestions welcome — please file an issue!)
102-
- **Vanilla JS**: [Nano Stores](https://github.com/nanostores/nanostores)
102+
- **Vanilla JS**: (see below)
103103

104104
#### Further Reading
105105

106106
- <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/cache" target="_blank">HTTP cache options</a>
107107

108+
### Vanilla JS (idempotent)
109+
110+
For idempotent data fetching (not having to wrestle with promises—great for UI applications), [Nano Stores](https://github.com/nanostores/nanostores) is a great pair with `openapi-fetch`. This general strategy could also be used in simple UI applications or Web Components:
111+
112+
```ts
113+
import createClient, { Success, Error } from "openapi-fetch";
114+
import { map } from "nanostores";
115+
import { components, paths } from "./my-schema";
116+
117+
/**
118+
* Shared query
119+
*/
120+
121+
type DataLoader<T> = { loading: true; data?: never; error?: never } | { loading: false; data: Success<T>; error?: never } | { loading: false; data?: never; error: Error<t> };
122+
123+
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
124+
125+
const GET_USER = "/users/{user_id}";
126+
127+
$userCache = atom<Record<string, DataLoader<Success[paths[typeof GET_USER]["get"]]>>>({});
128+
$userErrorCache = atom<Record<string, DataLoader<Error[paths[typeof GET_USER]["get"]]>>>({});
129+
130+
function getUser(userId: string): DataLoader<paths[typeof GET_USER]["get"]> {
131+
// if last response was successful, return that
132+
if ($userCache.get()[userId]) {
133+
return { loading: false, data: $userCache.get()[userId] };
134+
}
135+
// otherwise if last response erred, return that
136+
else if ($userErrorCache.get()[userId]) {
137+
return { loading: false, error: $userErrorCache.get()[userId] };
138+
}
139+
// otherwise, return `loading: true` and fetch in the background (and update stores, alerting all listeners)
140+
else {
141+
client.get(GET_USER, { params: { path: { user_id: userId } } }).then(({ data, error }) => {
142+
if (data) {
143+
$userCache.set({ ...$userCache.get(), [userId]: data });
144+
} else {
145+
$userErrorCache.set({ ...$userErrorCache.get(), [userId]: error });
146+
}
147+
});
148+
return { loading: true };
149+
}
150+
}
151+
152+
/**
153+
* Usage example
154+
*/
155+
156+
// Loading
157+
console.log(getUser("user-123")); // { loading: true }
158+
// Success
159+
console.log(getUser("user-123")); // { loading: false, data: { … } }
160+
// Error
161+
console.log(getUser("user-123")); // { loading: false, error: { … } }
162+
```
163+
164+
Keep in mind, though, that reactivity is difficult! You’ll have to account for stale data in the context of your application (if using a JS framework, there are already existing libraries that can handle this for you). Further, this is a contrived example that doesn’t handle invalidating the cache—both data and errors. Cache invalidation will be dependent on your specific needs.
165+
166+
If you need to work with promises, then just using `openapi-fetch` as-is will usually suffice. If you need promises + caching, then you’ll have to come up with a solution that fits your own custom usecase.
167+
108168
## React Query
109169

110170
[React Query](https://tanstack.com/query/latest) is a perfect wrapper for openapi-fetch in React. At only 13 kB, it provides clientside caching and request deduping across async React components without too much client weight in return. And its type inference preserves openapi-fetch types perfectly with minimal setup. Here’s one example of how you could create your own [React Hook](https://react.dev/learn/reusing-logic-with-custom-hooks) to reuse and cache the same request across multiple components:

0 commit comments

Comments
 (0)