kit
kit copied to clipboard
Using json alternative in [Page] endpoints.
Describe the problem
The results of endpoints are transformed to and interpreted as JSON. But JSON does not deserialize some data types properly, like dates or maps.
Describe the proposed solution
Allow other de/serializers like superjson or cbor. It might be a simple configuration option that sets a serializer and deserializer function to be used alternatively.
Alternatives considered
No response
Importance
would make my life easier
Additional Information
No response
I'm... a little confused.
export const get = () => {
const myComplicatedObject = getMyComplicatedObject();
return: {
headers: {
'Content-Type': 'application/json'
},
body: myCustomSerializer(myComplicatedObject)
}
}
Does that not work?
The manual serialization might work in the endpoints, and you might manually deserialize your fetch result in the frontend. But as far as I understand, the fetching mechanism of page endpoints is not interruptable...
I'm trying to understand why it needs to be interruptable -- it seems like the worst case for using a custom serializer/deserializer is, what, three or four extra lines of code?:
// index.ts
export const get = () => {
const myComplicatedObject = getMyComplicatedObject();
return: {
headers: {
'Content-Type': 'application/json'
},
body: {
content: myCustomSerializer(myComplicatedObject)
}
}
}
// index.svelte
<script lang="ts>
export let content: string;
let deserializedContent = customDeserializer(content);
</script>
Is there a more complex usecase that makes this harder?
It's not the worst case, you're right. But it's boilerplate code on every page that I don't consider good style. Allowing the user to set a custom de/serializer to access the own backend seems more elegant to me. It's just my opinion... Let it be the following. At a global config level, you can register a pair of functions ser/deser for a given content-type. Then those are used when the content type is set in the endpoints. Might work like a charm, don't look like a hack and be a useful extension also for external API calls.
One gotcha this could help avoid is when the data changes between server and client. i.e. the content contains Date objects, but then ISO format strings on the client and ... errors (not a good experience, unclear why for new users).
According to https://github.com/sveltejs/kit/issues/5960#issuecomment-1217964620 and https://github.com/sveltejs/kit/issues/5960#issuecomment-1218494704 it seems likely that Svelte-Kit will end up using https://github.com/rich-harris/devalue for sending data from the server to the client (e.g., in +page.server.ts
load functions.
That's only a solution if access to that server data is always through the matching SK front-end.
Maybe you have other apps or components that would like to fetch data in standard JSON format.
If you're doing that, it sounds to me like it should be your responsibility for just deserializing it yourself. That sounds an awful lot simpler than telling SvelteKit how it should be automatically interpreting certain JSON responses. If you have an endpoint (either internal to the app or external to it) that returns data in a certain format, you just need to interpret it correctly.
I would, if SvelteKit provided the hooks to enable it. But you get one kind of data on the server and different data (because it's been JSON serialized) on the client. If I could override the deserialization or set a reviver
function to be used, it wouldn't be a problem. But the only way right now AFAIK is to do double serializing which is horrible.
I was just about to open an issue about that. We have no way of passing a replacer
function to JSON.stringify()
on the server or a reviver
function to JSON.parse()
on the client because there are no hooks for that in Svelte Kit. I consider the ability to transform responses a very basic and important feature.
Does #6008 address this? I'd be interested to hear if there are cases that devalue
wouldn't cover (especially once we add BigInt
support).
That's only a solution if access to that server data is always through the matching SK front-end.
Maybe you have other apps or components that would like to fetch data in standard JSON format.
If you want to be able to return data in a different format than SvelteKit expects for consumption by other types of users, then that sounds like a different endpoint. The endpoints that SvelteKit uses to retrieve data from +page.server.js
files aren't a public API anyway, and are subject to change at any time. There's no reason to be stingy about endpoints and only provide data in some other serialized format and then expect to be able to tell SvelteKit how to read that format.
I take it page endpoints are considered "framework" and liable to change at any time / shouldn't be depended on as an external API (is it worth setting headers to prevent external requests so people don't accidentally do so?)
But what about when a page loads data from a server / standalone endpoint? I thought those were also direct (with no serialization) on the server?
That's where the custom reviver would be wanted, but not the non-standard JSON serialization that the page endpoints might use.
If a page loads data from +server.js
, it does so with fetch
, in which case it would be quite mad for SvelteKit to intercede — we can't mess with await request.json()
etc.
I thought those were also direct (with no serialization) on the server?
There's no intermediate HTTP request — our fetch
wrapper creates a Request
object from your fetch
call and passes it back to the SvelteKit server to handle directly — but beyond that, it's a standard Request
-Response
process. We don't have any privileged access to the response body (which could be expressed as a string or a Uint8Array
or a ReadableStream
etc), and nor should we.
Does #6008 address this? I'd be interested to hear if there are cases that
devalue
wouldn't cover (especially once we addBigInt
support).
You will find users with special needs at all time. Someone might want to use a binary format to reduce bandwidth or some special coding to increase performance, maybe some freak wants zod to intercept and check all the values ... ;-) I still suggest a way to customize - with a well chosen global default...
our fetch wrapper creates a Request object from your fetch call and passes it back to the SvelteKit server to handle directly
That's where the server / client inconsistency may originate. Maybe it could be be JSON serialized so they are always the same?
You will find users with special needs at all time. Someone might want to use a binary format to reduce bandwidth
Sounds like a great reason to use fetch
with a +server.js
file
That's where the server / client inconsistency may originate
What inconsistency?
What inconsistency?
That you get JavaScript Date Objects during SSR but ISO 8601 format date strings after client navigation.
But you don't, because we check that values are serializable and throw an error otherwise.
Since we now use devalue to serialize data (#6008) and since we're not going to interfere with people's Response
objects, I don't think there's anything to do here - closing