> RPC with status codes

Yes. All endpoints POST, JSON in, JSON out (or whatever) and meaningful HTTP status codes. It's a great sweet spot.

Of course, this works only for apps that fetch() and createElement() the UI. But that's a lot of apps.

If I don't want to use an RPC framework or whatever I just do:

  {
    method: "makeBooking",
    argument: {
      one: 1,
      two: "too",
    },
     ...
  }
And have a dictionary in my server mapping method names to the actual functions.

All functions take one param (a dictionary with the data), validate it, use it and return another single dictionary along with appropriate status code.

You can add versions and such but at that point you just use JSON-RPC.

This kind of setup can be much better than REST APIs for certain usecases

>All endpoints POST

This makes automating things like retrying network calls hell. You can safely assume a GET will be idempotent, and safely retry on failure with delay. A POST might, or might not also empty your bank account.

HTTP verbs are not just for decoration.

If you're doing well-formed RPC over POST, as opposed to ad hoc RPC (which, let's be honest, is the accurate description for many "REST" APIs in the wild), then requests and responses should have something like an `id` field, e.g. in JSON-RPC:

https://www.jsonrpc.org/specification#request_object

Commonly, servers shouldn't accept duplicate request IDs outside of unambiguous do-over conditions. The details will be in the implementations of server and client, as they should be, i.e. not in the specification of the RPC protocol.

> not just for decoration

Still, they are just a convention.

When you are retrying an API, you are calling the API, you know whether its a getBookings() or a addBooking() API. So write the client code based on that.

Instead of the API developer making sure GET /bookings is idempotent, he is going to be making sure getBookings() is idempotent. Really, what is the difference?

As for the benefits, you get a uniform interface, no quirks with URL encoding, no nonsense with browsers pre-loading, etc etc,. It's basically full control with zero surprises.

The only drawback is with cookies. Samesite: Lax depends on you using GET for idempotent actions and POST for unsafe actions. However, I am advocating the use of this only for "fetch() + createElement() = UI" kind of app, where you will use tokens for everything anyways.