Plugin
Plugins allow you to connect into the lifecycle of the whole library. They expose different sets of the methods allowing you to react to particular events happening within a system.
- Persists request side-effects
- Manipulates the lifecycle and allows hooking in
How to use?
The best way to create new plugin is to wrap the Plugin class into a function. This way you can pass in the
configuration options and return the plugin instance.
import { Plugin } from "@hyper-fetch/core";
const MyPlugin = ({ module }: { module: string }) => {
return new Plugin({
// Hook into creation of the request and log it
onRequestCreate: (request) => {
console.log(`[${module}] Request created`, request);
},
});
};
const client = new Client({
url: "https://api.example.com",
}).addPlugin(MyPlugin({ module: "MyApp" }));
Lifecycle Hooks
Plugins can subscribe to events across the entire Hyper Fetch system. Each hook method registers a callback and returns
the Plugin instance for chaining.
Plugin Lifecycle
| Hook | When it fires | Data received |
|---|---|---|
onMount | Plugin is added to the client via addPlugin() | { client } |
onUnmount | Plugin is removed via removePlugin() | { client } |
Request Lifecycle
| Hook | When it fires | Data received |
|---|---|---|
onRequestCreate | A new request instance is created via createRequest | { request } |
onRequestTrigger | A request is about to enter the dispatcher | { request } |
onRequestStart | The adapter begins executing the request | { request } |
onRequestSuccess | The adapter returns a successful response | { response, request } |
onRequestError | The adapter returns an error response | { response, request } |
onRequestFinished | The request completes (success or error) | { response, request } |
Cache Lifecycle
| Hook | When it fires | Data received |
|---|---|---|
onCacheItemChange | A cache entry is created or updated | { cache, cacheKey, prevData, newData } |
onCacheItemDelete | A cache entry is deleted | { cacheKey, cache } |
Dispatcher Lifecycle
| Hook | When it fires | Data received |
|---|---|---|
onDispatcherQueueCreated | A new queue is created for an unseen queryKey | { dispatcher, queue } |
onDispatcherItemAdded | A request is added to a queue | { dispatcher, queue, queueItem } |
onDispatcherItemDeleted | A request is removed from a queue | { dispatcher, queue, queueItem } |
onDispatcherQueueRunning | A queue status changes ("running", "paused", "stopped") | { dispatcher, queue, status } |
onDispatcherQueueDrained | A queue has no more pending requests | { dispatcher, queue } |
onDispatcherQueueCleared | An entire queue is cleared | { dispatcher, queue } |
onDispatcherCleared | All dispatcher storage is fully cleared | { dispatcher } |
Adapter Lifecycle
| Hook | When it fires | Data received |
|---|---|---|
onAdapterFetch | The adapter is about to execute a network request | { request, requestId, adapter } |
const analyticsPlugin = new Plugin({ name: "analytics" })
.onRequestStart(({ request }) => {
performance.mark(`req-start-${request.queryKey}`);
})
.onRequestFinished(({ request, response }) => {
performance.mark(`req-end-${request.queryKey}`);
analytics.track("api_call", {
endpoint: request.requestOptions.endpoint,
success: response.success,
});
})
.onCacheItemChange(({ cacheKey, newData }) => {
console.log(`Cache updated: ${cacheKey}`);
});
client.addPlugin(analyticsPlugin);
Available methods
| Name | Description |
|---|---|
| Invoke a registered plugin method by name. Used internally by the client to dispatch lifecycle events. |
| Callback that will be executed when plugin is unmounted |
| Callback that will be executed when request gets triggered |
| Callback that will be executed when response is successful |
| Callback that will be executed when request starts |
| Callback that will be executed when response is finished |
| Callback that will be executed when response is failed |
| Callback that will be executed when request is created |
| Callback that will be executed when plugin is mounted |
| Called when a dispatcher queue transitions to running, paused, or stopped |
| Called when a dispatcher queue has no more pending requests |
| Called when a new dispatcher queue is created |
| Called when an entire dispatcher queue is cleared |
| Called when an item is removed from a dispatcher queue |
| Called when a new item is added to a dispatcher queue |
| Called when the dispatcher storage is fully cleared |
| Called when a cache entry is deleted |
| Called when a cache entry is created or updated |
| Called when the adapter fetcher is about to execute a request |
| Bind the plugin to a client instance. Called automatically when the plugin is added via
client.addPlugin()
. |
Use cases
Logging
Plugins are heavily used by our devtools system to provide you with the live feed of what is happening in your application. They are great for logging, tracking, debugging and collecting metrics.
Persistence
There are many applications which require you to persist the state of the application. After reloading of the browser, app or server you need to keep up with the unsent requests.
There are many solutions for persistence, all of them approach this problem by introducing the sync mechanisms and local DBs. Among them you can find:
Hyper Fetch being the fetcher and a server state manager it is able to persist the state of the application. Not only that - it can store and persist requests which are not triggered yet - for example if you're in the offline mode.
Having the request in the JSON format you can easily persist it in the persistent storage like local storage or any other storage you want.
Now imagine our app is offline, we close it and open it again. We can see that the request is still in the persistent storage and we can trigger it again.
We don't have to do it for every request. To do so, we can attach into the Dispatcher lifecycle and on the
onDispatcherItemAdded and onDispatcherItemDeleted events. With them we can add the stringified requests to the
persistent storage.
Then on the start of the app we can trigger the requests from the persistent storage - this is because after refresh of the app Dispatchers do not have access to the persisted storage with our requests. At this time we can add the requests to the Dispatcher and trigger them or wait a bit waiting for the app to load.
That's it! It is very simple concept, so keep that in mind when you're building your app.
We are waiting for your feedback on this feature. If you have any ideas or suggestions, please let us know. We are very determined to make it better and more useful for you.
There are few more aspects that could be worth mentioning like:
-
Optimistic approach when you want to reflect all of the changes from your persisting requests inside of your UI.
-
Topic of the connectivity itself. Your backend need to track all of the persistent requests with some kind of identifier from the frontend - possibly passed with a Header or a Query Parameter. To prevent duplicates and conflicts, you need to have some kind of a logic to prevent them.
These are large topics deserving their own guides, so stay tuned!
Parameters
Configuration options
| Name | Type | Description |
|---|---|---|
| data | | Data stored in a plugin |
| name | | Name of the plugin |
