We
Shouldn't
Make
REST APIs

REST IS GREAT

  • Tens of millions of APIs built on REST
  • Easy to understand (Text-based protocol)
  • A plethora of great tools for testing and inspecting
  • High-quality implementation of HTTP available

...but it has problems

ALLOW ME TO ILLUSTRATE THEM TO YOU

0
 Advanced issue found

...using some work conversations

Hey Sarvesh. Are you free?

I want to discuss something about <an internal worker>

Hey Bhavya!

Yes, go ahead

I was thinking of creating an API in the worker which would retrigger the job when we send a request to it. Thoughts?

That sounds like a good idea! So will you "POST" to the endpoint or like "PUT" to it?

PROBLEM

Not all operations are intuitively and sufficiently modeled by the REST paradigm

 

In the above example, what will the REST  verb be for the restart endpoint?

Hey Sarvesh. I was looking at how <an internal connector> exposes some fields and it looks incorrect

Hey Murthy.

I was actually facing difficulties representing the API model in XML.

Can you help me in that?

Yeah sure. We need to send our response exactly the way it is represented in the API spec XML as clients will be generating their data models using it

Okay cool.

PROBLEM

There is no formal way to write succinct machine-readable API contracts

0
 Advanced issue found
0
 Advanced issue found
0
 Advanced issue found

XML is cumbersome with limited type support and extremely verbose
 

JSON Schema does solve this problem to a certain degree

Hey Sarvesh. We need to create an API connector for <an internal service> for clients to use. Can you take it up?

Yes sure Sravana.

Hey Sarvesh. We need to create an API connector for <another internal service> for us to use. Can you take it up?

Yes of course Sravana.

PROBLEM

Due to the lack of machine-readable API contracts, humans have to create client libraries or connectors as we call them

 

And humans are expensive which makes maintaining client libraries with the latest and greatest features (for e.g. HTTP/2) difficult

 

Swagger and OpenAPI have tried to solve this but they suffer from the same pitfalls of XML

Sarvesh, can you review this PR?

Yes sure Mayank

I was looking at the new Node backend endpoints and they don't follow REST guidelines.

Can we rename the endpoints to follow XYZ convention?

Yes sure. Although these are not really REST APIs but lets follow the best practices.

PROBLEM

REST isn't actually a protocol, it is an architectural principle. We don't write REST APIs. We write HTTP/JSON and use REST to determine how we structure our API

 

In practice, most implementations don't fully adhere to the REST philosophy and use only a subset of its principles

 

The reason is that it's actually quite challenging to map business logic and operations into the strict REST world

https://dzone.com/articles/5-easy-to-spot-tells-that-your-rest-api-is-not-res

So this is <a new service>. It is an orchestration layer for contract and licensing services and all clients are supposed to interact with it

So Sravana, will <existing service> also call <new service> in future and not <older services> directly?

Yes Chaitanya. That will most likely be the case

Won't the extra network call affect performance?

PROBLEM

REST is built on top of HTTP 1.1 which has a couple of problems

 

Larger numbers of requests take a significant toll on the time needed to load a page

 

REST is text-based (JSON, XML) which is great for inspection but is suboptimal for networks

 

You can compress the JSON, but then you lose the introspectability

SO, IS THERE A SOLUTION?

YES

...and it is a pretty solid attempt to fix all these things

SAY HI TO

gRPC

A high-performance, open-source universal RPC framework

So what's so great about this gRPC thingy anyway?

  • Built by Google
  • Based on HTTP/2
  • Uses protocol buffers for serialization
    • Binary format
    • More compact than JSON and other text-formats
    • Type-safe
  • Layered and pluggable (monitoring, auth, etc)

gRPC IDL

INTERFACE DEFINITION LANGUAGE

0
 Advanced issue found
syntax = "proto3";
package helloworld;

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

RUN THE COMPILER

PROTOC

0
 Advanced issue found
protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative helloworld/helloworld.proto
protoc --plugin=protoc-gen-grpc-java --grpc-java_out="$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" helloworld/helloworld.proto
protoc --plugin=protoc-gen-grpc-python --grpc-python_out="$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" helloworld/helloworld.proto

HERE'S THE MAGIC!

GENERATED CLIENT SIDE CODE

public static final class GreeterBlockingStub extends AbstractBlockingStub<GreeterBlockingStub> {

