You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
My problem is that my database client returns data that includes classes, and I can't return this from my +page.server.ts file and use it in my +page.{ts,svelte}. Examples of these classes include Prisma.Decimal, model instances from Type/MikroORM, objects with extra methods returned by an extended Prisma client (see Prisma docs) or anything else that might be a class inside a deeply nested object.
Describe the proposed solution
Having read through #6008 (and the pull request), my understanding is that devalue creates JS code that recreates the JS object is it passed, and that getting the generated code to reference constructors for these classes would be difficult (that said, I have an idea for this that I'd like to propose, not certain if it'd work, see below).
Instead, what I really am looking for is the ability to hand some arbitrary data to Svelte and have it transform it to data that is compatible with devalue. i.e. I register a function (server-side) that identifies my class, then provide a function that takes an instance of this class and returns an object that is already compatible with devalue. This could then be incorporated into the type generation system (similar to param matchers) so that PageLoad functions have corrected types.
I don't think my explanation was very clear, so here's an example:
// src/routes/+page.server.tsimport{PageServerLoad}from"./$types"exportconstload: PageServerLoad=()=>{// getCurrentUser returns a User classreturn{user: awaitdb.getCurrentUser()}}
// src/serde/User.ts (or elsewhere)
import {Serializer, SerializerTest} from "@sveltejs/kit"
import {User} from "$lib/server/models"
// devalue can use this function to test if a non-POJO it encounters is a User object.
export const test = (v: any): v is User => v instanceof User
// If so, it uses this function to convert the class to a POJO (which devalue can serialize).
export const serialize: Serializer = (u: User) => ({id: u.id, username: u.username, email: u.email})
// src/routes/+page.tsimport{PageLoad}from"./$types";exportconstload: PageLoad=({data})=>{// data.user is now the object returned by our serialize function, and is typed as such.return{user,other: "data"}}
Idea for deserializing classes
Rich's initial explanation of using devalue in #6008 says the deserialization process is (roughly):
Could devalue be made to set window.__data to a function that can be passed functions to deserialize the data? This would avoid the issue of ensuring constructors are available to devalue's generated code. The deserialization functions could also be lazy loaded if needed.
This isn't really useful in the above example (a database model likely has server-side only functions attached to it, in which case I only want the POJO), but for custom scalars such as Prisma.Decimal, this would be quite helpful.
Alternatives considered
My current solution is to write a function that 'jsonifies' my complex objects in a type-safe-ish way (Range is from edgedb):
I can then wrap database queries in this (or add it with .then(jsonify)). This doesn't allow for deserializing classes.
I've also been looking at potentially using superjson, but I lose the ability to modify the PageData type without touching anything else - I have to modify the types that I am reporting to superjson.
Importance
nice to have
Additional Information
No response
The text was updated successfully, but these errors were encountered:
This came up before in various variations, and we're hesitant to add something like this as it complicates the (de)serialization mechanism quite a bit. Also, what about actions? And should all get the same treatment, or is this per-load-function, or per-thing-to-(de)-serialize? It's hard to find a good answer here, which is why we have deferred this to "implement a wrapper yourself" so far. You already did one part of this, you could do the other end through running something like
/// +page.svelteexportletdata;// is the jsonified thing
$: user=unjsonify<User>(data);
benmccann
changed the title
Ability to pass arbitrary classes from server to client.
Ability to serialize arbitrary classes to send to client
Jan 27, 2023
Good points. As a user I would expect the feature to occur for actions, and perhaps error data as well, although my gut feeling is that case would be extremely niche (and tbh I've never even tested if Date or BigInt is serialised in those either). I'm not at all familiar with Kit's internals, but I my guess was that actions and loaders share serialization logic, so that applying this to both would be not much more work (or complexity) than applying it to one. I suppose that's not the case when working with type generation?
Describe the problem
My problem is that my database client returns data that includes classes, and I can't return this from my
+page.server.ts
file and use it in my+page.{ts,svelte}
. Examples of these classes includePrisma.Decimal
, model instances from Type/MikroORM, objects with extra methods returned by an extended Prisma client (see Prisma docs) or anything else that might be a class inside a deeply nested object.Describe the proposed solution
Having read through #6008 (and the pull request), my understanding is that
devalue
creates JS code that recreates the JS object is it passed, and that getting the generated code to reference constructors for these classes would be difficult (that said, I have an idea for this that I'd like to propose, not certain if it'd work, see below).Instead, what I really am looking for is the ability to hand some arbitrary data to Svelte and have it transform it to data that is compatible with
devalue
. i.e. I register a function (server-side) that identifies my class, then provide a function that takes an instance of this class and returns an object that is already compatible withdevalue
. This could then be incorporated into the type generation system (similar to param matchers) so thatPageLoad
functions have corrected types.I don't think my explanation was very clear, so here's an example:
Idea for deserializing classes
Rich's initial explanation of using devalue in #6008 says the deserialization process is (roughly):Could devalue be made to set
window.__data
to a function that can be passed functions to deserialize the data? This would avoid the issue of ensuring constructors are available todevalue
's generated code. The deserialization functions could also be lazy loaded if needed.This isn't really useful in the above example (a database model likely has server-side only functions attached to it, in which case I only want the POJO), but for custom scalars such as
Prisma.Decimal
, this would be quite helpful.Alternatives considered
My current solution is to write a function that 'jsonifies' my complex objects in a type-safe-ish way (
Range
is fromedgedb
):I can then wrap database queries in this (or add it with
.then(jsonify)
). This doesn't allow for deserializing classes.I've also been looking at potentially using
superjson
, but I lose the ability to modify thePageData
type without touching anything else - I have to modify the types that I am reporting tosuperjson
.Importance
nice to have
Additional Information
No response
The text was updated successfully, but these errors were encountered: