Option to use superjson to serialize/deserialize data
Closed this issue · 3 comments
https://www.npmjs.com/package/superjson
I want to use a Decimal
from https://www.npmjs.com/package/decimal.js in an swr
query, but an error is thrown if I return a Decimal
from a fetcher
. As I understand, it is related to the structuredClone
as it is used to store data in IndexedDB. Would you consider adding an option to customize serialization/deserialization of data to be able to use objects with functions (class
es) with swr
?
BroadcastChannel also requires objects to fit the structuredClone
algorithm.
Currently, you could write a wrapper around the model. Fetcher would return the serialized version of the data and then your wrapper could return fetch
and live
functions that deserialized it. What do you envision as advantages of having that baked directly into the library instead of letting very domain specific handling be taken care of by the consumer?
What do you envision as advantages of having that baked directly into the library instead of letting very domain specific handling be taken care of by the consumer?
Reasonable. I agree that this feature should be added only after this issue becomes not domain specific(i.e., when more people will ask for this). I'll write a wrapper meanwhile. Let this issue be an indicator that I encountered this problem just as I wrote my first query.
I managed to write this code and it works great:
import type { Fetcher, SuspenseFn } from "@svelte-drama/swr/dist/types";
import { swr as dramaSwr } from "@svelte-drama/swr";
import { derived, type Readable } from "svelte/store";
import superjson from "superjson";
export function swr<ID, T>(options: {
key(params: ID): string;
fetcher: Fetcher<ID, T>;
}): MySwrModel<ID, T> {
const model = dramaSwr({
key: options.key,
fetcher: async (key, params) => {
return superjson.serialize(await options.fetcher(key, params));
},
});
return {
...model,
live(params, suspend) {
const store = model.live(params, suspend);
return derived([store], ([data]) => {
if (!data) {
return data;
}
return superjson.deserialize(data);
});
},
async refresh(params) {
return superjson.deserialize(await model.refresh(params));
},
};
}
// here you can register any custom type you want. I needed `BigNumber` from `ethers`.
superjson.registerCustom<ethers.BigNumber, string>(
{
isApplicable: (v): v is ethers.BigNumber => ethers.BigNumber.isBigNumber(v),
serialize: (v) => v.toString(),
deserialize: (v) => ethers.BigNumber.from(v),
},
"ethers.BigNumber",
);
type MySwrModel<ID, T> = {
clear(): Promise<void>;
delete(params: ID): Promise<void>;
live(params: ID, suspend?: SuspenseFn): Readable<T | undefined>;
refresh(params: ID): Promise<T>;
// TODO: support this too
// fetch(params: ID): Promise<T>;
// update: {
// (params: ID, data: T): T;
// (params: ID, fn: (data: T) => MaybePromise<T>): Promise<T>;
// };
};