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

support user provided custom serialization/deserialization #77

Open
ethanniser opened this issue Oct 23, 2023 · 3 comments
Open

support user provided custom serialization/deserialization #77

ethanniser opened this issue Oct 23, 2023 · 3 comments

Comments

@ethanniser
Copy link
Owner

currently serialization/deserialization is hard coded in

there are many valid usecase for a user wanting a custom solution:

  • working around existing search param use cases
  • custom array / object notation
  • support for dates/bigints (superjson)

maybe look at how trpc handles custom transformers?

To discuss:
What should the signature of the swappable serializer be?
How to handle differences between search param and route param serialization / deserialization?

@ethanniser
Copy link
Owner Author

This should be trivial with @effect/schema but requires a big breaking change

@TmLev
Copy link
Contributor

TmLev commented Feb 6, 2025

If I can, I'd like to mention my use-case for custom serialisation of routeParams.

I have a route that looks like this: /foos/[fooHandle]. Now the fooHandle can be one of two things:

  • A uuid like "523ae1d0-2645-469e-b52b-125cf71498ce"
  • A short id formatted as id<NUMBER> (literal id followed by a number)

In Typescript it looks like so:

type FooHandle = 
  | { id: string } // uuid
  | { shortId: number } // without `id` prefix from URL

Currently, I do the following when declaring routeParams:

export const Route = {
  routeParams: z.object({
    fooHandle: z.string().transform((h) => parseFooHandle(h)),
  }),
} satisfies DynamicRoute;

This work perfectly for deserialising raw string from URL into a valid FooHandle.

However, when I need to create a path to this route, I have to serialise fooHandle manually:

$path({
  route: "/foos/[fooHandle]",
  routeParams: {
    fooHandle: serializeFooHandle(foo.handle),
  },
})

since routeParams.fooHandle expects a string, and foo.handle is of type FooHandle above.

Ideally, routeParams.fooHandle should be able to take an arbitrary input as long as it can be serialised into something appropriate for the route param of the $path function. And this serialisation should probably be declared in the const Route itself.

@ethanniser
Copy link
Owner Author

the options as I see them:

  1. do nothing- users are forces to implement this on their own (most likely though the same way as above)

pros: no breaking changes
cons: not the best dx

  1. add a global transformer (trpc-esce). Would require a $path builder. ie:
export $path = buildPath({ transformer })

pros: idk
cons: reexport is kind of annoying- leads to autocomplete conflicts if named the same
seems pretty common to me to have per route special serialization/deserialization logic which here is a bit tedious

  1. add as a argument to $path
$path({ ... }, { transformer })

pros: non breaking change, still can be typesafe (I think)
cons:
requires you to remember to provide this

  1. switch to @effect/schema
    pros: can basically do any two way transformation. very typesafe, composable
    cons: huge breaking change, requires schemas to be rewritten

Just thinking now though that even if we switch to schema, I think we would need to code gen a file with all of the schemas. Because right now $path at runtime doesnt actually depend on them at all- its pure. However I think we would need to create basically a runtime version of the AppRouter thing and have it lookup the right schema to encode at runtime


anyways open to thoughts

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

No branches or pull requests

2 participants