    private GreeterBlockingStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
      super(channel, callOptions);
    }

    @java.lang.Override
    protected GreeterBlockingStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
      return new GreeterBlockingStub(channel, callOptions);
    }

    /**
     * Sends a greeting
     */
    public io.grpc.examples.helloworld.HelloReply sayHello(HelloRequest request) {
      return blockingUnaryCall(
      getChannel(), getSayHelloMethod(), getCallOptions(), request);
    }
}

HERE'S THE MAGIC!

GENERATED SERVER SIDE STUB

public static abstract class GreeterImplBase implements io.grpc.BindableService {

    /**
     * Sends a greeting
     */
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
      asyncUnimplementedUnaryCall(getSayHelloMethod(), responseObserver);
    }

    @java.lang.Override 
    public final io.grpc.ServerServiceDefinition bindService() {
      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
          .addMethod(
            getSayHelloMethod(),
            asyncUnaryCall(
              new MethodHandlers<
                io.grpc.examples.helloworld.HelloRequest,
                io.grpc.examples.helloworld.HelloReply>(
                  this, METHODID_SAY_HELLO)))
          .build();
    }
}

gRPC SUPPORTED LANGUAGES

For generating clients

0
 Advanced issue found
  • Android Java
  • C# / .NET
  • C++
  • Dart
  • Go
  • Java
  • Kotlin / JVM
  • Node.js
  • Objective-C
  • PHP
  • Python
  • Ruby
  • C++
  • Java
  • Go
  • Python
  • Node.js
  • Ruby
  • C#

gRPC SUPPORTED LANGUAGES

For generating server stubs

PERFORMANCE

RESPONSE TIMES

https://www.yonego.com/nl/why-milliseconds-matter/#gref

PERFORMANCE

https://nilsmagnus.github.io/post/proto-json-sizes/

// Sample response body
{
  "query": "myQuery",
  "page_number": 42,
  "result_per_page": 100,
  "tickers": [
    {
      "name": "rPs",
      "value": 9.768923
    },
    {
      "name": "WEo",
      "value": 6.067048
    }
  ]
}

MESSAGE SIZE (in bytes)

PERFORMANCE

MESSAGE SIZE (in bytes)

https://nilsmagnus.github.io/post/proto-json-sizes/

no of tickers size raw JSON size protobuf
0 58 13
1 102 25
2 133 37
10 396 133
20 724 253
200 6578 2413
2000 65250 24013

PERFORMANCE

MESSAGE SIZE (in bytes)

https://nilsmagnus.github.io/post/proto-json-sizes/

no of tickers size gzipped JSON size gzipped protobuf
0 82 42
1 125 54
2 142 64
10 235 127
20 331 230
200 1970 1629
2000 17539 14808

OTHER SUPPORTED PRODUCTION LEVEL FEATURES

  • Timeouts, cancellation
  • Middleware (Authentication, Metrics)
  • Server-side streaming
  • Bi-directional streaming

THE BAD & THE UGLY

  • Load balancing is available but immature
  • Being a new ecosystem, tooling support is still growing
    • curl is not available
  • Documentation is still under development
  • Error handling is not that great
    • Cannot send error response bodies (HTML pages)
    • No standardized set of error codes; onus on the server to define the error codes
  • Browser support is limited

Where should we use or not use gRPC?

  • Full-fledged browser support is not available
    • But if our frontends are built on the BFF framework, we can continue using REST for frontend-to-server communication and use gRPC to communicate between services
  • gRPC is a very good fit for communication between microservices inside a closed network

Who is using gRPC in production?

  • Almost all of Google has transitioned to gRPC
    • Internally known as stubby
  • Netflix
  • Cisco
  • Cockroach Labs
  • Twilio

REFERENCES AND FURTHER READING

  • https://code.tutsplus.com/tutorials/rest-vs-grpc-battle-of-the-apis--cms-30711

  • https://www.youtube.com/watch?v=ZmGyeseXz8M

  • https://grpc.io/docs/languages/java/quickstart/

  • https://medium.com/@EmperorRXF/evaluating-performance-of-rest-vs-grpc-1b8bdf0b22da

  • https://nilsmagnus.github.io/post/proto-json-sizes/

  • https://www.youtube.com/watch?v=RoXT_Rkg8LA

Thank
You.

We shouldn't make REST APIs

By Sarvesh Raj

We shouldn't make REST APIs

  • 105