Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ask HN: Why isn't JSON-RPC more widely adopted?
151 points by vyrotek on Jan 2, 2023 | hide | past | favorite | 188 comments
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.

https://open-rpc.org

https://www.jsonrpc.org



Parsing makes it hard.

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.


Distinct RPCs being distinct URLs also makes it possible to easily route and load balance RPCs to different backends, which is lovely for scaling up.


JSON RPC tends to embed credentials into the body, where REST puts them in the URL string or headers.

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).


> JSON RPC tends to embed credentials into the body, where REST puts them in the URL string or headers.

The JSON RPC spec says nothing about authentication, you can use whatever authentication method you wish.


Many people have learned the hard way that it is one thing what the spec says, another what software actually do.

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


> JSON RPC tends to embed credentials into the body

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?


At that point you're doing OpenAPI, which will dispense with the subject routing boilerplate of JSON rpc, and have a lighter payload that you have to do less work to understand (presumably the JSON schemas are similar to your db tables)


When I’ve considered JSON-RPC in the past, the lack of easily identifiable URLs was the main drawback in my mind due to the difficulty of testing and debugging—the same reason I would never use gRPC or similar on my own projects. That said, I think a well-made browser extension would fix this issue for most people, similar to GraphQL, but it still introduces friction that isn’t immediately beneficial. I’d still give it a try in the future!


gRPC actually does have easily identifiable URLs. Protobufs can be heavyweight, but it's trivial to use a different encoding like JSON.


What do you mean by heavyweight? protobufs should be much faster to work with than JSON.


Not the OP, but: Protobufs are much more complicated on the implementation side than JSON. You at minimum need a protobuf library and a spec for your data that's shared on client and server. Migrations are also a lot more work because you may need to keep around every version of your protobuf in every client and/or server, depending on how synchronized the two are (i.e., if you can guarantee all clients and servers are upgraded 100% synchronously, as on a web site, then it's not so bad, but if clients are apps or peers that may or may not be upgraded, then the server may need to speak many different versions of a protobuf).

These all qualify as "more heavyweight," at least from an implementation perspective.


> because you may need to keep around every version of your protobuf in every client and/or server

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.


I stand corrected!


I don’t think this matters at all. People usually are not writing their own http libraries. They use existing ones, and those already take care of reading the http headers and the body separately. One should avoid handwriting a http server for avoiding security issues and to benefit from performance gains - eg using http/2.

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.


Then again, wouldn't you like the libraries you rely on to have clean simple implemenatations and the people who maintain them as easy a time as possible in doing so?


JSON-like data structures do not necessary require a stack automaton for parsing. Moreover they don't necessarily require any parsing at all.

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 not that parsing JSON to a JSON-like data structure is hard. That's easy.

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.


> It's the cumbersome overhead of having to parse it to a generic JSON-like data structure at all,

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'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.)


Would it be a completely ridiculous idea to have a special key that is required (if present) to be to the first entry in a JSON map/object?


Yes it would be. A good RPC tool should keep the distinction between envelope and payload.


Not to mention with the HTTP based RPC you can get some contextual freebies with the call like a token in a header and/or cookies. You can use all of that before parsing a single byte of the JSON.


See also my post: You Are Doing JSON-APIs Wrong https://news.ycombinator.com/item?id=31258658

JSON can be parsed linearly so long as every key fully determines the format of the value.


Can be, sure. But in practice that's not very useful:

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.


I'm aware! My faint hope is if I spread the word, maybe JSON-RPC 2 will not be bad in this way.


IIRC simdjson has some form of lazy parsing. I'm myself more for binary blobs lazily parsed, using a proved language and model checker (like recordflux or recent F* work on safe parsers). You could add a layer to 'optimize' the binary layout to have the most important fields upfront (for partial parsing performance) and everything cleverly aligned in cache lines.

You could probably combine it with a partial (proved) HTTP parser and get max efficiency (at the cost of abstraction-breaking layer fusion...).


I do not think "JSON does not support incremental parsing, HTTP does" is the reason why JSON-RPC is less popular.

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.


