top of page
Search
  • Writer's pictureDean Hiller

Can a violation of REST be good?

I have now had two different teams unanimously vote to violate REST, but this sounds extremely bad. Why would we ever want to violate REST with the clear contracts of CRUD(Create, Read, Update, Delete) simply translated into POST, GET, PUT, DELETE http methods. Let's dig deeper here and use methods/contracts in code to represent where we want to go in the future. Let's also separate this into Customer APIs and Internal APIs.


Customer APIs


For customer APIs, we know that developers are going to want GET, POST, PUT, DELETE, etc. This is a basic REST API which maps to read, create, update, delete(CRUD). There are tools on the market to generate all the REST API documentation as well so we can generate docs on swaggerhub, etc. to deliver to the customer. We have to listen to the customer here even if some endpoints we cannot modify to be compatible with previous versions (ie. they would not longer be backwards compatible if we modified them).


Case Study: GNIP(eventually bought by Twitter) was an API company and because it was REST based had to keep adding GET methods and become polluted with too many methods (This is part of the reason google is trying to move to gRPC in GCP for customers…..it provides a way to be backwards and forwards compatible after changes. More on this down below).


Internal APIs


We found a huge organizational speed increase from having contracts in source code(APIs) between microservices. Contracts were basically a single interface and the data objects/DTOs for requests and responses. So far, most developers probably agree with everything said so far. We assume the same can be said between GUI frontend and backend as well since the API is used on the front end and implemented on the backend. If you are using gRPC, it goes over http2 using POST requests (the request is in the body of the POST request in binary not json).


Next, let’s talk internal apis and not customer apis …


We can allow APIS like this (gRPC requires a data structure for evolution to a backwards compatible method and will not allow the first GET methods below)


GET typescript version:

@GET

function methodName(username:string): Promise<MethodResponse>

GET java/C# version:

@GET

public CompletableFuture<MethodResponse> methodName(String username)


POST typescript version

@POST

function method(request:MethodNameRequest): Promise<MethodResponse>

POST java/C# version

@POST

public CompletableFuture<MethodResponse> method(MethodRequest request))


Let’s explore one tradeoff here. Why did two teams(different companies) say all internal APIs would all be POST and violate typical REST standards....scary right? I had to remember why and document this. Here are the reasons but please read the detail afterwards

  1. It is 1 to 1 with an eventual strategy of moving to gRPC (Do we want to move to gRPC in 3-5 years?)

    • The move is quite seamless since a json DTO is exactly the same as a gRPC request object if done correctly

  2. It is future proof where the GET is not future proof(The POST is future proof)


Let us look at #2 here. Let us say you have 5 clients calling the @GET method. Along comes a new requirement and you need to add an optional company parameter that 1 of those 5 clients needs. We have to expose a new method…


Typescript version:

@GET

function methodName(username:string, company: string | undefined): Promise<MethodResponse>

Java/C# version:

@GET

public MethodResponse methodName(String username, String, company);


We have two choices

  • Keep adding methods and end up in method explosion

    • Twitter scale: this would result in some methods having 50 versions

  • Move over the other 4 calls to clients to this new method (and remove old method)

    • Twitter scale: Many endpoints had over 100 calls to them so you would have to move over 99 clients(no easy feat since you need sign-off from EVERY team!!)

This is what Twitter and Google call not being able to create backward compatible methods and an API that is not evolvable. Let us look at gRPC or an API that maps to gRPC and violates REST since it is just doing a read with a POST ->


Typescript Version:

@POST

function method(request:MethodRequest): Promise<MethodResponse>

Java/C# Version:

@POST

public MethodResponse method(MethodRequest request);


We add a new field ‘company’ to MethodRequest. We start using that field. There is no work to do! All previous clients simply do not change their code and will not be setting the company.


This method yields us with two huge advantages then

  • In large scale enterprise, as we grow, this eliminates a ton of work and rework and prevents adding a ton of technical debt

  • We have a path to seamlessly switch to gRPC in 1-2 years(or earlier if we grow really fast) because the method signature is 100% the same as gRPC

These two teams from two companies ended up choosing speed over REST internally with the intention of going to gRPC. gRPC is a violation of REST when it does reads since it does so over http2 with a POST method and the request is in the body the http POST request.


NOTE(and added bonus): A Platform team can swap generated clients for gRPC clients on all servers and GUIs if we do this correctly today (Just ask me how if you are confused). A Platform team can swap generated ‘scaffolding’ from json REST to gRPC if we do this correctly today. Scaffolding is the generated code between an http call and the controller endpoint (and needs to be written by the platform team for every team to use).


Final Questions to the team

  • Do we want a fully REST compliant API for customers? (Teams answer : yes)

  • Do we eventually want to do gRPC in 3-5 years (Teams answer: yes)

  • What do we want to do today so we have a quick path to gRPC if we want gRPC?

Final answer was yes, let us do all POST requests internally and violate REST. To this day, developers are easily adding more options in the request objects without having to do any rework. They have not switched to gRPC yet but have saved a bunch of time evolving APIs to backward compatible ones by just adding fields to DTOs. In addition to that, the Controllers implement the same API the client uses so contracts are enforced no matter what protocol is being use.


One last note is that if my client is released first supplying the extra 'company' parameter, depending on how they code the client, this method 'can be' forward compatible as well.


Happy coding!!!

421 views0 comments

Recent Posts

See All

Comments


Post: Blog2_Post
bottom of page