Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the serialisation/deserialisation of non-POJOs #9401

Open
carlosV2 opened this issue Mar 11, 2023 · 7 comments
Open

Allow the serialisation/deserialisation of non-POJOs #9401

carlosV2 opened this issue Mar 11, 2023 · 7 comments
Labels
feature request New feature or request

Comments

@carlosV2
Copy link

carlosV2 commented Mar 11, 2023

Describe the problem

In my current project I'm in need of sending data back and forth the server for which I've built a series of objects that I can easily serialise and deserialise with the amazing devalue library. This includes the initial load of data via some contraption like this:

export const load = (async () => {
  const data = // ... load data. f.e. from DB
  return { data: pack(data) };
}) satisfies PageServerLoad;

In this example, the function pack is a wrapper of the stringify function from devalue with a set of object types passed to this function as reducers.

On the client side, I'm obviously running the opposite method to get the data back with the equivalent revivers.

I particularly like this option because it allows me to introduce simple functions to these objects that I can run seamlessly regardless of the environment.

At the current stage, however, I'm in need of deduplicating code by separating layout logic from page logic. More often than not, I need to use the layout data to perform some logic on the page (think about the layout loading a resource and the page validating access to this resource).

Due to the current SvelteKit behaviour I can't return any non-POJO on the layout as it will be serialised for the client so I'm forced to use my pack function. However, when I pull this date in the page, I have to first unpack it to be able to use it which feels like a waste of resources.

The same waste of resources can happen on the client side. Attempting to use the data in the client layout requires to unpack it first but so does attempting to use it in the page. With a solution proposed here, the serialisation could be handled by SvelteKit and the same result could be used as many times as needed.

Describe the proposed solution

I think it would be nice if we could add some code in the hooks.server.ts and the hooks.client.ts to allocate for this. For example:

type Reducer = (value: any) => any;
type Reviver = (value: any) => any;
type SerialiseHandle = { reducer?: Reducer, reviver?: Reviver };

export const serialisationHandles: Record<string, SerialiseHandle> = {
   MyObj: {
      reducer: (value) => value instanceof MyObj && [value.param1, value.param2],
      reviver: ([param1, param2]) => new MyObject(param1, param2)
   },
   MyOtherObject: { /* ... */ }
}

This could be replicated similarly on the client hook thus allowing for the potential serialisation/deserialisation of dedicated object (i.e. a server version of MyObj and a client version of it). Alternatively, the handles can be abstracted into a third file to keep consistency between both environments.

Alternatives considered

I tried by returning the raw object on the layout which allows for the exact object to be retrieved on the page. Then I tried to re-return the object with the custom serialisation applied but it still complained about the non-POJOs.

Right now my only options are to either pack/unpack the data or to reload it altogether which it is, in both cases, a waste of compute power.

In the client side, there is the possibility of using a context to prevent from wasting too many resources.

Importance

nice to have

Additional Information

No response

@dummdidumm dummdidumm added the feature request New feature or request label Mar 13, 2023
@dkantereivin
Copy link

This would be super useful as it's already supported by devalue (as mentioned) and this would simply expose the appropriate API. In my particular usecase, I'd like to be able to write a replacer that converts MongoDB ObjectIDs to their hex strings, so that way I don't need to manually convert them every time I want to return an object that contains an ObjectID (needless to say, this is frequent).

@pkb-pmj
Copy link

pkb-pmj commented Apr 29, 2023

Just encountered the same issue with Prisma Decimal data type.
I quite like the proposed solution, it would be useful and rather easy to use.

@Beiri22
Copy link

Beiri22 commented May 3, 2023

This would be really nice. I'd like to use luxon as date library. The luxon DateTime type is not a simple POJO, so you need this API of devalue for it to work. I'm not sure, if the custom serializer / deserializer functions have to be put in the hooks file; or maybe better into the sveltekit configuration; but either way would be a big plus!

@Rich-Harris What do you think? Wouldn't such a feature harvest the full potential of devalue?

@clavin
Copy link

clavin commented Aug 3, 2023

Adding on, I'm using the official polyfill for the upcoming Temporal standard library in my project and I want to be able to send Temporal.Instants (and such) between the server and client, which should be supported in the future anyways. Having this customization of devalue supported in SvelteKit would be handy. 😄

@AndreasHald
Copy link

This is very interesting - we are also using the mongodb ObjectId class heavily and this would definitely make it a lot simpler for us.

I tried to give a PR a go, but the point where I got stuck is probably why this is tricky to implement - when server side rendering pages kit uses uneval and not stringify and supporting custom types here would require a replacer and notably that the class for recreating the type is in scope when the code runs.

If you would do this in hooks.(client|server) it now becomes a significantly more complex api.

@JonathonRP
Copy link

JonathonRP commented May 15, 2024

Adding on, I'm using the official polyfill for the upcoming Temporal standard library in my project and I want to be able to send Temporal.Instants (and such) between the server and client, which should be supported in the future anyways. Having this customization of devalue supported in SvelteKit would be handy. 😄

Same using Temporal plain date for financial admin dashboard and selecting/filtering on date ranges and Temporal and Date are not serialized. This would be great! (Also superjson already allows this but sveltekit using devalue internally gives no access to swapping these things out)

Fyi could look at trpc implementation because they seem to have it figured out. With transformers that work for server and client (like what's being asked for) and even swapping with others (like I was posing with superjson).

@AlbertMarashi
Copy link

@Rich-Harris also need this with my surrealdb, which introduces RecordID class that gets serialised into json instead of being represented as a class on the client side, and messes with typings due to the fact of how the return data is typed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request
Projects
None yet
Development

No branches or pull requests

9 participants