I would call it a bug if the client does not send the “jsonrpc” and “method” params first. It’s legal, but stupid. And from there, I know how what I am expecting to be sent and what types they are. If the client sends them out of that order, they get a slower response. But even then, it’s probably still maxing out a GB link.


Other than the alternatives mentioned here, one can also use well known headers that define API Methods which you can use for routing. No need to parse the entire request body just for routing.


So funny you say this. I think it's the insight of many developers including my own. I hacked together a framework that did this before the existence of GRPC. Now I'm trying to formalise it as a protocol. https://github.com/micro/network/blob/main/PROTOCOL.md


one way could be you peek method using sth like https://github.com/tidwall/gjson


I have experience working on some JSON RPC Load Balancer, and I can tell that it's one of the worst choices for an API:

- Method Name is a part of the body itself. So you have to parse it to make a decision on how to dispatch it. That's an extra cost.

- Error Code is a part of the response. So you have to parse it each time to figure out if it's a success. Also, some clients just give an HTTP Error Code instead. So you have to handle both.

- It can also be batched. Which introduces a lot of unspecified use cases.

- Like how are you supposed to dispatch those batched? Should they go to the same upstream? Or can it be parallelized? Should the order be preserved?

- With batches, the slowest request always blocks the in-flight response. You have to wait for all calls to be finished before producing a final response. That's a huge performance bottleneck. Also, what you do if some of them never finished?

- A lot of uncertainties about the ID. Especially because it can be a different type (int or string), which sometimes introduces two different handlers in the code. And there are no guarantees IDs are not repeating in the same batch. To make it worse, some clients rely only on the request/response order ignoring the IDs.

- Another problem is that it has no idea of Metadata, which is important for many real systems. For example, to do an Auth. Or do Caching and other optimizations.

So as an outcome, everyone just makes two layers. On the HTTP level, you have auth, routing, error handling, multiplexing, etc. And then you realize that JSON RPC only introduces extra complexity.


Graphql shares many of the same issues. And yet it is popular. And arguably a lot more convoluted. The difference is basically schemas and client generation. The whole point of RPC is that the stuff that happens on the network is just a means to an end and you generate the code that does the server and client stubs that you call.

I've seen just about anything on this front. People used to do SOAP which is all of the downsides of graphql and json rpc combined without most of the benefits. Stupidly complicated to do just about anything with it. It's a good reason people don't suggest that anymore for new APIs. REST APIs completely killed SOAP. The only places you see it these days are legacy systems from 15-20 years ago.

The thing with just about any kind of parsing overhead on servers is that it is completely dwarfed by network IO getting to the server and then more IO for getting to a database, redis, etc. Add ORM to the mix and you typically get this unoptimized mix of multiple calls to a database (because most orms suck at optimizing their joins). We're talking fractions of a percent of the time spent here. Web servers don't commonly use a lot of CPU time until they are handling dozens/hundreds of requests per second. With a asynchronous IO, you can server thousands of requests. The bottleneck usually becomes your database. Not the webservers. Those tend to run on cheap vms. Add more to scale at the cost of 10-20$ per month.

Back in the day when parsing overhead still mattered, we'd speed up our servers with a few simple tricks, which mainly boiled down to not buffering requests and responses. Use streaming parsers on the request payload, iteratively process whatever comes in, and stream the output as results become available. You'd be responding with the first bytes long before the last request byte had arrived. Great for processing large ndjson/csv files and responding with similar output. You can process mang GB of data with a single request this way. Doing this is hard but not impossible with modern asynchronous web framweworks. They all default to buffering everything and then processing and then responding. Reason: it's simpler and the performance difference simply does not matter.

But it's still a useful trick if time to the first bytes of your response matters to you and you have a really fast, tightly tuned backend. This is how you get to sub ms responses.


> People used to do SOAP which is all of the downsides of graphql and json rpc combined without most of the benefits. Stupidly complicated to do just about anything with it.

To be honest, this looks like cases of "you holding it wrong".

SOAP was one of the simplest systems I've ever worked with! You point your client lib to a WSDL and everything else just happens automatically. All you do then is calling regular methods in your code (witch happen to be remote calls). All the network crap is abstracted away and completely hidden. Full transparency.

