14 ms·
tRPC – Build and consume typesafe APIs without schemas or code generation
- pimpl 3y agoI've used tRPC and Next.js for a couple of personal projects and it's been a great experience. Hard to beat on iteration speed, especially when used with a pre-configured template like Create T3 App: https://create.t3.gg/ https://create.t3.gg/.
- Void_ 3y agoDoes it now work properly with Next 13 and server components?
- pimpl 3y agoI think patterns and best practices are still yet to be figured out. I believe you can create caller in a server component and it should work (https://trpc.io/docs/server/server-side-calls#create-caller https://trpc.io/docs/server/server-side-calls#create-caller), but the pages router appears to be a battle-tested solution.
- aCoreyJ 3y agoIn server components you can just await in the component itself so you don't need a solution like tRPC.
- tough 3y agoCould you expand on this? You don't need it, but you certainly can prefer to use it regardless? Hoping react-server-components <> trpc gets solved soon
- ushakov 3y agoIf you use Server Actions, you can just call a server function from your frontend directly, no need for middleware like tRPC https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions https://nextjs.org/docs/app/building-your-application/data-f...
- tough 3y agoThank you I only briefly read about them before and didn't consider them properly. Will try out calling the database directly there, but I kinda liked how with tRPC I can add validation/auth checks etc/ will have to see how I develop my own strategy for that w server actions.
- ushakov 3y agoYou can use zact for that https://github.com/pingdotgg/zact https://github.com/pingdotgg/zact
- welder 3y agoI'm using it with Next 13, but not using the new experimental app folder yet. No issues yet.
- austin-cheney 3y agoI am up voting this because its a good idea and you have really nice web site. I wrapped up something like this myself a few weeks ago, so I am in a position to offer some suggestions other people may not think about. The most common mistake I see with RPC, especially WebSockets, using Node.js is that they must be based off of HTTP. My attempt is based upon WebSockets, RFC 6455, where RPC is a generic term for socket based communication streams not specified against any single frame definition scheme. Since these technologies are raw sockets that include their own conventions for handshakes, optional frame header definitions, and possible security conventions there is no need for HTTP. In the OSI model HTTP is layer 7 where TCP sockets are layer 4. In Node that means just using the net/tls libraries opposed to the http based descendant libraries. Since, in Node, the http library descends from from the net library and https descends from tls which descends from net http always imposes overhead that TCP based sockets do not require. There are three benefits for executing a socket server over HTTP: 1) You are only using port 443 2) Simple and familiar implementation from Node 3) HTTP 1.1 is session-less, which allows anonymous untrusted connections. That is how the web works, but its less ideal for a security focused implementation. The reasons to not do this include CPU cost. Running sockets over HTTP increases processing overhead which reduces the number of concurrent streams you can offer and substantially slows down processing of incoming frames. In order to reduce your execution to a single port without sacrificing performance you could run HTTP over your socket implementation which allows you to customize your approach to security, but that would also require writing your own HTTP libraries.
- littlecranky67 3y agoMy issue with websockets is, they are still being blocked in corporate proxies. I particualarly see this in banks or research-places, where sockets are blocked to prevent data leaking out of the network.
- austin-cheney 3y agoI used to work at a giant bank. The reason they might be blocked by big banks is due to deep packet inspection. How that works is that the bank intercepts certificates on TLS socket establishment for normal web traffic so that the bank proxy becomes a formal "main in the middle". They do this to provide deep packet inspection on all encrypted traffic that goes out and comes in. That level of packet inspection is more challenging with something like RPC/WebSockets because its a binary stream, where HTTP just uses plain text for its header data. I can remember using Reddit, when I still used Reddit, when starting at the bank and I remember Reddit making heavy use of WebSockets that worked just fine from within the bank even though I was behind the bank's proxy. This was more than 6 years ago, but I believe the WebSocket traffic at that mega bank was just relayed through proxy just like the HTTP traffic and I also want to say Reddit served the WebSocket traffic different from the HTTP traffic, but I cannot remember for sure.
- hobofan 3y agoend-to-end (as long as the backend is Node.js)
- worksonmine 3y agoDoes end-to-end have to be agnostic to the backend to you? Do you feel the same way about end-to-end encryption? Would you say "end-to-end encryption as long as the backend is rust/go/c"? Why not, what's the difference? Most projects exist in a ecosystem, and in this case using the same language on both ends makes total sense don't you think? They pull from the same repository, use the same language servers, and run mostly the same code. Why is that a problem?
- hobofan 3y agoUntil very recently single-language frontend + backend stacks were not the norm. As tRPC is in competition with solutions like GraphQL or REST APIs where the backend can be implemented in a big number of languages, I thought that limitation was worth pointing out.
- jitl 3y agoWe’ve use an API style similar to tRPC at Notion, although our API predated tRPC by 4 years or so. You can build this kind of thing yourself easily using Typescript’s mapped types, by building an object type where the keys are your API names, and the values are { request, response } types. Structure your “server” bits to define each API handler as a function taking APIs[“addUser”][“request”] and returning Promise<APIs[“addUser”][“response”]>. Then build a client that exposes each API as an async function with those same args and return type. We use this strategy for our HTTPS internal API (transport over POST w/ JSON bodies), real-time APIs (transport over Websockets), Electron<>Webview APIs (transport over electron IPC), and Native (iOS, Android)<>Webview APIs (transport over OS webview ipc). For native APIs, the “server” side of things is Swift or Kotlin, and in those cases we rewrite the request and response types from Typescript by hand. I’m sure at some point we’ll switch to a binary based format with its own IDL, but for a single cross-language API that grows slowly the developer experience overhead of Protobuf or similar hasn’t seemed worth it.
- purkka 3y agoIn our current project with a TS frontend and Python backend, we use an OpenAPI schema as the source of truth and openapi-typescript-codegen [0] to interface with it on the client side. While not perfect, it provides a very nice interface to our API with request/response typings. I also wrote a 10-line mock API wrapper that you can call as mockApi<SomeService["operationName"]>((request) => response), and it will type-check that your mock function implements the API correctly and return a function that looks exactly like the real API function. [0]: https://github.com/ferdikoomen/openapi-typescript-codegen https://github.com/ferdikoomen/openapi-typescript-codegen
- iansinnott 3y agoCan second this approach. At a past job we did the same, except to connect the frontend to a Go backend. I really like that the openAPI approach is language agnostic, and makes it relatively simple to support SDKs for many other languages if needed. For any company where the API itself is a product, OpenAPI is great.
- aloukissas 3y agoCan highly recommend over plain REST as an alternative to graphql. The iteration speed with instant feedback is incredible. Perfect pair with NextJS.
- rwieruch 3y agoI have used tRPC for two ~50k loc web applications and love it. The DX is incredible. But I feel like tRPC had its hype time some time ago, so RSC’s hype quickly caught up. These days everyone speaks about whether to use or not to use RSC while there is such a great stable solution like tRPC out there. I am not against RSC, but there is way too much discussion about it. tRPC is a super pragmatic way to create applications with these days! Edit: RSC = React Server Components - which come with their own read/write data philosophy.
- candiddevmike 3y agoWhat does the Royal Shakespeare Company have to do with RPC? (or please define/link acronyms folks may not know!)
- thowrasdf23 3y agoI'm guessing React Server Components which is apple to oranges with tRPC.
- rwieruch 3y agoit is. But RSC make many library authors and maintainers question whether their library is still needed in a RSC world and if so how they can support it. Same story with tRPC https://github.com/trpc/trpc/issues/3297 https://github.com/trpc/trpc/issues/3297
- Takennickname 3y agoI think it makes people wonder whether rsc are needed, not the libraries
- ianlevesque 3y agoFor what it’s worth I have never heard of RSC and it’s not googleable. Have a link?
- 3y ago
- betimsl 3y agoIs there something like this but for JavaScript?
- denysonique 3y agoTelefunc is another alternative without the boilerplate where on the frontend you can just import and execute the backend functions remotely. https://telefunc.com/ https://telefunc.com/
- AlexErrant 3y agoCompared with tRPC: https://github.com/brillout/telefunc/issues/9 https://github.com/brillout/telefunc/issues/9
- 0xDEF 3y agoDoes anyone know if it's possible to build the ergonomics and DX of tRPC on top of gRPC-Web? tRPC is TypeScript/Node.js-based while gRPC-Web backends can be built in most languages.
- idbehold 3y agoDX for front or back end? The beauty of tRPC is that the types are derived/inferred from the backend runtime code (like, as you type). It would be nigh impossible to do that with grpc(-web) using proto files as the source of truth. It's possible there's a project out there which could automatically produce proto files from something like zod, json-schema, etc. which could be directly interpreted by TS to provide similar (as you type) DX while still allowing some other language backend to consume the derived proto files (though the DX there would be less than ideal). If you're just looking for similar TS clients/interfaces for grpc-web then I'd recommend https://github.com/timostamm/protobuf-ts https://github.com/timostamm/protobuf-ts which operates on plain JS objects (no new MyMessage().serialize(), instead the code generator mostly produces TS interfaces for you to work against: const myMessage: MyMessage = pojoConformingToInterface; const binary = MyMessage.toBinary(myMessage);)
- colesantiago 3y agoOK, looks great but as a manager and founder of a company that is looking into sponsoring OSS, how does this make me more money?
- hu3 3y agoI'll bite. With tRPC, changing an attribute of a class in your backend immediately tells you what broke in the frontend in your IDE. No need to recompile or regenerate anything. Also, when developing your frontend, you'll have immediate autocomplete. Again without code generation or compilation. That alone should save you dev time and prevent a ton of frontend bugs.
- threatofrain 3y agoWouldn't optional code generation be rather desirable? Then you don't need a monorepo, which a lot of people don't do.
- marksomnian 3y agoIt slows down the iteration cycle - when you make a change to your API definitions you'd need to push the change to either a schema repository or into production, then regenerate the API - at which point, if you've accidentally introduced a breaking change, your frontend is already broken - you can work around this with CI that detects breaking changes, but it requires a fair amount of work. Having them in a monorepo means that breaking API changes can fail CI and never make it into production.
- deleted 3y ago[deleted]
- weird-eye-issue 3y agoI've had lots of bugs and types would have very very rarely fixed them
- clintonb 3y agoOkay…go fix your non-type-related bugs.
- weird-eye-issue 3y agoThey are things that pop up during production usage from primarily logic errors...
- jensneuse 3y agoI'm a big fan of tRPC. It's amazing how it pushed TypeScript only stacks to the limit in terms of DX. Additionally, it made the GraphQL community aware of the limitations and tradeoffs of the Query language. At the same time, I think tRPC went through a really fast hype cycle and it doesn't look like we're seeing a massive move away from REST and GraphQL to RPC. That said, we see a lot of interest in RPC these days as we've adopted some ideas from tRPC and the old NextJS. In our BFF framework (https://wundergraph.com/ https://wundergraph.com/) we've combined file based routing with RPC. In addition to tRPC, we're automatically generating a JSON Schema for each operation and an OpenAPI spec for the whole set of operations. People quite like this approach because you can easily share a set of RPC endpoints as an OpenAPI spec or postman collection. In addition, there are no discussions around HTTP verbs and such, there's only really queries, mutations and subscriptions. I'm curious what other people's experiences are with GraphQL, REST and RPC style APIs? What are you using these days and how many people/teams are involved/using your apis?
- c-hendricks 3y ago> it made the GraphQL community aware of the limitations and tradeoffs of the Query language Could you expand on that? Our graphql types are generated from our federated schema whenever it changes, and response types for queries / mutations are generated whenever you save a file.
- jensneuse 3y agoWith tRPC and similar frameworks, you infer client types from server definitions without including actual server code into the client. As much as I like GraphQL, one thing that IDEs really suck at is recognizing when generated code changes. E.g. with Jetbrains IDEs it sometimes takes forever until a change to the generated code is actually picked up by intellisense. VSCode is a little bit better regarding this. When you infer types in tRPC style, this whole problem is gone. You can even jump between the client usage and the server implementation. That said, this is not without a cost. Large Typescript codebases can slow down the typescript autocompletion and this approach only works best when both client and server are written in typescript and ideally in the same codebase.
- cyco130 3y agoIf you wanna take the concept up a notch: https://rakkasjs.org/guide/use-server-side-query https://rakkasjs.org/guide/use-server-side-query
- nerdponx 3y ago> When it runs on the client, variables from the surrounding scope that you use in the server-side function are captured, serialized, and sent to the server. Since anyone can send any request to the server, you have to validate everything that comes from the surrounding scope. This is mildly horrifying to consider without good tooling support.
- cyco130 3y agoOn the next iteration, we're planning on providing a `createServerQuery` function which will _not_ capture the closure and return a function that can take an arbitrary number of arguments to be serialized. `createServerQuery` will have a required `validator` option that will also run on the server to validate those arguments.
- wruza 3y agoSurprised this thread isn’t full of scary RPC stories and SOAP CORBA under your bed at night. Either the wave is gone or it’s just a little early.
- bborud 3y agoI'm not too surprised. Most people for whom TypeScript is an option won't have been exposed to generations of RPCs that fail to deliver simplicity. So it isn't going to be on their radar. I've been using gRPC and REST'ish+JSON APIs for years now and what I find puzzling is that REST+JSON tends to mean a lot more work, less pleasing code than gRPC, and yet people prefer it. Not because it leads to simpler, better, faster less error prone code (it doesn't. Quite the opposite), but, I think, because people feel they can understand it. The people over at https://buf.build https://buf.build have been doing a great job trying to tame gRPC btw. The protoc toolchain from Google has been uniquely horrible to work with, but the 'buf' tool from said company has been a real life-saver. Not to mention their Buf Schema Registry, which admittedly I have only used on a few projects so far, but should migrate more projects to. Though in general, I feel that RPC mechanisms that are too closely tied to a given language are a waste of time. But that's me. tRPC isn't something I'd get into even if I was doing server side TS. It just doesn't seem like a good long term choice.
- makeitdouble 3y agoPeople sticking with REST+Json usually don't want to have a compilation step for the API exchanges layer. That means more validation and worse tooling, but also better legacy and potentially future compatibility, and way easier debugging at any stage. I feel this is the same debate between scripting languages and compiled languages, both provide different trade-offs and I don't think we'll see one completely disappear at any point in time.
- bborud 3y ago> People sticking with REST+Json usually don't want to have > a compilation step for the API exchanges layer. Hmm. I'm not sure what you are saying. The way people tend to try to consume REST APIs is by generating client (and/or server code) from a spec. For instance OpenAPI 2.0. At least on Go the tooling for that leaves something to be desired, and the generated code isn't exactly beautiful. So you either depend on a library that someone generates from the spec, and then shares, or you generate it yourself. (We do this for a bunch of languages for our APIs, and the clients are ... not uniformly beautiful :-)) If we're talking about writing the REST client by hand...well...I'm not sure why one would want to do that. (Nor am I sure that's what you meant, so I'm not accusing you of that). My current workflow (in Go) for consuming gRPC interfaces is to use the buf.build package proxy. Which means I just add an import, run go mod tidy, and I'm good. I don't even need the tooling installed. As I think it should be. I suppose this could be done for OpenAPI 2.0 too. You stuff OpenAPI 2.0 specs in and you get code for a bunch of languages out. (Someone must surely have built this already?)
- edem 3y agoI was really hyped about tRPC until I started using Remix I still think tRPC is great and for most projects you don't need more (specifically GraphQL).
- pjmlp 3y agoRPC IDL keeps being reinvented by those that fail to understand where we came from. Pop fashion industry.
- nerdponx 3y agoOpenAPI, JSON-RPC, and JSONSchema have existed for a pretty long time now. The industry has more or less standardized on JSON for data interchange and parsers/generators for JSON abound in every programming language, so it makes sense to do all this stuff with JSON. I've never seen IDL before but I looked up some examples and it does seem useful for RPC calls. But I'm not exactly eager to switch, it looks like it's meant for a much more powerful form of RPC than I'm willing to touch, and no tooling I know of supports it. The JS ecosystem of course is still affected by hipster disease (everyone seems to think their own wacky idea is groundbreaking). But the existence of a framework like this, built on well-established JSON-based standards shouldn't be surprising.
- bborud 3y agoIDL in this context is a generic term that stands for Interface Description Language. I think you kind of made his point for him.
- pjmlp 3y ago> I've never seen IDL before but I looked up some examples... Proving my point. Search for Sun RPC, DCE RPC, XDR. Take note when they came up, and everything in between until today.
- nerdponx 3y agoYes, but those systems have been obscure and uncommon for longer than a lot of today's programmers were even in the field. In the intervening time we all standardized on JSON and rediscovered or reinvented similar concepts all using JSON. Fine. It's great to understand and learn from prior inventions, but it's not like we're all going to switch back to IDL. Consider that JSON being ubiquitous immediately makes it easier to adopt compared to a custom description language. If people actively used IDL today, there would probably be a lot of demand for a JSON variant or subset. I'd make similar arguments about using JSON vs S-expressions for data interchange, but JSON works well with both Javascript and HTTP and everyone standardized on those, and maps cleanly to basic data structures in just about every modern programming language. These JSON-based tools are actually very much like Lisp in that both the interface specification and the data are expressed in exactly the same format/language. This is not true of a lot of these older standards, and seems to have been the failed promise of XML. IDL does look like it maps nicely to typed function calls in most languages, but it lacks the advantage of being expressed in a standard format/language that is already well-supported for other tasks, and seemingly doesn't impose any requirements on how the data itself is transmitted. For an example of why language/format matters, consider the tool c2ffi (https://github.com/rpav/c2ffi https://github.com/rpav/c2ffi). It generates a JSON description of a C header file. The header file itself is a pain to parse and extract information from. But once you have a tool to do it and put that information in a standard format, you can now build an FFI wrapper in just about any other language in at least semi-automated fashion. It makes the easy parts easier, compared to other systems like SWIG and GObject where the interface format is totally custom and you're mostly reliant on a single implementation to make it all work. If anything, let's be grateful that the good ideas of the past are being rediscovered and reinvented in a way that might grant them more longevity and broad usefulness than they had in their first life. Did you use IDL? What was your experience like? How would you compare it to something like gRPC?
- Timon3 3y agoIs there some way to reduce message size in WebSocket scenarios? It looks like it uses JSON-RPC under the hood with strings for message names etc. I'm looking for ways to improve the throughput of an embedded WS server. One idea I had was for clients to negotiate a number-based encoding for these parts to reduce all unnecessary traffic. Is there any way to implement this with tRPC?
- chaosite 3y agoWhy not just gzip the data? It might not beat a bespoke hand-crafted protocol that minimizes message size, but your use of the word "negotiate" makes me think that that's not what you're taking about...
- Timon3 3y agoThe embedded device the server is running on has a very weak CPU. Since I'm trying to optimize throughput this would have quite an impact. It's fine if the first few messages are slow, the speed just has to pick up after initialization is done, which is why negotiation is okay for me. So e.g. the client requesting IDs for every method on first connection would be fine, and would keep complexity down.
- marksomnian 3y agoI imagine you could write a custom tRPC link to send the data over something like MQTT which may be better for an embedded client - there's prior art in the likes of electron-trpc, showing how tRPC could be adapted to non-HTTP transports. Not sure how that interacts with the JSON-RPC bits of the subscription protocol though, if there's no way around it then plain MQTT may be better suited to your use case.
- Timon3 3y agoThank you for the suggestions! I am specifically looking to optimize frontend-to-server communication, so WebSocket is a must. Is there some way I could inject a custom transform function between TRPC and the WebSocket? I could copy the existing WebSocket link and add my transform in there, but it would of course be easier if I could just do my own wire handling with the existing code. Essentially I'd like to inject a custom respond[0] encoder and a custom parseMessage[1] decoder, and same for the client. [0]: https://github.com/trpc/trpc/blob/main/packages/server/src/adapters/ws.ts#L123-L129 https://github.com/trpc/trpc/blob/main/packages/server/src/a... [1]: https://github.com/trpc/trpc/blob/main/packages/server/src/adapters/ws.ts#L284 https://github.com/trpc/trpc/blob/main/packages/server/src/a...
- deleted 3y ago[deleted]
- janosd 3y agoI find it really sad that these efforts always stop at working in language X, but no formal specification exists that would make it possible to create an interoperable version in a different language. I love TypeScript, but for various reasons one may need a different backend language on the server side. Yes, OpenAPI is a thing, but I have yet to see an OpenAPI spec + code generator that works out of the box and doesn't need a whole lot of fiddling and workarounds. I also understand that it's hard to create something truly interoperable, my previous job was building a usecase-specific typing system, but adding yet more single-language ?RPC implementations isn't really helping. Obligatory XKCD reference: https://xkcd.com/927/ https://xkcd.com/927/
- marksomnian 3y agoI think part of why tRPC shines is because it's tightly coupled to TypeScript (and especially Zod, its schema validation library of choice - many of its features map 1:1 onto TypeScript concepts that don't exist in many other languages), which means it can avoid many of the issues that OpenAPI generators have. I'd also like to see a good TS-first OpenAPI client - Fern [0] is probably the closest I've seen. In general in my experience, when you take away the constraint of inter-language interop, you get much smoother DX because you can take advantage of language features. A good example would be the lack of native date/time types in JSON - valuable for interop because different languages handle dates differently, but painful when you're going to/from the same language. Web applications are a special case, because the client-side is effectively constrained to either JavaScript or WebAssembly (except you'd still need at least some JS in the latter case), so it follows that you'll get the best DX if you have JS or TS on both sides of the stack, especially if you can set up your project in a way that lets you share code between both. Not always an option, but I've always felt more productive (as a full-stack dev) when I've been using TS on both the client and server, compared to TS on the client and another language on the backend. [0]: http://buildwithfern.com/ http://buildwithfern.com/
- dsinghvi 3y ago:wave: Hey Mark -- I'm one of the primary contributors to Fern. +1 to your comment about how needing to support multiple languages results in not being able to leverage certain language specific features. We've tried the best to manage the trade-offs here, but there's a limit to what you can do. If you have feedback on how we can improve the TypeScript client, feel free to comment here or create an issue on our repo (https://github.com/fern-api/fern https://github.com/fern-api/fern)!
- todotask 3y agoI wish tRPC could interact with Go app but that's limited to TypeScript.
- kandros 3y agoTake a look at https://github.com/pacedotdev/oto https://github.com/pacedotdev/oto
- barelysapient 3y agoI'm using oto on a personal project and really enjoying it. In my case, we generate a Typescript API client for a React app. Very nice companion to Golang.
- blascsi 3y agoI've just started using tRPC, and I'm in love with it so far! I use GraphQL at work, but I always felt like the boilerplate is too much for my hobby projects, and I wasn't really happy with the code-first frameworks that I could find (especially with the ones that support proper Relay integration). Thanks a lot for the creators! Also if anyone is enticed by this, I highly recommend trying it out!
- ushakov 3y agoGarph has no boilerplate and comes with Relay out of the box: https://garph.dev https://garph.dev
- blascsi 3y agoOh yeah, I've heard about it but by that point I was using tRPC which fits my needs for now. I'll be looking at it later though when I'll need a GraphQL api! Thanks for letting me know!
- JSavageOne 3y agoI've personally been using ts-rest for my projects, which is traditional REST but allows one to autogenerate types and API docs from a zod schema. tRPC seems cool, but seems riskier to go with an alternative to REST or GraphQL, especially if you intend to make your API public and/or have multiple consumers of your API.
- zukzuk 3y agotRPC is not at all intended to ever be a public API, the way REST or GraphQL is. It's meant to be a tight, private connection between your client and your server. If you use tRPC in your stack but want a public API, you will almost certainly need to build that out separately, via traditional REST, or GraphQL, or maybe gRPC. This feels all kinds of wrong initially, and there are some obvious and not so obvious disadvantages to this, but honestly it's not all bad when you get down to it.
- tough 3y agoAt an open source company I worked briefly, we used tRPC, and I was tasked with making the Enterprise API, We went with a nextjs app, abstracted away our tRPC routers into a package and our monorepo, and used tRPC different routers both from our webapp and the API apps. This works great! You end up not repeating your logic all over the place, and make thin wrappers on your nextjs api endpoints or whatever to handle the differences between implementations
- t1mmen 3y agoI’m also singing ts-rest.com’s praise these days. Admittedly a small project with <25 endpoints atm, but holy crap, I am saving SO MUCH TIME on implementations, documentation, and bug hunting. I have separate packages (in a monorepo) for the “contract definition” and the “API implementation” (which is currently NextJS, but likely Express in the future — and the migration path looks like very smooth sailing) Request/response validation, OpenAPI spec, fetch & react-query clients without any additional effort. Mocks for tests auto-generate based on contract definition as MSW. It almost feels too good to be true.
- spion 3y agoSo what do you do when you decide that you don't want to use JavaScript anymore on either side of tRPC? (switch to something else on the server, or write a native mobile app, etc)
- zukzuk 3y agoUse TypeScript instead? (semi-joking) The way I've been approaching this lately is TypeScript on the frontend, then a thing TypeScript layer on the backend (via Deno), with those two pieces connected with tRPC. The real backend guts are in Rust (or whatever), and the backend tRPC layer talks to the Rust stuff with gRPC. So something like this: [(TS web client) <--tRPC--> (TS thin backend)] <--gRPC--> (Rust service) This is a bit awkward, but honestly worth it for what you get with tRPC. One thing that took some getting used to is with tRPC the line between "client" and "server" gets blurry, which makes me uncomfortable for all sorts of reasons but in practice works well enough to make it not worth worrying about for now.
- RadiozRadioz 3y agoWhat's the point of the middle layer? Seems like an extra step and a language change for no reason. Why not just expose HTTP from your Rust stuff?
- zukzuk 3y agoBecause you lose all the stuff that’s nice about tRPC. The experience of building a tRPC app is very different from building an app that talks to a traditional REST API. The front and back end with tRPC are very tightly bound. In a way the backend part of your tRPC app becomes the real consumer of your actual API.
- RadiozRadioz 3y agoOkay. I see 3 main benefits of the tRPC experience. 1. You don't need to manually write HTTP routes on the server side. 2. You don't need to manually write request code on the client side. 3. There's a type contract between both. I don't see how this "thin server" approach helps with any of these. 1. You're no longer writing HTTP routes, but instead you're writing gRPC. You haven't eliminated the work, you've just changed the technology. If you happen to be integrating with a pre-existing gRPC deployment, why re-invent the wheel with custom TS code instead of using one of the many gRPC->HTTP transcoders? 2. Modern web frameworks have so many abstractions on this pattern that it's a non-issue at this point. 3. Your thin server is effectively a mapper from gRPC to HTTP endpoints. That's the type of thing you can build a spec from. If you've used a transcoder and have a spec, you can codegen your client library with the correct types. I think tRPC works best for making highly cohesive full stack apps. Using it as a middleman for your backend seems weird to me.
- 0xblinq 3y agoI’m 99% convinced this is one of those things that will quickly go out of fashion and leave behind thousands of projects using “that legacy fox thing the previous devs wanted to use”
- deleted 3y ago[deleted]
- alexdotjs 3y agoHeya! Creator of tRPC here, just wanted to drop by and say thanks for all the love ♥
- kanarian 3y agoyou are awesome!
- tough 3y agoHey Alex, I had the luck to learn about tRPC directly from you when working at an open source company that used it for a while. I quite remember tRPC being the only unknown part of the stack when I started there, and I was a bit scared, but tbh it was pretty easy to pick up and it's an awesome abstraction to make the server of fullstack codebases in typescript. I always liked GraphQL but if you're working solo it doesn't make that much sense to have the GraphQL api as contract. With external devs a little bit more. +1 to OpenAPI and being able to generate code, SDK's, docs, automagically.
- Trufa 3y agoHey dude! We met in the prisma conf in Berlin a couple of years ago and had some beers after with an Aussie dude, since then I’ve used nothing but tRPC in all of my projects and couldn’t be happier, thanks for the hard work!
- johnmorrison 3y agoThank YOU Alex! This project has done incredible things for TypeScript
- dfee 3y agoThis is the first time I’ve even seen an emoji in an HN comment. What?! Unicode Character “♥” (U+2665) ♳
- ayewo 3y agoHow about emoji in a HN submission? https://news.ycombinator.com/item?id=36159443 https://news.ycombinator.com/item?id=36159443
- candiodari 3y agoCan I just say ... sigh ... I miss 1) Java remoting itself 2) GWT's "emulation" of java remoting to the client side It was SO very, very fast (and safe) to add a new backend interaction. Plus I loved that you could be so evil as to just serialize a java class, send it over RPC, to be executed on the other side. Security nightmare (even though it was fixable if you really wanted to), but damn, you can do absolutely everything with that.
- wb14123 3y agoOf course you don’t need schemas or code generation if you are only targeting one language.
- cco 3y agoI hadn't heard of tRPC until a developer I was working with raved about it and it just seemed like such an obvious good after I learned about it. We built a T3 app (tRPC, Next.js, Tailwind, TypeScript, Prisma) together (if you'd like to check it out https://github.com/stytchauth/stytch-t3-example https://github.com/stytchauth/stytch-t3-example). Type safe APIs while working in TypeScript is just so helpful.
- johnmorrison 3y agoI love tRPC, it's by far the best fullstack DX I've ever seen and has such a brilliant API especially when combined with Zod. Zod and tRPC are some of the most important projects in the future of TS imho, I think we're gonna see a beautiful bloom of tRPC inspired DX across the TS space in coming years. Two projects already have clearly have tRPC DNA attacking different use cases are Ping's UploadThing (https://github.com/pingdotgg/uploadthing https://github.com/pingdotgg/uploadthing) and our Lusat (https://github.com/lusatai/lusat https://github.com/lusatai/lusat).
- matthewfcarlson 3y agoI also recent did a personal project that uses tRPC and Zod and I agree that it was a fantastic experience. It also makes writing unit tests way easier.
- epolanski 3y agoI would also recomment effect/core + effect/schema, the pattern of creating typed services starting from schemas is there perfect for people that are more functional-programming leaning.
- johnmorrison 3y agoI saw that a while back, but it seems like a large learning curve and I don't immediately feel much drawing me towards it. Will be keeping an eye on it anyhow.
- MrJohz 3y agoDo you know of anything in this region for MongoDB? The two big problems there seem to be type-safe data CRUD, but also migration of data. I see people referencing Mongoose a lot, but that seems like it's a big downgrade from Zod/TS in terms of type safety.
- killthebuddha 3y agoI've never used it for Mongo, but Prisma is a pretty great type-safe ORM that supports Mongo: https://www.prisma.io/docs/getting-started https://www.prisma.io/docs/getting-started.
- rendaw 3y agoThis site used up 2gb of memory when I opened it, and apparently I was near the brink.
- ilyt 3y agoSo is it just TS to TS ? If it is same language I just had common library implementing all the types for server and client, no need to get any more fancy than that, crossing the language barrier is the problem for type safety.
- haney 3y agoI'm currently in the process of removing tRPC from our codebase. It's been a nightmare of tight coupling. I've also found that it encourages more junior developers to not think about interfaces / data access patterns (there's a mapping from Prisma straight through to the component). It's fantastic for rapid prototyping but really paints you into a corner if you ever want to decouple the codebase.
- macrael 3y agoIt's a dangerous anti-pattern to pass your db types through to your api handlers. Those are always different models and it's important to have an intermediary representation for the rest of your domain.
- calebio 3y agoAgreed. Although, sometimes it feels like this is a losing battle. I've worked with too many people who see that level of separation as "unneeded duplication", with constant complaints about having to update a bunch of different layers "just to add a new field. IMO, at a minimum, you have your API layer model, your internal model, and your database model with a mapping layer at each boundary. I rarely have problems when I structure it that way, but when working on applications that pass the same model from their API down to their database, or vice-versa, it's always full of the same types of problems that comes with tight coupling.
- senorrib 3y agoThis is exactly my experience. Glad to see I'm not alone, sad to hear that it's so common out there.
- macrael 3y agoI think there are certain types of boilerplate that used to be truly onerous that are now much less so because of types. If there are 3 different files that need to be updated to add a file, and forgetting one is only going to error at runtime (maybe even only if you're exercising that new field) that's horrible. But 3 files that require updating where the code won't compile until you've added all three is way way less of a problem.
- marko424 3y agoIf anyone wants to split, make backend and frontend repositories separate, take a look - https://github.com/mkosir/trpc-api-boilerplate https://github.com/mkosir/trpc-api-boilerplate
- anon7331 3y agoIs it just me, or is this over engineered? Brings me back to SOAP services and WSDLs.
- marksomnian 3y agoDropping in a tRPC use case that I've really got a lot of mileage out of: communication between the Electron main and renderer processes using https://github.com/jsonnull/electron-trpc https://github.com/jsonnull/electron-trpc. Traditional Electron IPC is hard to do type safely, which electron-trpc solves, and the react-query integration (meaning you get automatic type-safe hooks to issue the requests) is really nice.
- JasonSage 3y agoHey there, author of electron-trpc here. I'm always happy to hear that folks get value out of the integration and have a good time using it. Thanks for the mention!
- igeligel_dev 3y agoLove tRPC. I have built two side projects [1][2] now with it and its just so smooth. When I introduced the tool to some other people I worked with, they were amazed on how fast it is. Usually it takes some time to create frontend schemas from backend endpoints but tRPC is just so fast. Thanks to the creator. Literally made me a more efficient developer! [1] https://hackathon.camp/ https://hackathon.camp/ [2] https://sheetsinterview.com/ https://sheetsinterview.com/
- skybrian 3y agoI'm wondering how they handle version skew and migration. Fields have a lifecycle. When they're introduced, no clients or servers know about them. Clients and servers aren't restarted all at once. They won't have the same version of the types. Data will be written using one version of a type and read using a different version. If you can guarantee all binaries, running programs, and data gets upgraded (no persistent data exists) then it might not be a problem. As soon as you have multiple organizations involved, guaranteeing all apps and servers sync to the latest version of a library, rebuild, and redeploy will be difficult. Static type checking assumes no version skew. All the code in the binary got built with the same version of the library defining the types. It's a closed world.
- pennaMan 3y agoThat's why it's a tool for web apps
- dbbk 3y agoSPA web-apps still have long-lived sessions
- simonbw 3y agoWeb apps can still have the problem of the server and client getting out of sync. 1. Plenty of people deploy their frontend and their backend separately. 2. Even if frontend and backend are deployed at the same time, there's still the case where the user has the old version of the client loaded in their browser as a new backend gets deployed. I've seen some web apps tackle this by periodically checking if a new version is available and either refreshing the page or prompting the user to refresh the page.
- tantalor 3y agoEven with webapps, duration a binary rollout you can have clients reaching different versions of the app on each request (that's basic version skew).
- yencabulator 3y agoPretty much "you do it". Zod can accept unrecognized fields, and then you move to optional fields, and finally once the dust settles you can make the field required. Of course, the demographic that most desires what tRPC does is the least likely to think about that sort of stuff.
- nprateem 3y agoI assume the tight coupling would mean it can't be used for capacitor mobile apps?
- dbbk 3y agoSure it can, you can use https://github.com/prosepilot/trpc-openapi https://github.com/prosepilot/trpc-openapi
- bobolino123 3y agoLazy imports are not supported with tRPC, making serverless functions heavy which results in long cold starts.
- tantalor 3y ago.input(z.object({ name: z.string() })) What the heck is "z"?
- anandchowdhary 3y agohttps://github.com/colinhacks/zod https://github.com/colinhacks/zod
- mishkinf 3y ago"without schemas". How do you define the types that you have to submit on the client and server calls?
- squishy47 3y agowe're using trpc at work and it's quickly becoming the default for gateway apis built for specific frontends. Everyone seems to love it, and when combined with Zod it allows us to iterate super quickly as we pretty much only have to think about business logic as schema properties can easily be shared. though my favourite feature is zod transforms, meaning a iso date string can be validated and transformed into a DateTime object before it gets to the business logic. also it means we can remove the npm library of types that was previously used to sync types between FE/BE (for this reason alone i love it really) I found the Docs somewhat lacking when i was trying to get the first project going but now I've used it for 6 months and i don't need to look at the docs it's been great. (remember to have exactly the same versions installed on the FE/BE and the same tsconfig makes it easier too, spent too long on these obvious fixes... :P )
- aabbcc1241 3y agoThe title is misleading, there is schema (defined with zod in specific)