diff --git a/docs/config/404.md b/docs/config/404.md index bfea573..eea6ebc 100644 --- a/docs/config/404.md +++ b/docs/config/404.md @@ -7,17 +7,21 @@ align-items: center; font-size: 20px; } + rk-embed { width: 100%; } + h1.not-found { display: inline-block; border-right: 1px solid rgba(0, 0, 0, 0.3); padding-right: 1rem; margin-right: 1rem; margin-bottom: 6rem; + margin-top: 2.5rem !important; vertical-align: top; } + h1.not-found__text { display: inline-block; padding-right: 1rem; @@ -34,10 +38,15 @@ // Write some code in the meantime :) const Axios = require('axios'); -const { setupCache, buildWebStorage } = require('axios-cache-interceptor'); +const { setupCache } = require('axios-cache-interceptor'); -const axios = Axios.create({}); -setupCache(axios, {}); +const axios = Axios.create({ + // +}); + +setupCache(axios, { + // +}); await axios.get('https://jsonplaceholder.typicode.com/posts/1'); ``` diff --git a/docs/config/sidebar.md b/docs/config/sidebar.md index a3d5330..ab4a416 100644 --- a/docs/config/sidebar.md +++ b/docs/config/sidebar.md @@ -1,10 +1,11 @@ - Getting Started - - [Homepage](/ 'Homepage') + - [Introduction](/ 'Introduction') - [Installing](pages/installing.md 'Installing') - [Configuration](pages/configuration.md 'Configuration') - [Storages](pages/storages.md 'Custom storages') - [Request Id](pages/request-id.md 'Request Id') + - [Invalidating Cache](pages/invalidating-cache.md 'Invalidating Cache') - [Development mode](pages/development-mode.md 'Development mode') - API Reference diff --git a/docs/index.html b/docs/index.html index 8a3b3ce..ea91dee 100644 --- a/docs/index.html +++ b/docs/index.html @@ -82,11 +82,14 @@ Axios Cache Interceptor - + @@ -117,6 +120,5 @@ crossorigin src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-jsx.min.js" > - diff --git a/docs/js/index.js b/docs/js/index.js index e9a1b2f..6631642 100644 --- a/docs/js/index.js +++ b/docs/js/index.js @@ -3,7 +3,7 @@ window.$docsify = { loadSidebar: 'config/sidebar.md', notFoundPage: 'config/404.md', - homepage: 'pages/homepage.md', + homepage: 'pages/introduction.md', subMaxLevel: 2, diff --git a/docs/pages/examples.md b/docs/pages/examples.md index 5282ba1..dac2151 100644 --- a/docs/pages/examples.md +++ b/docs/pages/examples.md @@ -6,7 +6,7 @@ > > Please, don't hesitate to open an PR with your own examples and use cases. -## Nodejs server example +## Nodejs Server An **NodeJS** with **ExpressJS** example to return data from another api. @@ -60,32 +60,54 @@ app.get('/cache/:id/get', async (req, res) => { app.listen(3000); ``` -## Jsx component example +## React Component -You shouldn't -[store cache in state libraries](https://betterprogramming.pub/why-you-should-be-separating-your-server-cache-from-your-ui-state-1585a9ae8336), -it even is a bad practice. And even if you do so, you probably will have to write a lot of -code to handle cache invalidation, strategies and etc. +> You shouldn't +> [store cache in state libraries](https://betterprogramming.pub/why-you-should-be-separating-your-server-cache-from-your-ui-state-1585a9ae8336), +> it even is a bad practice. And even if you do so, you are going to write a lot of error +> prone code to handle cache invalidation, caching strategies and so on. -With this library, you can just call any axios method without worrying about requesting -thousands of times for every component draw. Simple as that! +I'm also the maintainer of +[`Axios Cache Hooks`](https://tinylibs.js.org/packages/axios-cache-hooks/), a +[**950B**](https://bundlephobia.com/package/axios-cache-hooks) library that just provide +you a simple and complete react hook to use with your axios cached instance. + +It is a super powerful hook, because it will share the same updatable piece of data for +every request with the same [id](pages/request-id.md). By using a +[web storage](pages/storages.md?id=Web-storage) with it, you are up to **share component +level data in a micro frontend scale**. + +```ts +import Axios from 'axios'; +import { createAxiosHooks } from 'axios-cache-hooks'; +import { setupCache } from 'axios-cache-interceptor'; + +const axios = setupCache(Axios); +const { useQuery, useMutation } = createAxiosHooks(); -```jsx function Component() { - // React component state (but can be from any other framework, library and etc) - const [data, setData] = useState(null); + const [user, { loading, error }] = useQuery(() => axios.get('/users/123')); - // Calling this function every component redraw does not have any - // problems, as the response is cached in the first request. This - // even work with concurrent requests and for many components at - // the same time - axios.get('https://api.example.com').then((response) => { - setData(response.data); - }); + if (loading) + return ( +
+

Loading...

+
+ ); + + if (error) { + console.error(error); + + return ( +
+

Error!

+
+ ); + } return (
-
{data}
+
{user.name}
); } diff --git a/docs/pages/homepage.md b/docs/pages/introduction.md similarity index 68% rename from docs/pages/homepage.md rename to docs/pages/introduction.md index 05fe354..d320d08 100644 --- a/docs/pages/homepage.md +++ b/docs/pages/introduction.md @@ -10,3 +10,11 @@ Axios Cache Interceptor can be understood as an intermediary that will analyze e request made, check if no similar request has been made before, if so, return it, if not, wait for the response, warn other requests if they are waiting and return the response to the original caller. + +## Where to start + +- [Installing](pages/installing.md) +- [Configuration](pages/configuration.md) +- [Per request configuration](pages/per-request-configuration) +- [A NodeJS Server example](pages/examples?id=nodejs-server) +- [A React Component example](pages/examples?id=react-server) diff --git a/docs/pages/invalidating-cache.md b/docs/pages/invalidating-cache.md new file mode 100644 index 0000000..6b1dbb5 --- /dev/null +++ b/docs/pages/invalidating-cache.md @@ -0,0 +1,132 @@ +# Invalidating Cache + +A common problem when using a cache-first approach in your client is that there will be +times when your server has newer data, your user knows that it has, but their client +doesn't. + +Here's a simple step to step example of it. + +1. User list all available posts, server return an empty array. +2. User proceeds to create a new post, server returns 200 OK. +3. Your frontend navigates to the post list page. +4. The post list page still shows 0 posts because it has a fresh cache for that request. +5. User gets angry that the post list page is empty, they click the refresh button. +6. The in-memory cache is vanished and the post list page now shows the created post. + +## How to fix it + +> You should be familiar with the [Request ID](pages/request-id.md) concept. + +There is a [`cache.update`](pages/per-request-configuration.md?id=cacheupdate) option +available for every request that you make. You can resolve this problem in two "major" +ways: + +**In these examples, we'll wrap the `list-posts` and `create-post` calls inside known +functions, thus, centralizing the api logic into a single function.** + +### Updating the local cache + +When you are calling a `create-post` endpoint, it's almost sure that, if the request +succeed, your `list-posts` cache will get out of date. + +You can update your `list-posts` cache by using the `cache.update` option in yours +`create-post` requests. + +By default, you only need to know the id of the `list-posts` request. There's many ways to +get that information, either by saving the `response.id` in a object or manually defining +the `config.id` in each request. + +> This method only works when you are manually making a known request out of date by +> calling another endpoint of your API. Similarly with a `list-posts` and a `create-post` +> request. + +```js +function listPosts() { + return axios.get('/posts', { + id: 'list-posts' + }); +} + +function createPost(data) { + return axios.post('/posts', data, { + cache: { + update: { + // Will perform a cache update for the `list-posts` internal cache + 'list-posts': (cachedState, createPostResponse) => { + // If the cache is not cached, we don't need to update it + if (cachedState.state !== 'cached') { + return 'ignore'; + } + + // Imagine the server response for the `list-posts` request is: { posts: Post[]; } + // And the `create-post` response comes with the newly created post. + + // Add the current created post to the end of the post's list + cachedState.data.posts.push(createPostResponse.data); + + // Return the same cache state, but a updated one. + return cachedState; + } + } + } + }); +} +``` + +If you want to update the cache for a specific request outside of the request config, you +can use the storage api. + +```ts +if (someLogicThatShowsIfTheCacheShouldBeUpdated) { + const cache = await axios.storage.get('list-posts'); + + // Edits the current cache + cache.data = '...'; + + await axios.storage.set('list-posts', cache); +} +``` + +Then, every time you create a post, your listPosts function will - without contacting the +server - return the updated list of posts. + +### Invalidating the local cache + +This method works by completely erasing the local cache and forcing the client, (whenever +needed) request again to your endpoint. + +There's about 3 situations that makes this method better than the previous one: + +- When you cannot predict the entire data needed. +- When you know that there is newer data available made by another user or service. +- When the data is too bigger or complex to you handle it in your client without the need + of duplicating hundreds of service classes. + +```js +function listPosts() { + return axios.get('/posts', { + id: 'list-posts + }); +} + +function createPost(data) { + return axios.post('/posts', data, { + cache: { + update: { + // Will erase the list-posts cache and force the client (when needed) request to the server again + 'list-posts': 'delete' + } + } + }); +} +``` + +If you want to invalidate the cache for a specific request outside of the request config, +you can use the storage api. + +```ts +if (someLogicThatShowsIfTheCacheShouldBeInvalidated) { + // Deletes the current cache + await axios.storage.remove('list-posts'); +} +``` diff --git a/docs/pages/request-id.md b/docs/pages/request-id.md index 438c365..199fb8b 100644 --- a/docs/pages/request-id.md +++ b/docs/pages/request-id.md @@ -1,14 +1,18 @@ # Request id -A good thing to know is that every request passed through this interceptor, has an id. -**This does not mean that is a unique id**. The id is used in a number of ways, but the -most important is to bind a request to its cache. +Every request passed through this interceptor, has an id. **This doesn't mean its a UNIQUE +ID**. + +Each request id are responsible for binding a request to its cache, for referencing (or +invalidating) it later and to make the interceptor treat each request with the same id as +the same request. The id generation is good enough to generate the same id for theoretically sames requests. A simple example is a request with `{ baseURL: 'https://a.com/', url: '/b' }` results to the same id with `{ url: 'https://a.com/b/' }`. -Also, a custom id can be used to treat two requests as the same. +> If you send two completely different requests with the same id, the interceptor will +> treat them as the same request, sharing its cache and every other as. ```js #runkit const axios = require('axios');