trpc icon indicating copy to clipboard operation
trpc copied to clipboard

[RFC] Next major overview

Open KATT opened this issue 4 years ago • 8 comments

So. We're making a new major.

Goals

  • More ergonomic API for creating procedures and building out your backend
  • CMD+Click from your frontend and jump straight into the backend procedure. This will work with react-query as well!
  • Better scaling than the current structure - the TypeScript server starts choking a bit when you get close to 100 procedures in your backend
  • Fully backward compatible for the 99% use-case with a migration path which you can do incrementally.

Todos

Important for contributors: All of the work here is related to the next branch.

Tracked in https://github.com/orgs/trpc/projects/1

  • [x] New Router API
    • [x] Tracked in trpc/v10-playground - the current winning proposal is https://github.com/trpc/v10-playground/pull/33
    • [x] Error formatting in new API - formatError()-equivalent.
    • [x] meta-equivalent
    • [x] Maybe we need a createRootRouter() that additionally accepts { transformer: X, formatError: Y}
    • [x] Implement new API in tRPC
    • [x] Make old routers compatible (.legacy() at the end to transform them to the new version)
  • [x] New client API with backward compatibility
    • Maybe old can be client.query('postById', input, options) whilst new stuff can be client.queries.postById({ input, ...options})
  • [x] New react-query API with backward compat
  • [ ] Make actual issues for all of these
  • [x] Add all old projects in a folder (copy/paste examples/* to examples/.test so we can track breaking changes easily and make sure we remain backward compatible for a long time
  • [ ] Rewrite TRPCClient to be functional instead of a class but reuse the same interface
  • [ ] Write a migration guide
    • [ ] New router API
    • [ ] New links
  • [x] Move transformers back to runtime to make it backward compatible
  • [ ] Explore: Add stripInternal-flag
  • [x] Simplify HTTP-responses
  • [x] New links architecture (#1366)
  • [ ] Refactor observables implementation to have unknown errors or make it guaranteed to be TRPCClientError of some sort.
  • [ ] Make transformers dynamically opt-in similar to how a batch request is indicated by ?batch=1 maybe a transformed request could be ?transform=1 or transform=TRANSFORMER_NAME?
  • [ ] Write a blog post about new major and API.
  • [ ] "Marketing"? Hype video, new website, etc
  • [ ] Decide on structure for react-query integration
  • [ ] Add old website on www/-docs as v9.trpc.io
    • [ ] Link it in Docusaurus
    • [ ] Make sure we have deployment for a "v9"-branch

Stretch

  • [ ] Add DTO for clients without transformers - #1694
  • [x] #1597
  • [x] New Subscriptions API (nice to have) - #1596
  • [ ] trpc.output.snapshot() https://github.com/trpc/v10-playground/issues/34
Old notes

Definite

  • [x] Remove official support for node 12.x
    • [x] Remove E2E-test for 12.x
    • [x] Change tsconfig build target
  • [ ] Add official support for node 16.x
  • [ ] Add stripInternal-flag
  • [x] Remove all @deprecated stuff
    • [x] Remove LegacyRouter introduced in #915
    • [ ] [...]
  • [x] New links architecture (#1366)
  • [ ] Move official support for react-query 3 to 4
  • [ ] Simplify HTTP-responses
    • [ ] From { id:null, result: {type: 'data', data: x } } -> { result: { data: x } }
    • [ ] maybe remove id: null from HTTP responses?
  • [ ] Migration guides for all changed parts
    • [ ] New Links

Maybe ❓

Feedback please!

  • [x] Optimize so router inference only needs to be done once? #1218
  • [ ] ~Update so procedures needs to return { data: x } instead of just x so we could add metadata and potentially discriminated union of { data: x } | { error: y } ~
  • [x] Decide:
    • Pipe-based rather than object resolvers? { input: z.string(), resolve({input}) {}} -> `t.pipe(t.input(z.string), ({input}) =>
    • Chained resolvers
  • [x] Plan how to deal with degrading typescript performance when approaching ~100 procedures (there are no runtime issues, but the DX slows down)
  • [ ] Turn client.query result into [ output, { meta } ]
  • [ ] New useQuery() API that expects suspense as default - See this gist: https://gist.github.com/KATT/aa1ad532d6e57520b942f657569e1505
  • [ ] Refactor @trpc/server to easier work with CloudFlare-workers etc (#1375)
  • [ ] Collaborate with Blitz.js? https://github.com/blitz-js/blitz/discussions/3083#discussioncomment-1841427
  • [ ] https://jsonapi.org/format/

Transformers

  • [x] Turn transformers into a link on the client (#1366)
  • [ ] Revert above and put transformers back to runtime 😬
  • [ ] ~Turn transformers into a middleware on the server~
  • [ ] Make transformers dynamically opt-in similar to how a batch request is indicated by ?batch=1 maybe a transformed request could be ?transform=1 or transform=TRANSFORMER_NAME?

Misc

  • [ ] Rename @trpc/react/ssg -> @trpc/react/ssr & createSSGHelpers -> createServerSideHelpers (or something, as it is often used in getServerSideProps() as well as getStaticProps())

Open questions

  • Should we continue to follow OpenRPC?
  • Any changes that could make tRPC easier to support stuff like Svelte/Vue?
  • Any changes that could make tRPC stricter support stuff like AWS Lambdas? (Already is doable to do Lambdas but through an Express->Lambda converter) #882
  • Ideas?

KATT avatar Sep 05 '21 08:09 KATT

@mmkal would be awesome if you could share how we could make an options bag on that Router<X>

KATT avatar Sep 28 '21 01:09 KATT

I got about 2/3 of the way through a PR to do it, but got distracted. I'm also not sure that it will help all that much, but I'll try to pick it up again some time in the next week.

mmkal avatar Sep 28 '21 01:09 mmkal

I picked the typearg-options-bag up again, but got loads of type instantiation is excessively deep and possibly infinite errors, as well as all sorts of other type test failures, so I don't think it's worth it.

mmkal avatar Oct 14 '21 02:10 mmkal

Regarding Simplify HTTP-responses:

  • This would means moving away from the JSON-RPC spec, are we ok with this?
  • Would this also mean removing id: 1 from the HTTP-request?
  • I see you've listed https://jsonapi.org/format/ as a maybe? I've been using the Slack Web API (https://api.slack.com/web#slack-web-api__basics) recently which has a very similar RPC-type structure. Could be worth taking a look and drawing inspiration.

jlalmes avatar Mar 17 '22 10:03 jlalmes

continued...

  • Moving towards a Slack-style API might make public-facing TRPC APIs easier to use and drive greater adoption (cc. https://github.com/trpc/trpc/issues/755)

jlalmes avatar Mar 17 '22 12:03 jlalmes

is there any example use trpc in vue 3?

skimhugo avatar Mar 24 '22 05:03 skimhugo

Hey. Just came across this upcoming update. I wasn't aware that this update was in the works, and had started work on a similar project. https://github.com/rakeshpai/birdsong It isn't production-ready yet. It is heavily inspired by tRPC, but these are the differences from tRPC 9:

  • Using actual tokens instead of strings to name methods (tRPC 10 takes care of this.)
  • Cmd+click to jump to definition between client and server. (Again, 10 takes care of this.)
  • Handling all the built-in JS types, eg. Dates, Maps, Sets, etc., and not just the primitives that JSON supports, without needing an external library. (tRPC requires an external library for this, but that might be ok for most people.)
  • Easily extending the supported types by writing simple serialiser / deserialiser functions for the new custom types - useful for custom types that don't have internal state, like for eg. Email. (I haven't exposed this currently even in my package, but the design has been done to allow for this.)
  • Being agnostic to the environment (node / deno / CF workers, etc) that the server is going to run in. This has been done by converting node's request and response objects to W3C Request and Response objects at the boundaries, and using the W3C objects everywhere in the code rather than the node built-ins.
  • Automatically detecting if a method's network call should be a GET or a POST rather than explicitly having to specify if the method is a query or a mutation - this has been done by seeing if the method name starts with some known prefixes like get or list. So, getUsers would be a GET request, and updateUser would be a POST just because of how they're named. I'm on the fence about whether this is a good idea. It does simplify the API somewhat, but introduces a little bit of magic and/or forces a convention.
  • Keeping the client-side bundle vanishingly small - it's currently under 5kb before minification and gzip, and IIRC it's under 3kb after minification and gzip. (Aren't proxies magical!) This does forgo many runtime checks, but that should be ok since the TS types are very strict.
  • A functional composition based API, rather than a chaining+router API, for writing the server code. You could put this down to personal preference - a chaining API might be what most people prefer anyway.
  • Simplified imports - there's just one package to be npm installed, and the server and client code are both part of the same package. Client-side bundling + tree-shaking eliminates server code, so there's no leakage of server code into the client bundle.
  • Built-in but opt-in pretty logging on the client-side similar to redux-logger to help with debugging.

I had even more ambitious plans with my package, but some of those have fallen by the wayside for now:

  • Having the same API for communication between the browser main thread and web workers to enable functionality similar to Comlink. The API has been designed to minimise HTTP-specifics to enable this in the future, but I haven't done anything to actually build this out. The end result is that in large client-heavy projects, there's only one RPC API that teams need to learn, regardless of which thread/network boundary they're crossing.
  • Ditto for communication between the main thread and service workers. This is particularly interesting since the semantics are still of making HTTP calls from the main thread, except you run the 'server' inside a service worker. This could potentially open up the doors to building new kinds of apps that run primarily in the service worker, but are painful to do today because of how the sw APIs are. Again, I haven't really explored this yet.

I'm very happy to know that the first two points above, which are the biggest ergonomic differences from tRPC 9, are now going to be handled in 10. With that in mind, I'm happy to abandon my project. However, I thought I'd leave the rest of the list here to see if there's interest in targeting them with upcoming updates to tRPC.

You can see usage examples of my package in the tests. Apologies for not documenting this better.

Edit: Terminology (/s/module/package)

rakeshpai avatar Aug 01 '22 07:08 rakeshpai

Hey @rakeshpai! Birdsong looks great! Here's a response to your points:

Handling all the built-in JS types

I don't see this changing for us. superjson handles all of this as you noted and allows you to have custom types with serializer/deserializer functions.

Being agnostic to the environment (node / deno / CF workers, etc) that the server is going to run in

tRPC v10 can run anywhere and is agnostic to the environment. We cannot use the Fetch API Request and Response yet because they aren't available in Node so we just use a common format for request/response shapes. Also tRPC v10 has a Fetch adapter to convert these shapes to Request/Response objects.

Automatically detecting if a method's network call should be a GET or a POST rather than explicitly having to specify if the method is a query or a mutation - this has been done by seeing if the method name starts with some known prefixes like get or list

We can consider this, but I don't think it's a great idea. I'm not a fan of implicit behavior on this level. In a way, having to specify keywords in front of the procedure names is still explicit. As a side note this was also brought up in https://github.com/trpc/trpc/discussions/2270#discussioncomment-3198442.

Keeping the client-side bundle vanishingly small

We'd love to have your help here! This is something on our radar to look at before release. That being said, the client is kept fairly minimal.

Simplified imports - there's just one package to be npm installed

Yeah, this is also something we discussed. It's not a huge problem right now (compared to the other things we're working on), but we're open to ideas here.

Built-in but opt-in pretty logging on the client-side

Awesome! tRPC has this too with the loggerLink.

Having the same API for communication between the browser main thread and web workers to enable functionality similar to Comlink. The API has been designed to minimise HTTP-specifics to enable this in the future, but I haven't done anything to actually build this out.

You can likely achieve this with a custom link on the client. tRPC has also also been designed to minimize HTTP-specifics. For example, see the WebSocket link and trpc-chrome.


It seems like you've thought about this a lot and we'd love to have you involved in the decision-making for v10. We'd also love to have you as a contributor to tRPC! If you're interested, please join the Discord and we can talk more.

sachinraja avatar Aug 01 '22 13:08 sachinraja