Ask HN: Why isn't JSON-RPC more widely adopted?
JSON-RPC / OpenRPC seems like a great option for APIs. Why isn't it adopted more widely?
Many of the APIs of projects I've worked with look like essentially like RPC style calls. The URLs are structured as action/method names to do specific things. I like many of the benefits to an RPC approach compared to REST, GraphQL, or gRPC-Web. Unfortunately, the client code generation with popular options feels clunky when you want to use them in an RPC style. It seems like JSON-RPC would be better.
194 comments
[ 3.5 ms ] story [ 213 ms ] threadMethods can be expressed in http method, and resource can be expressed in url. Response status can be expressed in http statuses.
Some people prefer that for lower payload size. Others prefer byte encoded payloads like msgpack instead of json. In trading platform api's i've noticed that.
Also, using url based resources lets you route these requests at the network proxy level (caddy haproxy nginx envoy traefik etc).
This also simplifies circuit breaker work. The more error states you can interpret at the HTTP protocol layer the simpler it is to implement resiliency behavior. If you have to keep parsing responses looking for errors then you have to instrument every single call site separately - and distinctly. The more variation the harder it is to validate that they work properly.
- Enjoy
- no it's over here
- What are you talking about?
- Who the hell are you?
- Not for you
- Nothing's here
- I fucked up
Only the last one should be associated with circuit breakers, which I think you already get. Then it's just two values to worry about. < 500, and >= 500
And it does start getting really blurry when you get into you fucked up. Or you are fucking up so hard that we fucked up and in fact our fucking up everybody else.
And status 418 is only legal on April 1 in countries that celebrate April Fool’s Day. You can just turn your servers off for the day and not answer requests.
JSON-RPC on the other hand is simple and all libraries I have seen implement it correctly and even if there is no library you can easily handroll everything.
I've worked with all three, and I think SOAP is the worst. EDIFACT is huge, but the structuring of the elements are pretty well defined. I've had to both consume and generate non-trivial EDIFACTs and while it looks quite weird it's easy to do and easy to debug, at least in my experience.
Except for trivial stuff, SOAP as always been a nightmare for me. We've tried tooling but there always incompatibilities and workarounds needed in our experience. These days I manually generate the SOAP envelopes for each integration we do, with potentially some processing of the payloads as well.
While XML is verbose, with a good XSD that doesn't try to be fancy I find it quite nice to work with. Even the signed XML stuff (XMLDSIG), which while it took some time getting into, turned out to be not hard to implement due to tools and libraries that worked. So XML in itself is nice, it's just SOAP that manages to make it all hard for no good reason.
JSON-RPC is easy enough, tons of JSON libraries out there that can be used if you need to hand-roll.
probably tooling, I haven't looked into it but whatever tooling available back in those SOAP days probably had poor UX. These days if there is a will to make either SOAP or JSON-RPC the way, the community could probably make better and more tools.
Having said they both look shit to me :)). I guess if we go with the assumption that there will be tools to help with debug etc then the whole appeal of text based protocols disappears and you wonder why use such inefficient transports.
For example client does the request, some proxy intercepts the request, checks some things, then forwards the request. Another reverse-proxy on the server side again intercepts the request and routes it to the final web server.
If one of the proxies receives HTTP 5xx, it might perform few retries.
Devops people want to have some meaningful info in the URL, so they can write some security or routing logic.
It might be useful for devops people to have not just 400, but many different response codes, like 400, 401, 403, 404 for different situations. They'll make metric out of it and set up an alerts.
So if I would design modern RPC, I'd go the following route:
1. Do not make it protocol-agnostic, but rather embrace HTTP. Method+URL should contain routing information (jsonrpc method field). Errors should have a way to map on HTTP response codes. It does not mean that it's impossible to use it over say websocket, you just need to wrap it with HTTP-like structure.
2. Do not bind it to JSON-only. There should be a way to provide request parameters via URL query parameters, via JSON body and via protobuf body. Good server implementation should respect Content-Type and respect Accept HTTP headers. So I can call that method via cur, via some JS code or with optimized protobuf mobile SDK. JSON should be the main target, though, other encodings should be mapped to JSON.
3. Support meta-information, probably using something like JSON Schema or OpenAPI.
4. HTTP Method must be used as a caching and idempotency information. GET responses are cacheable. POST is not idempotent, etc.
Actually it might look a little bit like REST. But I just tried to extract the most useful parts out of it and remove useless parts like coding some parameters in URL Path or the whole philosophy about entities and stuff. If you don't want to think about caching and idempotency stuff, you just use POST for your endpoints and that's perfectly fine.
I've never used JSON-RPC but from the little I've read, the part it is useful for is exactly the part people often customize.
There is not so much tooling for JSON/RPC in general. But it is very simple. I wrote my own client library wrapper that did the connection handling and request/response mapping. You'll probably want to do something like that to be able to handle requests that take a long time, that need to be retried, to re-open closed websockets for some reason or such. But if you have that, it's OK.
I was not aware of the open-rpc 'schema' stuff, that looks very interesting, simple and useful
HTTP GET is idempotent, which seems trivial, but it's worth more than people think. With JSON-RPC you can't safely retry any RPC, and that matters at scale! (If the transport is HTTP, I'm pretty sure all JSON-RPC are POSTs. JSON-RPC can be good for local communication like language servers, where you don't really care about retrying or caching)
FWIW Google had/has a very RPC-like model, and when I left many years ago there, I remember they came around to giving explicit guidance to use a REST-like model (which you can do in an RPC system). That is, where you have more nouns than verbs, and you GET nouns.
writing resource oriented apis does benefit quite a bit, as it often forces you into good api patterns around idempotency. many aws apis and almost all new ones follow this pattern and they are much betterto integrate with because of it.
Yeah, def getHandler() = { val a = randomString() + readDatabase(); writeDatabase(a); return a;}
However that doesn't mean it's a good implementation. What you're doing there is gonna break caching for example, unless that's intended. If I'm calling a `GET` and your service is behaving like a `POST`, I'm not gonna be able to rely on standard semantics for a lot of things!!!
See section 9.1.2: Idempotent Methods, where they succinctly address the point:
> Naturally, it is not possible to ensure that the server does not generate side-effects as a result of performing a GET request; in fact, some dynamic resources consider that a feature. The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.
-
It's funny, I wouldn't have thought the RFC needed to say this. I'd have thought it was a waste of words since it'd be obvious to absolutely anyone that you can't... what? magically force developers to not write a bit of code in a system you have literally no control over?
Yet here we are, proving the authors well-prepared all these years later.
Of course it's possible, there are ways to guarantee that and to prove that. That's an area of ongoing research. E.g. https://link.springer.com/chapter/10.1007/978-3-642-36594-2_...
Though I've said just one thing - you can't expect that a GET query would be idempotent. You may only hope.
Depending on your model it can be proven.
Electricity consumption won't be a side effect in any reasonable model.
Surely this is a troll...
If fact the latter is better, because it may be somehow enforced by cogen if you code-generate into a language which may, for example, enforce purity or totality.
And the whole story about "REST not being an RPC" or "there is purity/idempotence/whatever in REST because RFC says that GETs are pure/idempotent/whatever" makes very little sense.
At the same time I'm saying that it's total bullshit when someone says that it's not possible to enforce lack of side effects (like in referential transparency) during a remote call.
It's such an off-the-wall connection I can hardly refute it: it's like we're talking about toasters and you dived into talking about CRISPR because I said the word "crispy". If anything it'd imply you're not familiar with CRISPR or toasters.
> Though I've said just one thing - you can't expect that a GET query would be idempotent. You may only hope.
I guess you should look up what it means to expect something. In the context of your sentence hope and expect are synonymous*, so maybe you meant you can't guarantee?
And even ignoring that mistake, it's not really useful to say "You can't be sure something doesn't follow guidance, you can only expect it" because that's tautological. Guidance in and of itself doesn't have any way to assert direct influence on an implementation, that's why it's literally called guide·ance.
*before you use that to dive into a grammatical diversion, that is not generally the case, only specifically in your use.
Let's assume we make a remote call with a list of VM instructions for a virtual machine which has no access to any I/O. Now we only need to prove the correctness of the answer. Whatever you do on the remote side, you won't be able to make your computation impure. Yes, you still may write logs or sleep but it won't break referential transparency, there will be no way to return two different accepted results for the same inputs.
You may even have I/O in some very limited form.
You don't have to supply the code.
If you don't agree, show me side effects in EVM.
> In the context of your sentence hope and expect are synonymous
Only in your eyes.
Anyway, I've been saying that REST is too unformal and weak-typed.
You implicitly revised your previous statements, and the revised point is even further off base (I mean, now you're showing you don't know the difference between REST and HTTP verbs?)
At some point just take it as a learning experience that non-sequitur about verifiable computing don't have any of the "shock and awe" on HN that they might on and your Facebook wall...
You should stick to things you understand if you insist on making authoritative statements.
-
By the way: language doesn't only work "in my eyes", words have meaning, learn those meanings before you use them.
> You should stick to things you understand
Ok. We don't need VC, for practical purposes we may do a lot of contract enforcement at the tooling level, like if we generate code from an IDL, we may restrict access to various APIs, enforce purity and totality.
> you don't know the difference between REST and HTTP verbs?
It doesn't matter if you talk about REST or HTTP, nothing guarantees "idempotence" of GET. Also the discussion was in the context of REST as something opposed to RPC.
> learn those meanings before you use them.
You command too much.
If you're willing to ignore the rules you can get away with pretty awful stuff that no one should really need to account for in a general conversation.
This is a common cache busting technique. Why is it a bad thing?
If you have the option between cache busting and just using the correct HTTP verb, please choose the latter.
As to whether there was an option in this particular case, I think the fact I referred to it as a "truly truly awful codebase" should provide a hint.
YMMV, however.
In 2017 I left to join Azure, which is REST-heavy (in all this, I mean "modern" REST, not "Fielding" REST). I hated it at first, but eventually saw how nice it is to have the conventions. Less to explain in public API docs, easier to generate scaffolding, common themes of how to destructure incoming requests in middleware handlers and middle-tier services, a standard way to serialize references to resources across the platform. I was a convert.
Moving to google in 2021, they're still very RPC, and it feels so cluttered and arbitrary. I'm not there anymore.
Yes, REST is roughly just some conventions around RPC at the core (though you get things like caching for free), but ultimately it's a really nice set of conventions that are easy to follow and limit surprise, and are standard across the industry. As one user commented, you do sometimes have to work around REST to get it to fit, typically futzing in headers or request metadata, but to me the pros outweigh the cons.
Azure's APIs are more RESTful than most: they actually bother using resource URLs as identifiers.
I've never understood what people find so hard about HATEOAS. It's like people have never interacted with a HTML form or followed links to stuff, or have difficulty figuring out how webpages can consist of multiple linked resources.
Or that an object graph of a system is a web of linked resources, and that every program has an entry point object/interface from which you can navigate to any part of that object graph, just like in REST.
The way Azure does it is kind of nice (to me at least, as an ex member of ARM) because ARM can generically check access just by the URIs. The downside though is that the URI also includes the subscription and tenant id, so ARM has to deal with the URI being stale after stuff gets moved around. And then the advent of management groups and cross-tenant access made it so it's not quite so straightforward anymore. But it was still quite useful.
Now the question is: Why do people insist to calling some (no standardized) ad hoc RPC protocol "REST(full)"?
Idempotency is not exclusive to REST. When you invoke File-Save twice in Notepad it doesn’t create two different files either.
You would have to encode your app-specific logic in every proxy or load balancer. In HTTP, they already exist.
The top two comments in this thread explain this more:
https://news.ycombinator.com/item?id=34228122
By using a proper protocol.
Not everything is nail just because the modern thin-client is only able to wield the HTTP hammer.
Personally I find JSON-RPC pretty meh and do not have that big of an issue with Rest, but I can definitely see the perspective of those who dislike Rest. Rest has it issues.
This kind of "make the trivial part easy", "get started quick", "need no tooling" approach feels fast... at first, but then very quickly runs into nigh insurmountable problems.
A great analogy I heard was: "If your plan is to go to the moon, you won't get there by making a fast bus."
Is it neat they I can copy as curl a request to replay? Yeah. But, that isn't too hard to do with most any framework, is it?
Advice that I provide to dev teams is that every time you introduce a network hop, you're more-or-less going to triple the complexity of that interface boundary.
Before, where you might have had a simple function call such as "foo(p1,p2);", you now have to:
1) Define a message type that encapsulates the p1 & p2 arguments into a single thing.
2) Do this on both server and client, consistently, and allowing for versioning to handle rolling upgrades.
3) Encode and decode your actual programming types into this blob. (Across dissimilar languages this is especially fun.)
4) Write the "RPC server" code.
5) Write the "RPC client" code.
That last one is the bit that a lot of developers skip, or make it "other people's problem".
Imagine you're writing a server for an RPC endpoint. You (internally) define some return message type, call "toJson()" or whatever on it, return it from some HTTP REST route mapping... and you're done.
You're done.
You.
Queue the potentially dozens, hundreds, or possibly hundreds of thousands of developers that have to bang out the client side of this. By hand. Typing it in, based on your documentation... of which there is likely none. Or it's in HTML and not machine readable.
They're going to make mistakes. They will assume something is nullable when it isn't, or not-nullable when it is. They'll have no idea what error messages can be expected, or in what format, and the only way they'll find out is... in production. Rare errors they'll likely never handle properly.
Meanwhile, with "proper tooling" the server-side developer uses a formal Interface Definition Language (IDL) such as the one used by CORBA, DCOM+, gRPC, Cap'n Proto, or whatever. This IDL is ingested by tools(!) on both the server and client, generating reams of "stub" code, ready for business logic. Better tools will automatically insert the matching doc-comments, so that when typing in a proper IDE (which you use, right?), then hovering over a function definition will show its help summary text live, in context.
In enterprise settings, it's common to see code that's basically just a dozen APIs glued together to do something useful.
If those APIs are hand-rolled REST, then this takes months of development effort, and then endless maintenance as things just break in weird and unexpected ways... in production.
If those APIs are generated with tooling, then hundreds of kilobytes of that boilerplate RPC client code can be spat out in just minutes of effort.
More importantly, nobody then ever needs curl to diagnose random errors in production, because the code won't even compile if something doesn't match the schema. If something returns an unexpected value, nobody needs to inspect the wire traffic, because the deserialized value is in memory, visible to a debugger or log trace.
Getting "into the weeds" of banging out RPC requests by hand is for people that don't realise that they're not supposed to care what the wire format is!
It really is night & day. For example, back in 2006 with Windows Communication Foundation, you could just paste a single URL into Visual Studio, and then you'd be off to the races. It would generate everything for you, and you could just start programming your business logic immediately.
But it does not have to be this way. For my work, I've written a few smaller scripts that talk to things like Github and Jira using only python stdlib. There are no third-party libraries or codegen tools, there is not even install step - you check out the code and run, and it _just works_. And users love it and use every day, and the fact that I can tell them "no dependencies needed, just make sure you have our standard corporate Linux install" lets those scripts be used by many different teams.
Some things require mountains of code, but not all of them.
Our client side story isn't so good, as your reply predicts. Our primary JS websocket client uses introspection so that's no burden. But we also ship a manually constructed matching Typescript interface which does get out of sync. And other clients are left out on their own. It'd be nice if the codegen tools linked in the original post were further along as this would make life for generic clients much easier. I've been watching them for some years but they don't seem to be going anywhere and I'm comfortable enough with our custom implementation.
being able to easily introspect requests and define your own over the wire was immensely helpful, and it’s painful that i have to reach for a special tool like grpcurl.
If you want, there are plenty of JSON proxies including Cloud Endpoints [1]. I also do most things with manual curl'ing, and relied on the Google APIs to have these kinds of "Fine, we'll also take JSON" setups in place.
Once you've figured it out, you build the proto in your automation directly. But I basically never use grpcurl either.
[1] https://cloud.google.com/endpoints/docs/grpc/transcoding
If you're doing URL routing, you're at the mercy of your web framework
If you've got a data structure coming in, you're probably turning it into types in the programming language you used fast, and you're more quickly into the land of plain old programming, rather than routing configuration in a web framework.
1. Binary support for non-web clients 2. Robust code generation 3. First-class cancellation/deadline concepts 4. More guidance around a standard HTTP transport; kind of related to 2 5. Simple schema/type definitions to assist in code generation and API ecosystems
Ultimately, we went with gRPC and it has been pretty nice. The code generation is mostly good and where it isn't, we can make our own wrapping code. There have definitely been some downsides and gotchas when it comes to the servers, specifically around client streaming and bi-di requests. Also, grpc-web is really not great and is the biggest downside so far. Overall though, it feels worth it for our use case. Protobuf has been great.
I would love something like https://orval.dev for gRPC-web. Have I missed something or is it just early to expect it?
I tried a few libraries but couldn't get them to work or would generate unappealing results. I believe I'm hitting this issue with my local experiments. https://github.com/grpc/grpc-web/issues/535
UPDATE: I was able to get something working with these from a ReactJS app!
Server: https://learn.microsoft.com/en-us/aspnet/core/grpc/grpcweb
Client TS Gen: https://github.com/timostamm/protobuf-ts
There are two correct responses to this:
- Use a decent RPC framework to hide HTTP's semantics (which are not appropriate for RPC) and to get generally better but still widely-accessible tooling; I use gRPC but at this point there are a dozen good-enough ones (JSON-RPC not being among them).
- Design a REST, or at least REST-ish, API. Map your URLs to entities not actions or methods. Make each entity have a canonical URL. Use content negotiation. And so on. It's more work but you're also available to a lot more real HTTP clients.
There is also a wrong answer:
- Continue trying to do RPC over HTTP semantics with an inefficient format and assume that having a standards document per se solves literally any problem.
Those structures that do, are often persistent; so the right trade-off might be to make your protocols more lightweight (I use |;, separation of just text) and then use JSON for your database objects (fits well with my |;, separators).
Here are two examples of "packets":
Movement: "move|<session>|<x>,<y>,<z>|<x>,<y>,<z>,<w>|walk" (where x,y,z,w are floats)
Storage: "save|<session>|{"name": "torch", "amount": 3}"
You can read more here: http://fuse.rupy.se/doc/game_networking_json_vs_custom_binar...
While if you do need something deeply hierarchical, there's a good chance it might be something massive and so you'll probably just pass a URL to it as a document, rather than embed it as parameters.
So why use JSON for input if you don't need it? It's just using the simplest tools for the job. Since any web request already parses GET/POST parameters, why would you add JSON into the mix when the parameters can usually already handle what you're doing?
(But if you did have an API where input was necessarily deeply flexibly hierarchical, JSON RPC could very well be the perfect tool for the job. Also, compare with API output which often is very deeply hierarchical, which is why JSON is so popular on the output side.)
Because next morning you start to need it and your API becomes inconsistent and your b2b buddies make a deep sigh which you can hear through a chat. Inconsistency is a minefield. Makes no sense to complicate everyones job out of the blue.
If there's a single data value that needs a flexible JSON payload, then just put that as an encoded string in your GET/POST parameter. It's not "inconsistency", it's just a special case. Same as you might not need a floating point number anywhere except for one endpoint. Not a big deal. You don't need to migrate the entire call to JSON-RPC.
I believe all the described tactics come from a static html limitations territory where it makes sense to put continuation data in hrefs to avoid forms and javascript.
Consider writing an HTTP based RPC server. I parse the first line. It tells me the location and the method. Followed by a bunch of key value pair headers where both the key and the value are strings. So far so good, I could write all of the above in C or Go pretty easily. Finally comes the body. I can unmarshall this in Go or parse this in C easily because I can I know what to expect in the body by the time I get there. This is because I already know the method and location.
Contrast this with JSON RPC. I have to partially parse the entire object before I know what function is being called. Only then can I fully parse the arguments because I don't know what type they are (or in other words, which struct the arguments correspond to) until I know what function is being called, what version of RPC is being used, etc.
Super annoying. And HTTP is just sitting there waiting to be used.
HTTP allows for incremental parsing. I can parse the first few lines separately from the body. It makes handling input really nice.
Having everything in a single JSON object doesn't allow for incremental parsing because the standard says I can't guarantee order of key value pairs when an object is concerned.
These all qualify as "more heavyweight," at least from an implementation perspective.
That shouldn't be necessary. Protobuf changes are supposed to be additions-only, where existing field numbers never change their meaning, but new optional/default fields are added. If you do that, you only need one version of the proto spec.
This gives you the advantage of load balancers being able to fast drop misbehaving clients for load shedding. You also have the security advantage of authenticating a legitimate client before handing off the payload to a parser (both as a DoS vector, or the risk of parsing untrusted data).
The JSON RPC spec says nothing about authentication, you can use whatever authentication method you wish.
This is a variation of Hyrum's Law: With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody. https://www.hyrumslaw.com
Really? For a JSON-RPC over HTTP API? Where?
If you are using a generic JSON-RPC framework (does that exist?) which supports multiple transports then credentials in the request object seems like a good optional feature but surely you could disable it and stick your regular HTTP auth middleware in front?
E.g. I've implemented a simple indexed binary format for JSON-like data: https://github.com/7mind/sick
The problems with JSON-RPC are mostly in its design, it's not ergonomic and its type system is a joke.
There are many better alternatives ranging from gRPC (still not ergonomic and not modular) to Protoforce (extremely powerful and ergonomic but only marginally adopted).
It's the overhead of having to parse it to a generic JSON-like data structure at all, before knowing what the keys and values mean, so you can then transform the JSON-like data structure to the program's API structs.
If you knew the shape of the RPC method before parsing the JSON body, the body would be parsed directly to target language structs and variables, without using any generic key-value objects (dicts/hashes/maps), any generic arrays, or even any strings in some cases (when JSON strings represent enums). For many RPCs there would be no memory allocations during JSON body parsing. But you don't know the shape, so you either have to allocate memory and store the input in key-value objects, arrays and strings temporarily, or parse the JSON body in two passes, the first pass to get the shape for the second pass.
You don't have to do that.
> If you knew the shape of the RPC method
The whole idea of RPC is about "knowing the shape" (typing) and good RPC implementations do that.
> you either have to allocate memory and store the input in key-value objects
If I use JSON-like data structures I don't have to use classic parsers with intermediate AST representations.
> or parse the JSON body in two passes
I've shown you a small library which allows me not to parse anything at all.
I looked at your repo. It doesn't implement JSON-RPC or parse JSON-RPC requests, so I guess you are talking about alternatives to JSON-RPC. An RPC protocol using your library's SICK format may well be efficient (though I'm not sure it's efficient to serialise).
When I saw your initial reply I thought your were saying the criticism of JSON-RPC was incorrect in the comment you were replying to. But now I realise you were saying a different protocol not using JSON can be more efficient than JSON-RPC, using a differently request format which could be used as a drop-in replacement for JSON in JSON-RPC.
> If I use JSON-like data structures I don't have to use classic parsers with intermediate AST representations.
Indeed, but JSON-like data structures are potentially in the "too much overhead" category by themselves, regardless of whether a classic parser is used. Parsing per se isn't necessarily the main overhead: JSON itself can be streamed as tokens. Even with your SICK format, the data structure has to be converted to target language structs, enums, etc, which is a type of parsing, even though it's not parsing text.
> The whole idea of RPC is about "knowing the shape" (typing) and good RPC implementations do that.
Indeed. The type of a JSON-RPC request is known only when the "method" key is read, and that key can occur anywhere in the top-level object. Start, middle or end. Finding "method" requires at minimum a pattern-matching scan of the JSON body. Until that's done there's no way to know what types the other values in the JSON body are to be mapped to in the API's target language.
So we can agree good RPC implementations "know the shape" when reading a request body, and JSON-RPC doesn't have that property.
(A variant of JSON-RPC which uses an array with the method in the first element does have that property though. That one has the merit of the high level of cross-language tool compatibility due to JSON, a 1:1 correspondence with JSON-RPC requests, plus the capability to serialise/parse directly from/to target API structs, variables, enums etc in whatever language without requiring any intermediate JSON-like data structure.)
Most typed languages give you two options for parsing JSON:
- 1. General JSON values (hash maps of JSON scalar values).
- 2. Strongly typed - convert JSON string to a specific type that you defined in your application code.
You could use 1 to then use in a switch statement to branch to 2.
You may have to parse the JSON twice in some languages to do this, but JSON is typically small and fast to parse.
JSON can be parsed linearly so long as every key fully determines the format of the value.
1. Client libraries may not send keys in a defined order, so the key that tells the server what method to call might end up in the middle or end of the payload.
2. The vast majority of default or common JSON parsers out there for the various languages and frameworks most people use just don't support incremental parsing.
Your post isn't particularly relevant, since it proscribes a different way of structuring your payloads than JSON-RPC (and others). It doesn't matter if these others are "wrong"; they exist, and have wide support, so people will gravitate to them, rather than rolling their own.
Once one had the body - which will typically be read in one go for usual RPC use cases - it’s as easy to deserialize json as anything else that could be transferred there.
You could probably combine it with a partial (proved) HTTP parser and get max efficiency (at the cost of abstraction-breaking layer fusion...).
I think tRPC sort of implements a transport layer for (a superset of) this, no?
1. REST positioned itself in opposition to RPC. The concept of RPC is a negative one in many engineers’ minds.
2. JSON-RPC isn’t that much better than passing your own JSON formatted messages over HTTP. So I don’t think it gets you too much incremental value these days.
With that said I love JSON-RPC and tend to reach for it pretty often for prototypes or greenfield projects.
“Hypermedia as the engine of application state.”
PayPal and GitHub APIs tend to work this way. They return a pile of hyperlinks.
If stuffing a bunch of links into your JSON response is what truly transforms your RPC into REST (I don’t see what else makes GitHub API so different from other APIs), then I don’t even know what’s the point of that.
And surely it is only superficially similar to the non-API REST, ie how simple websites usually work.
In terms of what we think of as an API today, my favorite is SWORD: https://sword.cottagelabs.com In particular, it does not define any URL scheme, client applications are expected to read links from the XML (for v1 and v2) or JSON (for v3) body.
RPC is like stored procedures in a database. Some databases implementations only provide access to data through stored procedures, hiding the underlying tables.
The first is usually more flexible to the client, he can decide what data it needs. RPC in a database is gives the server more control over the client: what data it can get, how it can be retrieved/manipulated. This introduces tighter coupling between client and server, which can be a problem is you have many unknown 3rd party clients.
I am not seeing the difference between REST and RPC.
Whatever some people say, REST is a form of an RPC. If someone wants to object, they should think again and again and again.
Conceptually there is another key difference that is REST revolving around standard HTTP verbs and encourages CRUD like APIs while RPC encourages APIs more like a non networked APIs
Just think about it. For example, try to build isomorphism between REST and any RPC you want.
You should be learning, not arguing.
RPC = Remote Procedural Call.
REST = Representational State Transfer. REST is closer to file transfer than RPC.
Not true. "RPC" is just a generic name and there are multiple approaches and implementations which may provide you some guarantees, in specific environments such guarantees may be strong.
> REST does
How can I enforce or trust any REST "guarantees"?
I have just googled a random service that self-describes its API as REST [2]. I would be glad if someone could pinpoint me at the exact differences that make it "closer to file transfer than RPC".
[1] I guess in its original long gone meaning it's something closer to what 90s web looked like, but no one uses it in that sense.
[2] https://developers.google.com/fit/rest
https://developers.google.com/fit/rest/v1/datasets#get_a_dat...
It says: "To get a dataset, do HTTP GET to this URL. The result is a JSON document". Notably, this does not _look_ like RPC call -- there is no "getData" anywhere, no {"status": "ok"} field. Instead, this is pretty identical to how you would access a static directory with weirdly-named files.
(If you never worked with "static directory with weirly-named files" API, here is an example: ftp://ftp.swpc.noaa.gov/pub/forecasts/45DF/ . To fetch data, you access file named "{MM}{DD}45DF.txt" (via FTP, no less!), get the document and parse. The REST is a modern equivalent of this.) NotablyThis does not sound like a "getData()" function
> there is no "getData" anywhere
Here it is: HTTP method GET
> no {"status": "ok"} field
And here it is: "The response is a 200 OK status code."
In general, do you think it is possible at all to have a request/response protocol which is _not_ RPC?
Fetching stuff over FTP, Gopher or HTTP is not RPC per se. Just as merely using classes, or not using classes, doesn't mean that you do or don't adhere to OOP.
> In general, do you think it is possible at all to have a request/response protocol which is _not_ RPC?
RPC means that you call functions that require remote execution similarly to the way you call local functions. If what you are doing doesn't fit that paradigm semantically, it isn't RPC.
That "basically" is doing a lot of work in your claim. Show me the equivalent of idempotency that clients and servers can depend on in any standard RPC spec. If there is no such guarantee, then REST is not RPC.
Certainly you can in some sense mimic REST using RPC per the Turing tarpit, just like you can simulate RPC in REST, but that's not the same as saying REST is RPC.
> With the RPC communications protocols, a maybe call lacks execution guarantees; an idempotent call, including broadcast, guarantees that the data for an RPC is received and processed zero or more times; and an at-most-once call guarantees that the call data is received and processed at most one time (may be executed partially or zero times). Both idempotent and at-most-once services guarantee that a sequence of calls in a session are processed in the order of invocation by the client.
@total def sum(a: int, b: int): int
@pure def traverse(...):
I can't enforce any REST "guarantees" but I can do it up to some extent with a custom RPC framework.
There's a sense in which it's correct (i.e. that it's possible to implement one with the other), but it's definitely not the right way to think about it.
REST is about working with state objects that live on other servers. RPC is about getting other servers to do actions. They're different ways of thinking and coding, despite the fact that yes, you can wrap an action up in some state, or that most actions will be modifying remote state.
And given that XML-RPC is a thing, JSON-RPC was supposed to be its replacement (because it's easier to parse JSON than XML).
JSON-RPC has the same problem as WSDL... It would be useful if clients would discover the service and use it automatically... But that does not happen in practice. It's always a human who does the integration work manually because only a human can fully make sense of what kind of data is provided by various endpoints. You can't automate endpoint discovery and therefore there is no need for a single common standard to facilitate that.
gRPC uses protocol buffers, which had the goal of being smaller and faster to parse than XML. The issue it was trying to fix was the performance of XML.
The JSON-RPC protocol itself doesn't seem to solve anything that the other options don't solve as well.
> JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of number types of various capacities and complements, fixed or floating, binary or decimal. That can make interchange between different programming languages difficult. JSON instead offers only the representation of numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit sequences even if they disagree on internal representations. That is enough to allow interchange.
It also explicitly excludes NaN encoding, which further distances itself from any coupling to IEEE floating point expectations.
I think that one day browser will support it natively.
The schema definition in it is really really ugly and I could barely get it to work. Also at the time (2015) the language support for that schema definition wasn't great. I also tried swagger at the time and there were quite a few things I just couldn't get it to do. If I couldn't get it to behave in a targeted research spike it was really going to be a non-starter for new employees trying to slam out features. These issues meant people would go with the losest typing or just "object" for a lot of items. One problem is that they both have the flexibility of XSD but whether a given laganguage will support that validation or do it correctly was spotty at best.
We went with our own dumbed down version of GPRC called Twirp which is very very simplified. The schema language for proto3, which twirp uses, is a lot simpler to write, uses close to just native typing, and does a lot less. Doing less was actually nice because it meant less rules. I was able to implement the prototpe ruby version which someone else (thanks cyrus) put into production in under a day because there were so few rules but what it had gave us type safety.
For years people really liked REST because it was "self describing via api" but it turns out no one cares, what they want is a client in the language they're using that works. They really don't care that much how the client is generated. So to me Interface Description Languages (IDLs) and a code gen step are what you want with some simple basic rules.
We built a similar tool for config checking, and most of the features weren't used. But Twirp and this language removed a whole class of issues that had plagued us and caused outages for years around string vs int in configs and apis.
Also, at our scale, for some services, proto or another binary format has substantial benefits. I'm not saying it matters for your service and human readable is great (we had to learn and built up tools to debug things, and kept json support in twirp exactly for the poor humans), but when you're sending a millon requests a second or so to a service, there is a noticeable cost equivalent to an engineer's salary in infra costs. But most people are not running at that scale.
On the front end GQL worked better for us, it pushed the flexibility to the front end devs while we sorted out the back end over several years. The level of coupling we'd have had if we ran Twirp, Swagger, or anything else direct from front to back end would have made work 100 times harder. I was skeptical when they started but it's been a boon, and I was smart enough to let smart people be smart in an area I'm not smart in eg I stfu.
Anyway TL;DR (at the end of course) there were better options for me such as Amazon's internal one, GRPC, Twirp, roll your own Proto based, and about a billion others.
I'll also note the v1 spec wiki page link is broken.