[HN Gopher] TRPC: End-to-end typesafe APIs made easy
___________________________________________________________________
TRPC: End-to-end typesafe APIs made easy
Author : namelosw
Score : 106 points
Date : 2022-05-06 14:50 UTC (8 hours ago)
(HTM) web link (trpc.io)
(TXT) w3m dump (trpc.io)
| jeroenhd wrote:
| Only supports using Javascript/node servers it seems, so sadly
| it's not really usable for me.
|
| The type exchange seems to rely on shared code between the
| frontend and the backend to exchange types, rather than using
| schemas like other systems.
|
| Not implementing server-side events seems like a shame because
| they're a very capable mechanism for subscriptions without
| resorting to websockets.
| goodoldneon wrote:
| If you're using OpenAPI, you could use this to generate
| TypeScript interfaces:
|
| https://github.com/bcherny/json-schema-to-typescript
|
| It works really well
| chrisarnesen wrote:
| TRPC is designed for systems where the front and back ends are
| both TypeScript. In that case it's simpler to have them share
| code than to go through an intermediary type system such as
| GraphQL.
| sachinraja wrote:
| I don't believe there's anything really preventing the use of
| server-side events. The Websocket implementation is via an
| adapter and special client so I think the same could be done
| for SSE (probably achievable in an external package too).
| bradrn wrote:
| I've never used this, but it looks like a very similar idea to
| Haskell's Servant library [https://docs.servant.dev/en/stable/].
| Could someone who knows more enlighten me as to how correct this
| comparison is?
| Vosporos wrote:
| Hi, Servant user here: Haskell's type system is sound, which
| means that you can have type safety. TypeScript's type system
| is unsound, so every guarantee that you may think you have wrt.
| type safety go out of the window at the moment you start the
| typechecker.
| lalaithion wrote:
| Hey, don't be this way. Haskell's type system is _also_
| unsound (see: undefined, unsafePerformIO). Typescript has
| done an amazing job bolting an advanced static type system on
| top of a dynamically typed language. The edge cases of that
| type system exist, and are a bit more prevalent than Haskell
| 's, but there exist a number of libraries in Typescript that
| take inspiration from Haskell libraries. This question is
| about the differences between the design of these two ways of
| statically representing APIs and not about the fundamental
| differences in the type systems of their host languages.
| aranchelk wrote:
| They seem similar. With Servant you start with a type
| definition describing your API (using type-level functions),
| from their docs:
|
| type UserAPI1 = "users" :> Get '[JSON] [User]
|
| In tRPC based on the example it looks like you start by
| defining and instance then grab the type and export it.
| arpinum wrote:
| I'm not seeing the appeal of this over Smithy, which can target
| multiple frontend and backend languages. What am I missing? What
| is wrong with codegen tools? Especially when they help you design
| strong contracts upfront, rather than ad-hoc API design as their
| demo shows.
| janpot wrote:
| Nice! I wrote something similar for Next.js a while ago. The
| pattern is so much more ergonomic than everything else I've used
| before.
|
| https://npmjs.com/package/next-rpc
| gavinray wrote:
| You can do this with GraphQL too:
|
| https://genql.vercel.app/
|
| https://github.com/graphql-editor/graphql-zeus
|
| I did a 5 min talk about these newer breeds of codegen tools
| (where it's a single client SDK that does automatic return type
| inference based on the input args), they're really neat:
|
| https://www.youtube.com/watch?v=7n3MeMFHiMk
|
| (Skip to 2:14 to see the autocomplete/type-safety)
| [deleted]
| hu3 wrote:
| It's not the same though. tRPC requires no code generation.
|
| With tRPC if I change the return type of an API, I get instant
| feedback on my frontend code with errors and autocompletion.
|
| Is my understanding correct?
| sirtimbly wrote:
| Correct! Instead of codegen, it relies on the TS language
| server to enforce schema compliance.
| Vosporos wrote:
| you can't have type-safety with an unsound type system, silly.
| sachinraja wrote:
| Could you elaborate on this? What part of tRPC do you think is
| unsound due to the type system?
| colinmcd wrote:
| TypeScript's type system is unsound in a mathematical sense:
| https://effectivetypescript.com/2021/05/06/unsoundness/
| Though this has little bearing on practicality which is
| probably why the original comment is at the bottom of the
| pile.
|
| He's also using a stricter mathematical definition of "type
| safety" whereas tRPC means it in the colloquial way it's used
| in the TypeScript ecosystem (that is, a fully typed interface
| between client and server, ideally that's non-duplicative and
| inferred directly from your code instead of being manually
| defined).
| markoutso wrote:
| I am really not getting this. If you run Typescript on both ends,
| can't you just share the source code that defines the types?
| hu3 wrote:
| You kinda can share function signatures with TypeScript's
| import type.
|
| And that's what tRPC does but adds some generics in the middle
| to make things seamless.
|
| Explanation here: https://colinhacks.com/essays/painless-
| typesafety
| ricardobeat wrote:
| You could argue that this is actually adding a lot of (nicely
| knit) seams.
| colinmcd wrote:
| I think that's a good characterization!
| colinmcd wrote:
| Basically tRPC lets you define a router containing all your
| endpoints in a single structure. Then you can import the type
| definition of your router and make typed API calls without
| needing to wire everything together with types.
| colinmcd wrote:
| I wrote the initial proof of concept for tRPC a little over a
| year ago (though I'm no longer involved in the project). Seems
| like there's some confusion about how this works. A more complete
| description is available here[0] below but I'll put a brief
| explanation below as well.
|
| - It's designed specifically for a full-stack TypeScript app.
| Your API is implemented as strongly typed server-side functions.
|
| - The input type is specified using a schema library like Zod or
| io-ts.
|
| - TypeScript infers the return type of these functions.
|
| - You can then import the type signatures of your functions into
| your client. Just types, no runtime code! This is safe in
| typescript with the `import type` syntax. tRPC uses those type
| signatures to provide a typesafe client API, basically an SDK for
| your API. There's no codegen happening - the client uses
| TypeScript generics to provide a set of strongly typed "proxy
| functions" that can be "called" on the client. These proxy
| functions execute HTTPs requests under the hood:
| https://trpc.io/docs/rpc
|
| The point is to avoid the boilerplate, codegen, and lack of
| DRYness associated with building a strongly typed API in
| TypeScript. You can do this with GraphQL but it relies on code-
| generation, requires complete buy-in, and (IMO) doesn't provide a
| great developer experience. TypeScript has a native way of
| representing types, so the goal is to avoid an intermediate
| representation (in the form of an OpenAPI spec or GraphQL schema)
| entirely. As you update your server code, the client's type
| signatures update instantaneously.
|
| Another important goal is to make it easy to write DRY code when
| building APIs. Without tRPC, it's common to manually provide type
| definitions for the results of API calls. But those definitions
| can easily get out of sync with your actual server code. tRPC
| infers types from your code itself and provides a typed interface
| for your API thats doesn't require any manual type annotations.
|
| [0] https://colinhacks.com/essays/painless-typesafety
| codethief wrote:
| This is very cool, thanks so much for explaining how this works
| and, of course, for initiating such a cool project!
|
| As far as I can tell, tRPC basically bridges (hides) network
| communication and allows doing remote procedure calls almost
| like regular, type-safe function calls on the same machine
| within the same process.
|
| Now I would give a fortune for having something like that also
| for cross-language communication and for communicating with
| other processes (binaries and shell scripts). I've been
| thinking about this a lot in recent years: In SWE we need to
| jump through many hoops only because it's so god damn hard to
| invoke a function or a whole API living somewhere else in a
| type-safe manner. Intermediate formats like JSON, protobuf,
| standards like REST, OpenAPI, and GraphQL, and even languages
| like SQL (and the various ORM libraries it inspired) are all a
| result of that to some degree: Once data leaves the safe haven
| of your (hopefully statically typed) programming language where
| the compiler/interpreter knows exactly what its type is and
| what the signature of the function is that you're trying to
| call, you're basically on your own.
|
| I recently read about WASM Interface Types[0] and it seems like
| a step in the right direction, though unfortunately it will
| only bridge the - let's call it - _type safety gap_ between
| processes, not between different machines in a network.
|
| [0]: https://hacks.mozilla.org/2019/08/webassembly-interface-
| type...
| oneplane wrote:
| So essentially, it's protobuf but it describes the API instead
| of a message (which arguably is also an API description) and is
| for TypeScript on both sides.
| colinmcd wrote:
| Exactly. But with faster iteration speed, more type
| inference, less overhead, and a TypeScript-native DX.
| lalaithion wrote:
| What's the backwards compatibility story here? What changes to
| backend function argument and return types can I make without
| breaking currently-open webpages when I push a new backend
| instance?
| theobr wrote:
| tRPC has been a lifesaver for us at Ping.gg
|
| Never had such a seamless experience building and deploying "full
| stack" apps. Multiple classes of bugs just, like, don't happen
| now?
|
| Helped build up a lot of the GraphQL tech at twitch, and I still
| love it, but man do I not miss all the work maintaining the
| "contract" between front and back. I know we will have to move
| off tRPC eventually, but I'm hyped at how far it's taken us
| already
| hu3 wrote:
| > I know we will have to move off tRPC eventually
|
| Is that when you can no longer get by with only
| NodeJS/TypeScript on the backend?
|
| I've been thinking about that in a greenfield project I'm
| trying tRPC out.
| hegem0n wrote:
| I love this. This is the future
| jre wrote:
| I like this idea very much. Shameless plug and I don't want to be
| the Rust fanboy, but I've played with something similar in Rust:
|
| https://github.com/julienr/liveboard-rs
|
| Basically it uses actix for the backend and yew (Vue-like rust
| frontend framework) for the frontend. This enables one to share
| types (and helper functions) between both, which is great:
|
| https://github.com/julienr/liveboard-rs/blob/master/shared/s...
|
| That being said, I think maturity-wise, Typescript is probably a
| better bet for this right now, so I'll definitely look at trpc
| for $dayjob.
| eandre wrote:
| If you want a similar experience to this but more optimized for
| backend development using Go, I've been building
| https://github.com/encoredev/encore for the past few years!
| tantalor wrote:
| > without schemas
|
| What is a type definition if not a schema?
| sirtimbly wrote:
| Conceptually, yes. But type definitions are exported by
| declarative lines in the same code that runs. Basically the
| schema is imperative code that runs actual validations, the
| tRPC methodology simply looks at the inferred types from that
| code to enforce consistency between client and server rather
| than parsing an actual static schema artifact (e.g. a .gql
| file).
| kristiandupont wrote:
| I recently gave a talk about our experience with tRPC. So far
| it's looking very promising!
|
| https://www.youtube.com/watch?v=k1TCueEhhJo
| evantahler wrote:
| This is one of the best things about typescript. You can do this
| kind of thing without much special tooling at all -
| https://www.evantahler.com/blog/post/2020-10-16-typescript-f...
| lowwave wrote:
| interesting. Kinda like fastify json schema.
| [deleted]
| vander_elst wrote:
| How does this compare to gRPC? That the wire protocol is actually
| http+json instead of binary? I wasn't able to find much on their
| website
| sachinraja wrote:
| tRPC offers typesafety across your frontend and backend without
| needing to generate any code. This makes it so updates in your
| backend "router" are immediately reflected in your frontend
| code. You can't use gRPC with TypeScript without codegen. tRPC
| may be more familiar to TS/Node.js devs too.
| awhitty wrote:
| This project looks interesting! I have used a similar project
| called tsoa [1] and have appreciated that it generates an OpenAPI
| spec that can be consumed readily by many other clients, even
| outside the JS ecosystem. Does TRPC have plans for a similar
| feature set?
|
| [1] https://tsoa-community.github.io/docs/
| alexdotjs wrote:
| Yup, with the optional `output`-property we actually have all
| the type information needed to generate an openapi-schema.
|
| See this for more info:
| https://github.com/trpc/trpc/issues/1724
| mhoad wrote:
| Not looking to actively bash anyones work here but I'm really
| confused on the value proposition here over something like gRPC
| which has a lot of substantial advantages from speed, to wide
| cross language support and has been battle tested in the most
| extreme scaling scenarios possible.
|
| A truly huge amount of time, money and optimization went into a
| project like that and has been proven to work well for years now.
|
| The JS ecosystem confuses me with things like this regularly just
| the other day I was watching a video where the lead devrel person
| from Vercel was trying to explain "edge functions" to a senior
| backend engineer that started falling apart under the most
| rudimentary questioning so much so that in the video they have
| this awkward hard cut and they rush to wrap up the interview.
| Link if anyone is interested https://youtu.be/yuxd2kurpzk
|
| This gives me kind of similar vibes in that I just don't really
| get the use case here unless it's to intentionally stay strictly
| in the JS ecosystem for some reason?
| stickfigure wrote:
| The docs don't say anything about the wire protocol - is this
| based on REST/JSON, or something proprietary? If proprietary, is
| it spec'd and documented? Support for implementations in other
| languages?
| tcbyrd wrote:
| https://trpc.io/docs/rpc
| alexdotjs wrote:
| Awesome to see my lil' baby on the frontpage!
___________________________________________________________________
(page generated 2022-05-06 23:01 UTC)