Issues start to come up as soon as people thing they should do SOAP "by hand". Sending hand crafted XML through HTTP libs. This is of course pure horror. But this whole thing was never meant to be used like that. As long as you used the proper tooling SOAP is really great and orders of magnitude more productive than RESTfull nonsense.


Not sure if you are more agreeing or disagreeing with GP, but cost is not just computing cost, but also complexity and the higher risk to reliability that comes with it.

JSON-RPC makes sense if one only considers the abstract case with a single client and a single server box and minimal networking. Technically it's a viable solution but just not practical.

HTTP on the other hand is nice as it separates the business logic stuff like the RPC parameters from stuff that's interesting to the transport-network-thingies that sit between you and the data on a server, like load balancers, DoS protection, authz, rate limiters, ... etc.


It's a trade-off. Mostly pure REST is nicer for external APIs where it's just more important to follow the principle of the least amount of surprise for the ed user. For better or worse, people kind of know how to deal with REST clients.

Graphql, SOAP, etc. all force the user into doing complicated stuff with libraries, figuring out parsing for their techstack, etc. This is fine for internal microservices where you might not care too much about this and can standardize things a bit. But for external users it's more challenging.

With simple Rpc stacks (like JsonRpc), basically the up side is you get to rely on code generation to deal with networking. Cutting down on the amount of ceremony for adding new stuff is definitely nice in a fast moving team. But I would not use it for external APIs. It's kind of a legacy way of doing thing. An alternative to something called XmlRpc, which used to be a light weight alternative to SOAP when people were still doing XML and when MS came up with the genius idea to stuff something called an XMLHttpRequest in Internet Explorer. This was what enabled Ajax websites early on. The X in ajax of course stands for XML. That was just before json got really popular and people started getting really pedantic about their REST verbs.

Anyway, short history lesson. I'd not over think this and indeed simply use REST for public/external APIs and use whatever works for you internally for e.g. client server traffic between your browser/mobile apps and your servers or intra microservice communication. If it's internal, you control the techstack and you can sacrifice some things for making it faster/easier to develop and use without too much regard for what the other side is going to be using. Because you control both sides. REST APIs can be a bit of a burden to maintain. Lots of boiler plate client and server side. Not always the best choice internally.


Read about the important of idempotence in distributed systems

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.


