Search code examples
restapigraphqlrpc

API which is REST-like but also uses mutations like GraphQL?


Would there be any huge flaws with designing an API so that it is REST-like in that there are GET endpoints for resources (i.e /courses, /professors, /students) but all POST/PUT/DELETE use a single endpoint (i.e /actions) with a payload which specifies both how the request should be handled and the necessary information needed to handle it (i.e { action_name = "create_course", name = "math" } or { action_name = "drop_course", course_id = 5 }).


Solution

  • Would there be any huge flaws

    Caching.

    Caching is an important element in the REST architectural style; "large-grain hypermedia data" wants to be cached so that you aren't repeatedly sending the same data over the network again and against. In HTTP, we've got an RFC dedicated to caching semantics. This include cache invalidation -- generic http aware components will know to invalidate a cache entry if there is a successful unsafe request.

    So by routing all of your unsafe requests to a single endpoint, the generic components end up applying the cache invalidation to the actions endpoint, rather than to any of the resources that are actually being changed by the request.

    DELETE has an additional flaw -- it doesn't take a payload

    A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.

    So any generic component participating in the message exchange is going to assume that the /actions endpoint has been deleted, not some arbitrary resource referenced by the payload.

    PUT has a somewhat different problem

    The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.

    So in your case the generic components are going to be assuming that your messages are in fact proposed representations of the /actions resource.

    The point of the uniform interface is that everybody agrees about the semantics, and you can extract value from generic HTTP aware libraries that don't understand your specific domain. When you start goofing on the semantics, you accept responsibility for any loss of property that may occur.

    Remote authoring semantics (PUT/PATCH/DELETE) really don't fit well with a single /actions endpoint.

    POST does a bit better, in that the constraints are minimal. To some degree, POSTing everything to a single endpoint reduces HTTP from an application protocol to a transport protocol.

    Note that GraphQL, and SOAP before it, accepted this trade off. Horses for courses.

    What are "generic HTTP components"?

    Browsers, caches, proxies, servers, crawlers.... Anything that has access to the messages being exchanged, but not the out of band specifics of your domain.

    Also by cache invalidation of successful unsafe requests do you mean that if tracking recipes, a GET to /recipes would be cached until a GET/PUT/PATCH/DELETE request to /recipes comes along?

    The response to a GET to /recipes might be cached until a POST/PUT/PATCH/DELETE to /recipes comes along.

    Whether or not it would be cached depends on the semantics of the caching headers provided with the response.

    And of course, my local cache isn't going to know about requests that you make, so I may be looking at stale copy. I'll get "read your own writes" semantics for my own changes, though.

    The more interesting case is a reverse proxy in front of the server, which would invalidate its cache entries after a successful unsafe request, reducing the load on the server while still serving fresh data.