logo

System Design - API

Last Updated: 2024-01-23

APIs are used to glue web client and servers, or mobile app and its backend, or different backend services within the microservice architecture.

Server listens on a port and respond to the requests that are sent to it, e.g. listen on HTTP port for URL requests, or on RPC port for RPC requests, sometimes both.

The 3 most popular flavors: REST, GraphQL, and RPC.

  • Internal API / Micro-services protocol: gRPC or Thrift
  • External API: HTTP (REST) or gRPC or GraphQL

REST

  • REST is not a protocol, a file format, or a development framework. It’s a set of design constraints: statelessness, HATEOAS(Hypermedia As The Engine Of Application State), and so on.
  • rely on HTTP verbs and organize things as "resources"

GraphQL

  • Solves over-fetching and under-fetching problem in REST.
  • Easy to change API.
  • Created by Facebook, originally used by mobile app calling backends. Github started to use GraphQL for its API since v4, replacing v3 REST API.
  • Relay (https://relay.dev/): Facebook's GraphQL client.

RPC

Usually used in microservices, among the backend services.

3 pupular choices: gRPC / ProtoBuf, Thrift, Avro.

  • Thrift and ProtoBuf are statically typed, while Avro uses a more dynamic approach.
  • Protocol Buffers: came from Google, and used by all Google internal services.
  • Thrift: came from Facebook and used by all Facebook internal services.
  • Thrift has data serialization and RPC framework in one; ProtoBuf is the data serialization, gRPC(open source) or Stubby(Google's internal) is the RPC framework.

RPC Framework: You define the interface (e.g. in .proto files, in a language-independent way), the RPC framework will generate the server stub and the client stub and things to glue them together (network). The client will use ClientStub when it want to talk to the server; the server will implement the ServerStub.

gRPC / Protocol Buffers

  • Developed by Google. Google internally uses Protocol Buffers as its primary file format.
  • Language neutral.
  • Uses HTTP/2 for transport, supports full-duplex streams between client and server. HTTP is NOT exposed to the API designer, gRPC will generate subs and skeletons to hide tHTTP from the client and the server.
  • Protocol Buffers is similar to Thrift. It provides a compiler and a set of libraries that a developer can use to serialize data. A developer defines the structure or schema of a dataset in a file and compiles it with the Protocol Buffers compiler, which generates the code that can then be used to easily read or write that data.
  • Compared to Thrift, Protocol Buffers support a smaller set of languages. Currently, it supports C++, Java, and Python. In addition, unlike Thrift, which provides tools for both data serialization and building remote services, Protocol Buffers is primarily a data serialization format. It can be used for defining remote services, but it is not tied to any RPC (remote procedure call) protocol.

Thrift

  • Language-independent.
  • Supports a variety of languages, including C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml, Delphi, and other languages.
  • Thrift provides a code-generation tool and a set of libraries for serializing data and transmitting it across a network. It abstracts the mechanism for serializing data and transporting it across a network. Thus, it allows an application developer to focus on core application logic, rather than worry about how to serialize data and transmit it reliably and efficiently across a network.
  • With Thrift, an application developer defines data types and service interface in a language-neutral interface definition file. The services defined in an interface definition file is provided by a server application and used by a client application. The Thrift compiler compiles this file and generates code that a developer can then use to quickly build client and server applications.
  • A Thrift-based server and client can run on the same computer or different computers on a network. Similarly, the server and client application can be developed using the same programming language or different programming languages.

Avro

  • Language-independent.
  • Supports rich data structures, including nested data.
  • Schema(in JSON) is stored along with data. Therefore, an Avro file can be later read by any application. In addition, since schema is stored along with data, each datum is written without per-value overheads, making serialization fast and compact.
  • When data is exchanged over a network using Avro, the sender and receiver exchange schemas during an initial connection handshake.
  • Avro automatically handles field addition and removal, and forward and backward compatibility—all without any awareness by an application.

WebSocket

  • a completely different protocol distinct from HTTP, however it uses port 80 and 443 to support HTTP proxies.
  • full-duplex communication channels over a single TCP connection.

WebSocket vs Socket.io vs ws

Socket.io is built on top of the WebSocket protocal but it is NOT a WebSocket implementation since it adds additional metadata to each packet. (A WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a plain WebSocket server either.)

ws (https://github.com/websockets/ws) is a plain WebSocket server.

RPC vs REST

  • REST/HTTP: the addressable entities are "data entities" (called "resources" in the HTTP specifications), and the behaviors are hidden behind the data (the verbs like GET, POST, DELETE).
  • Remote Procedure Call (RPC): the addressable entities are procedures, and the data is hidden behind the procedures. (However Google is suggesting a resource oriented design even for gRPC: https://cloud.google.com/apis/design/resources)

Best Practices

Both communication ends (clients and servers) must respect communication best practices, to ensure a stable and reliable distributed system.

  • Servers: report meaningful error status, signal when they run out of capacity.
  • Clients: throttle to send fewer requests to capacity restrained servers.

API Gateway

The API gateway pattern: every request needs to go through API Gateway in order to consume the underlying APIs.

API Gateways will perform non-business logic functions: auth, logging/telemetry reporting, monitoring, analytics, quota, rate-limiting, security, anti-abuse, routing, load balancing, HTTP to gRPC transcoding, etc.

  • Basic API Gateways: API call routing, authentication, authorization, quota enforcement. Examples: Amazon API Gateway, Google Cloud Endpoints(based on NGINX).
  • Advanced API Gateways: more features, like custom code, XML and JSON transformation, advanced API management. Examples: Google's Apigee Edge, Salesforce's Mulesoft.

Landscape

Cloud

  • AWS API Gateway
  • GCP Cloud Endpoint
  • Azure API Management
  • (Google's) Apigee API Gateway

OSS

  • Kong: Enterprise fully managed, otherwise not fully managed
  • Ambassador: based on Envoy, not fully managed
  • istio

OpenAPI vs gRPC

OpenAPI(https://www.openapis.org/) and gRPC(https://grpc.io/) have a lot in common:

  • both open-source.
  • both describe APIs: OpenAPI describes REST API in a JSON or YAML file, while gRPC uses ProtoBuf.

Notes:

  • ALL Google internal APIs are based on gRPC (actually gPRC's predecessor, Stubby).
  • Apigee is one of the founding members of OpenAPI, and it was acquired by Google in 2016; gRPC was from Google. So Google actually supports both...
  • gRPC is part of Cloud Native Computing Foundation and OpenAPI is part of Linux Foundation.

Guides

Google's API design decisions: https://aip.dev/

Google's API Style Guide: https://cloud.google.com/apis/design/