Pretty much this. I was very anti-REST back circa 2015-2017 when I started doing web dev. I was doing solo work and all my services were JSON-RPC (I can't remember whether that was an actual standard then or if I just rigged it). I was (and still am!) into functional programming, and RPC seemed to fit better: it's just a remote function call. Nakedly PUTting a resource just seemed to violate my idea of good architecture.

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.


Can you point to some sources of information about the modern REST you are talking about?


"Modern" REST generally disposes of things like hypertext and representations as being too complex, instead leaning on things Swagger/OpenAPI to declare an IDL equivalent, so you're getting effectively an RPC system with some of the guarantees you'd get from REST.

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.


It's not hard, it's just wasteful and not very useful.


> 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.


> "Modern" REST generally disposes of things like hypertext and representations as being too complex, instead leaning on things Swagger/OpenAPI to declare an IDL equivalent, so you're getting effectively an RPC system

Now the question is: Why do people insist to calling some (no standardized) ad hoc RPC protocol "REST(full)"?


Yes this is what I meant.

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.


i think the benefit of client expectations around verbs isn’t the be-all-end-all. i can still write a non-idempotent fairly easily.

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.


Isn't the cache situation somewhat solved via defining your RPC as either a "query" or "mutation" (for example in systems such as https://trpc.io/ > https://trpc.io/docs/caching)?


How would proxies and load balancers know about this?

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


> How would proxies and load balancers know about this?

By using a proper protocol.

Not everything is nail just because the modern thin-client is only able to wield the HTTP hammer.


What prevents you from making specific calls idempotent? You can even specify it via a POST/PUT method in any sort of RPC call to make HTTP proxies (or automatic resubmitters) aware of this detail.

Idempotency is not exclusive to REST. When you invoke File-Save twice in Notepad it doesn’t create two different files either.


> HTTP GET is idempotent

Yeah, def getHandler() = { val a = randomString() + readDatabase(); writeDatabase(a); return a;}


Technically you can implement whatever you want under a `GET` - you can even stuff a body to a `GET` even though the RFC doesn't allow it (see ElasticSearch API).

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!!!


I saw a truly truly awful codebase get around the caching bit by appending a random string of bytes to the query string.

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.


> I saw a truly truly awful codebase get around the caching bit by appending a random string of bytes to the query string.

This is a common cache busting technique. Why is it a bad thing?


The correct solution for cache busting is generally to put a low TTL on the resource in question. Adding random crap in the end is only good if you're dealing with utterly broken caches or your webserver does some tremendously stupid header rewriting. In my experience, when people resort to that, it's down to a lack of understanding of HTTP itself.

YMMV, however.


Why do I get the impression this is a leading question...

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.


If you can't enforce a contract it's not a contract.


Maybe should learn what an RFC is, and check out this one before you keep trying to counter the document that formally minted the verbs in the first place: https://www.rfc-editor.org/rfc/rfc2616#section-9.1.2

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.


> Naturally, it is not possible to ensure that the server does not generate side-effects

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.


SCC is so unrelated to anything even being remotely discussed, that it would be an insult to the term "orthogonal" to describe it as such.

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.


> SCC is so unrelated to anything even being remotely

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.


When you're this far out of your depth, it may be best to stop floundering and see if you can float.

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.


I don't think I've revised anything. I said that it's possible to impose enforceable contracts on remote code execution. I'm not saying all the approaches are practically useful in the domain of RPC, but even there we can do more than just stick to informal promises.

> 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.


It is not possible to prove that calling the server will not produce side effects. The link you provided does not address side effects, but correctness, which is much weaker. A trivial example is that calling the server will consume electricity.


"Side effect" is a breakage of referential transparency.

Depending on your model it can be proven.

Electricity consumption won't be a side effect in any reasonable model.


What are you going on about? This feels like someone who's just read their first compsci book and now thinks they can build New Internet.

Surely this is a troll...


I'm saying that unenforceable contracts, like the "idempotence" of GET requests are no better than an annotation on a method in a conventional RPC IDL.

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.


HTTP GET being idempotent is an agreed-upon contract. If you violate it, that's up to you, but that would be a red flag to me as to the quality of your APIs.


These guys may somehow disagree with you: Etag, Last Modified, the whole If family.


For me, tradeoffs.

Methods 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).


> Response status can be expressed in http statuses.

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.


Funny enough I found that rolling most of the error types into one error type of circuit level that is either server failure or application logic failure is really where we wanted to go with circuit breakers. Having many new error types and not making the compiler and force you to check for those error types causes new errors to show up in an existing RPC method and no one notices it. Worse it either gets accidentally thrown into server errors which causes circuits to close when they shouldn't because it's actually a problem in the calling service, or they get thrown into user cases when they should actually be server failures and services continue to slam services that are actually broken. At work we mostly got it down to those two areas with lots of nuances under the business logic level issues as opposed to the server issues. However I don't think that using hundreds of status codes is the right way to go about it those two classes seem to be a really good place at the Hep binding and circuit breaker level. Circuit breakers in general are kind of tough.


You can think of REST as having 600 status codes, or you can think of it as having about seven.

- 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


Oh I 100% agree with you.. though I think you're missing "you fucked up". My point is more just that people mess up and blur the lines accidentally either throwing exceptions which get propagated up as internal service errors when they should actually get run across the wire as business logic errors as part of you fucked up. Or really they didn't guard their code correctly and you get we fucked up when you really meant say overloaded.

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.


HTTP error codes, the short version:

  2xx:  here you go
  3xx:  you go here
  4xx:  you fucked up
  5xx:  I fucked up
(not mine, stolen from I-can't-remember-where)


400 You Fucked Up aka "What are you talking about?"


aah missed it, I was cooking dinner. 100% agreed.


Originally I had them in random order and then I sorted them to try to make it easier for people to guess the status code from the colloquial description :)


which one is teapot?


You can’t prove the teapot exists, or does not exist, so does it really matter?

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.


That seems like a very reasonable hedge against uncertainty. I will bake it into our sla/slo.


Perhaps, by the time they decide on a formal RPC standard, people conclude they might as well use a compact optimized RPC standard, like gRPC. REST-ish JSON APIs work fine and are universally familiar. What's the big advantage of encoding whole requests into JSON blobs, rather than just URL routing? If I'm going to do that, why wouldn't I just use GraphQL, and get the graph traversal part for free?


there is something nice about being able to just write some json and curl an endpoint. getting to this point with grpc is effort.


This to me always feels like someone saying it's nice to be able to just pour crude into a couple of buckets and transport them around in the back of a truck when the goal is to build an oil refinery.

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."


this seems hyperbolic? the overwhelming majority of aws apis are json over the wire (with some variation in how the operation is defined. there is no perceivable downside to this practice, and they’ve built quite the “oil refinery”.

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.


I've only limited experience with it but it does the job OK. Can you expand on "nigh insurmountable problems"?


An RPC interface is the same as any other interface in programming, but with more complexity to deal with. Hence, just as we have the "proper tooling" to deal with internal interfaces, there's a stack of tooling for dealing with RPC interfaces.

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.


If you are in the "enterprise setting" and your languages/frameworks/standards require hundreds of kilobytes of boilerplate RPC code, then yes, you probably some tooling about this.

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 Python implementation automagically handles all the sever-side plumbing and Just Works. I didn't write it but think it's neat and good enough.

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.


Most crud apps are not the equivalent of an oil refinery.


At the same time, most crud apps need auth and a whole lot of other considerations.

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?


do it with grpc and regular curl.


Disclosure: I used to work on Google Cloud.

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


Use gRPCurl.


so another thing i have to install. and i also need to set up the introspection endpoint on my service to make sure it works well.


Postman does both.


> What's the big advantage of encoding whole requests into JSON blobs

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.


For my current job, I speced out what was needed for an RPC solution. I ended up basically coming up with what JSON-RPC provides but was missing a few things.

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.


As for better gRPC-web, you might want to look into connect-web https://github.com/bufbuild/connect-web


what have you not liked about grpc-web?


Personally, I find gRPC-Web very attractive but the current state of TypeScript/JS code-gen is very confusing and lacking.

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


Ah you should check out https://github.com/bufbuild/protobuf-es which feels great so far. Then there's connect by the same buf people but it has a grpc-web option https://connect.build/docs/web/getting-started/. The amount of code generated is also tiny, which I love.


Ultimately just the fact that we can't use standard browser dev tools to introspect requests. There are some plugins but they have not worked reliably for me.


IMO it’s two things.

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.


> REST positioned itself in opposition to 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.


said who? the historical context is that REST comes after SOAP, a very clunky implementation of RPC, and positioned itself as different. It got popular because of its ease of debugging.

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


> said who

Just think about it. For example, try to build isomorphism between REST and any RPC you want.


I don't think it's a very good argument, in CS, you can build isomorphism between many things, especially when both terms are vaguely defined.


You are just trying to figure terminology and usage out on the fly by guessing.

You should be learning, not arguing.


What the hell is isomorphism, I'm a dum dum



Heard of sacarsm?


I think this is confusing different levels of abstraction. It's certainly true that more or less any system can be turned into a sequence of 'procedure calls' with appropriate arguments and return values. You can make the equivalent argument that OO is just the same as structured programming, or that functional programming is just a form of assembly language.

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.


No, it's not. You are making stuff up on the fly. RPC does not mean "Transferring bytes between computers across networks using some protocol."

RPC = Remote Procedural Call.

REST = Representational State Transfer. REST is closer to file transfer than RPC.


I don't know what REST is "really" supposed to mean [1], but every time I saw it used, it basically meant RPC with JSON over HTTP.

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


Here is an example:

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


Even disregarding the argument that those differences wouldn't warrant the asserted distinction,

> 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."


Under the same logic, would you call fetching static file "RPC call"? What about fetching a static file via FTP (per my other example) or Gopher -- is this RPC as well?

In general, do you think it is possible at all to have a request/response protocol which is _not_ RPC?


> Under the same logic, would you call fetching static file "RPC call"? What about fetching a static file via FTP (per my other example) or Gopher -- is this RPC as well?

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.


> I don't know what REST is "really" supposed to mean [1], but every time I saw it used, it basically meant RPC with JSON over HTTP

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.


https://pubs.opengroup.org/onlinepubs/9629399/chap6.htm#tagt...

> 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.


@readonly def readProfile(uid: UserId): UserProfile

@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.


Yes it is. It's a semiformal weak-typed RPC.


RPC does not make idempotency guarantees, REST does. Ergo REST is not RPC.


> RPC does not make idempotency guarantees

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"?


REST is an RPC?



Is there a single example of API that fits that definition of REST though?


HTTP+HTML+CSS (and Javascript) is the REST API used to communicate between clients (browsers) and servers. It's not a coincidence, it was the point of the thesis defining REST to formalize and improve the Web. See: https://www.ics.uci.edu/~fielding/pubs/dissertation/introduc...

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.


HATEOAS is the search term you want to use.

“Hypermedia as the engine of application state.”

PayPal and GitHub APIs tend to work this way. They return a pile of hyperlinks.


If by "tend to work that way" you mean that they satisfy around 20% of the requirements in the post by Roy T. Fielding as opposed to other services that satisfy only 10%...


I don't think you are familiar with the GitHub REST API; none of Fielding's points apply to it.


I honestly don’t know how someone can design a truly HATEOAS API without breaking everyone’s expectations and making it a chore for people to deal with it.

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.


Rest is more like a database: a data/resource model, with a fixed set of operations you can use on this model.

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.


The "data/resource" model also "hides the underlying tables". For example, `POST users/1/emails` might "create" an email, but really its triggering a API call to an external service.

I am not seeing the difference between REST and RPC.


Yes. All these interactions are "request message with immediate response message" protocols. Everything else is hand waving and religion.


I previously worked on an embedded device with a JSON/RPC over WebSockets API. I needed to use the API from a web browser context.

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


Honestly, I think most projects just use a mix of whatever the author likes most (Except for frameworks where it's predetermined). Most would say they use REST but if you look closer they actually aren't. I'm also on the opinion that it doesn't really matter as long as it's documented well.

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.


We use jsonrpc over websockets in production for many years in trading services. It works very well. We use lightweight libraries that look like this [0] and this [1]. It's lightweight, fast, type safe, easy to maintain and debug etc.

We use (compatible) extension of jsonrpc for async generators - yielding elements individially for longer array responses, transparent to the client, to avoid head of line blocking (haven't open source this yet, but will soon).

Other than allowing error code to be string it's all pure jsonrpc 2.0.

Supporting things like adaptive throttling (that dynamically adapts to client's connection speed) was very easy to implement as well.

[0] https://github.com/preludejs/jsonrpc

[1] https://github.com/preludejs/refute


I think worth reading is "A Critique of the Remote Procedure Call Paradigm - 30 years later" (https://blog.carlosgaldino.com/a-critique-of-the-remote-proc...), as well as Tanenbaum's original paper: https://www.cs.vu.nl/~ast/Publications/Papers/euteco-1988.pd.... Sure, it's old, but still very relevant. gRPC does better against his criticisms than the likes of JSON-RPC does.


> Many of the APIs of projects I've worked with look like essentially like RPC style calls.

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.


RPC is a fundamentally broken concept because it relies on both server and client being synchronized, which is brittle and requires whole system upgrades.

Protobufs and things like optional fields etc help, but not that much.

One of the problems is that RPC is procedural and treats the data as parameters to the function.

REST says that the "data" (the resource) is the primary element and is oriented towards the original OO concept of message passing.

RPC has been bad since the days of XDR and SunRPC, has repeated the same problems with CORBA, WS-, XML RPC, JSON RPC, Java RMI, etc etc.

Programmers love it because it looks* like a standard function call, but the problems are hidden under the covers (versioning, network failures, idempotency, retries, etc).


People keep advocating that REST isn't RPC, while almost no one uses it as it was originally proposed, almost every major product with REST APIs has client SDKs that wrap the boilerplate of doing REST calls, just like any RPC wire protocol.


Most API's don't need to receive deeply hierarchical data. Function calls almost always take just a handful of values, and if you need the occasional array, that's quite easy to manage with duplicated keys ("...&filter=price<300&filter=color:red") or numbered suffixes ("...&filter5=price<300&filter6=color:red") or delimiters ("...&filter=price<300,color:red") as preferred.

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.)


So why use JSON for input if you don't need it?

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.


Overengineering is a slippery slope. And the reality is that in 99% of cases, you don't need it.

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.


In my opinion, overengineering is putting JSON into a URL or exposing data as a resource and putting a URL parameter to it in a URL only to avoid an actual request body, for some unclear reason.

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.


How do you return data?


I would say not many data structures requires hierarchy, which kinda is the point of JSON or XML before that.

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...


What problem are you trying to solve?

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.


Fashion. Lots of modern development practices is fashion-driven. JSON-RPC sounds too old.


And I am still struggling about the argument of why SOAP is bad but JSON RPC is good. The xml vs json debate is a tab vs double space debate to me.


I have done a ton of integrations including many SOAP ones and it is because the SOAP standard is way too big, especially if we include XSDs. I have never used a SOAP library which implements the whole spec and it is very common for XSDs generated by one SOAP library to generate incompatible client code if you use another library (e.g. .NET and Java SOAP libraries are not compatible in some edge cases). I have quite often had to manually implement my own bespoke SOAP client (on top of some XML library obviously) due to the popular SOAP libraries not supporting the XSD I got. So SOAP is too big and all or virtually all libraries are more or less broken. I have never personally worked with such a broken mess of a protocol as the SOAP ecosystem. EDIFACT is supposed to be worse but I never worked with it, that was just some of my colleagues.

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.


> EDIFACT is supposed to be worse

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.


> why SOAP is bad but JSON RPC is good

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.


Who said SOAP is bad? SOAP is good. bugs.debian.org provides SOAP API, and it just works. I imagine maybe SOAP was bad when libraries and tools were immature.


What do you think about a rebrand? How about jRPC?


There are some conventions that modern infrastructure expects.

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.


Generally speaking, json-rpc compares negatively to REST and to other RPC standards like gRPC or Thrift. REST is more popular, in large part because of Ruby on Rails, but also because it provides semantic meaning and a system that defines what the api call should be named. No need to look up the remote function, like you need to do for json-rpc. For those who prefer RPC calls, why not use a RPC system that compresses data efficiently and which use IDLs for code generation. Anecdotally, I found that many developers had difficulty understanding json-rpc, because it was so close to REST apis


The problem with using some standard like json-rpc instead of ad hoc API (aka fetch this URL and you'll receive this json) is that one day some 3rd party will use some obscure feature of that standard (like tunneling via email) and you'll be forced to implement it. Eventually every 3rd party will have slightly different implementation of that standard and you will have to make various customer specific workarounds. If it is ad hoc API, they tend to consume it as is and don't have stupid requests. Also every customer updates to different version of the standard at different times so it will be nightmare, where as if it is ad hoc API, everybody write it once and never touches it again. I find ad hoc apis vastly superior and more stable (as long as they remain simple).


Because it's stateful. And a JSON-RPC call is not guaranteed to get something in return. When abstracted with timeouts and callbacks it works nice though. The big advantage is that it can run via any transport protocol, while compared to HTTP you have the transport protocol built in.


I use JSON-RPC as a standard to command devices that connect to a message bus (I use NATS: https://nats.io/). I wouldn't use it as an alternative to REST or GraphQL, they have different goals/use cases.



Lack of tooling, plus gRPC being faster and safer than JSON-RPC while being as convenient as JSON-RPC except for requiring tooling. Also it brands itself as an alternative to REST while gRPC is branded more as being complementary to REST. Disliking REST is a huge hangup for me.


What is wrong with disliking rest? The more I use rest and the older I get the less I like it. Rest always looks so simple at first but then you realize that e.g. the size of the filter clauses for listing some resource exceed the maximum reasonable URL and you need to turn it into a post. And it is also very common that a lot of what you are doing does not in any way match to any easy to grok resources so you just end up with some kind of RPC anyway. I feel I waste too much time on pointless choices when designing Rest API:s and that designing an RPC API often is more straightforward.

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.


I find it useful to link tightly coupled front and backends where scalability/load balancers aren’t a great concern and the API isn’t intended to be public. Otherwise I’d go for REST and openapi

Lately I’ve been experimenting with using a generic proxy object on the client corresponding to a server side object that is the actual API, and defining a typescript interface that both the proxy and server version implement. It makes invoking backend APIs a lot nicer.


The spec promotes it as transport-agnostic. Out of interest, has anyone come across JSON-RPC transported over anything other than HTTP?


I believe some text editors call LSP processes over sockets using JSON-RPC.


I somehow thought bitcoind used JSON-RPC over TCP, but it seems that it's actually HTTP? I don't understand why HTTP is used.



I like rpc because of its simplicity. Function call done over the wire. I want to just invoke a function somewhere without worrying about constraints of “style” (rest, graph quality, etc). Whether it’s json, xml, grpc etc underneath doesn’t really matter to me.


Every time I go to use JSON it is great until I remember it cannot represent 64 bit integers.


This is a Javascript issue, not a JSON issue. The JSON spec doesn't apply any limits on the number type. ECMA-404 states:

> 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.


Yep, if you use e.g. Java's jackson, you can configure it easily to deserialize numbers as BigInteger or BigDecimal instances. Useful trick if you need it and it really works. You can have arbitrary precision numbers in Json. Of course this does challenge most other parsers out there.


Huh? The json number spec does not specify a precision or size. Any limitation is from your parser.


Same. We use 64-bit ints as "Entity IDs", and countless other stuff. Saying that it's a "parser problem" is not really helpful, bc many parsers refuse to support 64-bit ints, even if they could, bc compatibility with JavaScript is more important than 64-bit ints for them. For example: jsonnet


Use https://www.npmjs.com/package/json-bigint

I think that one day browser will support it natively.


In Javascript, when do you need to represent something larger than 2^53 anyway?


>It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, websockets, or in many various message passing environments.

I think tRPC sort of implements a transport layer for (a superset of) this, no?


I am using something similar to JSON-RPC over websockets in a personal project. It is far less of a hassle than HTTP, 8x faster, and less prone to failure.


I was told by the author of tRPC, that they use JSON-RPC behind the scene.


Whoa, that's news to me. Is this called out anywhere online? I've been following tRPC a bit and it looks really great. I only wish it wasn't locked into using TypeScript on the server.

It's the dream dev experience I've been chasing. I'm trying to get as close as I can using auto-generated gRPC-Web.


How is gRPC-Web not an RPC approach?


I tried Json rpc, swagger, and json schema for a few use cases at work when we were deciding on our serialization format.

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.


rest(ful) cargo cult

Good luck trying to propose any kind of API that violates the blurry image others devs and architects have of rest(ful) APIs.

No one got ever fired for going for a rest(ful) API.


i would because ** REST for API but i am already too used to write my APIs in protocol buffers so even though I have abandoned gRPC I still use PB and so I need rest-ish approach.


Because JSON and RPC are two separate standards and don't need to be intermingled into a combined standard. It's relatively easy for clients to adapt to any JSON interface when integrating.


RPC is not a standard, though? I believe it's merely a common name for a pattern, calling functions remotely. The details (how requests and responses are serialized and transported) is all that really matters.

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).


My point still stands. It's not difficult to adapt a client for whatever combination of protocols and standards the server wants... RPC over HTTP or WebSockets or TCP or whatever... The server might want to use different protocols depending on the exact use case. There is no need to force the server to use a specific one.

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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: