REST · GraphQL · gRPC · API design

REST vs GraphQL vs gRPC: differences and when to use each

An accurate comparison of REST, GraphQL and gRPC: transport, payload formats, contracts, streaming, caching, browser support and tooling, plus how to test each style.

By · · 8 min read

REST, GraphQL and gRPC are three ways to design an API. REST exposes resources at URLs and uses HTTP methods and status codes, usually with JSON; GraphQL exposes one endpoint where the client sends a query describing exactly which fields it wants from a typed schema; gRPC is a remote procedure call framework that calls typed methods over HTTP/2 with compact Protocol Buffers messages and built-in streaming. In short: REST is the default for public and web APIs, GraphQL suits clients with varied data needs, and gRPC suits fast, strongly typed service-to-service calls.

None of them is better in general. They make different trade-offs, and many systems use more than one: gRPC between internal services, REST or GraphQL at the edge. This guide compares them point by point, sums it up in a table and shows how to test each one.

REST in brief

REST (Representational State Transfer) is an architectural style described by Roy Fielding in 2000, not a protocol or a specification. In practice, a "REST API" means:

  • Resources at URLs: /orders, /orders/42, /orders/42/items.
  • HTTP methods for intent: GET reads, POST creates, PUT replaces, PATCH updates, DELETE removes. GET, PUT and DELETE are idempotent by definition.
  • HTTP status codes for outcomes: 200, 201, 404, 409, 422 and so on (see the HTTP status codes guide).
  • A representation format, almost always JSON today, negotiated with Content-Type and Accept.
HTTP
GET /v1/orders/42 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOi...

REST has no built-in schema. Teams usually describe the contract with OpenAPI, which also drives documentation, client generation and contract tests (see testing an API against its OpenAPI spec).

Strengths: works everywhere HTTP works, including browsers, curl and every language; HTTP caching, CDNs and proxies understand it; easy to debug by reading requests. Weaknesses: fixed response shapes cause over-fetching (fields you do not need) and under-fetching (several round trips to assemble one screen); versioning and consistency depend on discipline.

GraphQL in brief

GraphQL is a query language and runtime, published by Facebook in 2015 and now maintained under the GraphQL Foundation. The server publishes a strongly typed schema; the client sends an operation (query, mutation or subscription) that selects exactly the fields it needs.

GraphQL
query Order($id: ID!) {
  order(id: $id) {
    id
    status
    customer { name email }
    items { sku quantity }
  }
}

Over HTTP this is typically a POST to a single endpoint such as /graphql with a JSON body containing query and variables. Queries can also be sent with GET. The response is JSON with a data key and, when something failed, an errors array. Many servers return HTTP 200 even when errors is present, so a client or test must check the body, not only the status. The GraphQL over HTTP specification, being developed at the GraphQL Foundation, defines more precise status code behaviour for the application/graphql-response+json media type.

Subscriptions (server-pushed events) usually run over WebSocket using the graphql-transport-ws protocol (the graphql-ws library) or the older subscriptions-transport-ws protocol; some servers use Server-Sent Events instead.

Strengths: one round trip for nested data, no over-fetching, a self-describing schema with introspection, strong tooling for exploring and autocompleting queries, and an easy way to evolve the API by adding fields and deprecating old ones. Weaknesses: HTTP caching is harder because most traffic is POST to one URL; the server must defend against expensive queries (depth and complexity limits, persisted queries); naive resolvers cause N+1 database queries unless batched (for example with DataLoader); and error handling is less uniform than HTTP status codes.

gRPC in brief

gRPC is an open-source RPC framework, released by Google in 2015 and now a Cloud Native Computing Foundation project. You define services and messages in a .proto file, and code generators produce typed client stubs and server interfaces in many languages.

Protobuf
syntax = "proto3";

service OrderService {
  rpc GetOrder (GetOrderRequest) returns (Order);
  rpc WatchOrders (WatchRequest) returns (stream OrderEvent);
}

message GetOrderRequest { string id = 1; }
message Order { string id = 1; string status = 2; repeated Item items = 3; }

gRPC runs over HTTP/2 and encodes messages with Protocol Buffers, a compact binary format, by default. Every call is a POST to /package.Service/Method; the result is reported in a gRPC status code (0 OK, 5 NOT_FOUND, 14 UNAVAILABLE, 16 UNAUTHENTICATED and so on) sent in HTTP/2 trailers, alongside key-value metadata. There are four call types: unary (one request, one response), server streaming, client streaming and bidirectional streaming. Clients set deadlines, and cancellation propagates to the server.

Strengths: small payloads and efficient parsing, strict contracts with generated code, first-class streaming in both directions, and deadlines, cancellation and status codes built into the protocol. Weaknesses: browsers cannot speak native gRPC; binary payloads are not human-readable without tooling; HTTP caching and CDNs do not apply; and long-lived HTTP/2 connections need load balancing that understands HTTP/2 (layer 7) to spread calls across servers.

gRPC in the browser: gRPC-Web

Browser JavaScript has no API to control HTTP/2 framing or read trailers, which native gRPC requires. gRPC-Web is a variant protocol that browsers can use; it normally needs a translating proxy (Envoy is the common choice) or server-side support in the framework. gRPC-Web supports unary and server-streaming calls, but not client-streaming or bidirectional streaming. Alternatives such as the Connect protocol take a similar approach. If a browser frontend is the main client, this extra layer is a real cost to weigh.

Comparison table

RESTGraphQLgRPC
ModelResources and HTTP methodsTyped schema, client-selected fieldsTyped service methods (RPC)
TransportHTTP/1.1, HTTP/2 or HTTP/3HTTP (usually POST); WebSocket for subscriptionsHTTP/2
PayloadUsually JSON; any media typeJSONProtocol Buffers (binary) by default
ContractOptional (OpenAPI)Required schema (SDL), introspectableRequired .proto files
Code generationOptional, from OpenAPIOptional, from the schemaStandard workflow
EndpointsMany URLsOne URLOne path per method
ErrorsHTTP status codeserrors array in the body, often with HTTP 200gRPC status codes in trailers
StreamingNot built in; use SSE, WebSocket or chunked responsesSubscriptions (usually over WebSocket)Server, client and bidirectional streams
HTTP cachingNative (Cache-Control, ETag, CDNs)Limited; persisted queries over GET help; client caches commonNot applicable
Browser supportNativeNativeNeeds gRPC-Web and usually a proxy
Human-readable on the wireYesYesNo (binary)
Typical usePublic APIs, web and mobile backendsFrontends with varied data needs, aggregating many servicesInternal microservices, low-latency and streaming systems

When to choose which

Choose REST when the API is public or consumed by many unknown clients, you want HTTP caching and CDNs to work for you, the data maps naturally onto resources, or you want the lowest barrier to entry for consumers. It remains the safest default.

Choose GraphQL when you have several clients (web, iOS, Android, partners) that need different shapes of the same data, screens assemble data from many entities or backend services, and your team is ready to invest in query cost limits, batching and schema governance. A GraphQL layer in front of existing REST or gRPC services is a common pattern.

Choose gRPC when both ends are services you control, latency and payload size matter, you want strict contracts with generated clients in several languages, or you need streaming in both directions. It is a frequent choice for internal microservice traffic, often behind a REST or GraphQL gateway for external clients.

Mixing is normal. A typical layout is gRPC between internal services, a GraphQL or REST API for the frontend, and REST webhooks for partners. Choose per boundary, not per company.

How to test each style in Swastik Codes

Swastik Codes has a request type for each style, and the three share environments, variables, auth helpers and swc.* test scripts (Postman's pm.* syntax also runs unchanged). Saved requests of all three types can be run in the collection runner and on monitors.

REST

Use the REST API client: pick a method, enter the URL, add query parameters, headers, a body (JSON, form-data, URL-encoded, binary) and an auth type, and send. Then assert on status, headers and the body:

JavaScript
swc.test('order is returned', function () {
  swc.response.to.have.status(200);
  const order = swc.response.json();
  swc.expect(order.id).to.eql('42');
});

If you have an OpenAPI document, import it to get one request per operation. For step-by-step guidance, see how to test a REST API online and validating responses with JSON Schema.

GraphQL

Create a request and switch its type to GraphQL with the GraphQL client. Fetch the schema by introspection (or load SDL or an introspection JSON if introspection is disabled), browse types in the schema explorer, and write the query with autocomplete and a separate variables editor. Because GraphQL often returns 200 with errors, test the body:

JavaScript
swc.test('no GraphQL errors', function () {
  const body = swc.response.json();
  swc.expect(body.errors, JSON.stringify(body.errors)).to.be.undefined;
  swc.expect(body.data.order.status).to.be.a('string');
});

Subscriptions connect over WebSocket with graphql-transport-ws or the legacy graphql-ws protocol. The GraphQL client guide walks through each step.

gRPC

Create a gRPC request with the gRPC client. Load the service through server reflection, .proto files (single files or a folder, with import paths) or a .proto URL. Pick a method, edit the request message as JSON (an example message is generated from the schema), add metadata and a deadline, choose TLS, and invoke. Unary, server-streaming, client-streaming and bidirectional calls are all supported, and you can paste a grpcurl command or copy a request as one.

JavaScript
swc.test('call succeeded', function () {
  swc.response.to.have.statusCode(0); // gRPC OK
  swc.expect(swc.response.messages.count()).to.equal(1);
});

The web app does not need a gRPC-Web proxy, because the swastik.codes server makes native gRPC calls over HTTP/2 on your behalf. That server only reaches public hosts; for localhost:50051 use the desktop app for Windows and Linux. See the gRPC client guide for streaming and scripts.

Frequently asked questions

Is GraphQL faster than REST?

Not inherently. GraphQL can reduce the number of round trips and the amount of data sent, which helps on slow mobile networks, but each query may do more work on the server, and HTTP caching is harder. A well-designed REST endpoint with caching can be faster for the same screen.

Is gRPC faster than REST?

Usually, for service-to-service calls: Protocol Buffers are smaller and quicker to parse than JSON, and HTTP/2 multiplexes calls over one connection. The difference matters most at high request rates or with large messages. For a few calls from a browser, network latency dominates and the gain is small.

Can a browser call a gRPC API directly?

Not with native gRPC. Browsers need gRPC-Web (or a similar protocol such as Connect), which usually requires a proxy or server support, and it does not support client or bidirectional streaming. Testing tools that relay calls through a server, like the Swastik Codes web app, avoid this limitation for testing purposes.

Does GraphQL replace REST?

No. GraphQL solves specific problems, mainly varied client data needs and aggregation across services. Many organisations run both, and GraphQL servers often call REST or gRPC services behind the scenes.

Can I use REST, GraphQL and gRPC in the same project?

Yes, and it is common: gRPC internally, GraphQL or REST for frontends, REST for public and partner APIs. Keep each contract in its own schema format (OpenAPI, SDL, .proto) and test every layer.

Which is best for real-time data?

gRPC streaming for service-to-service streams, GraphQL subscriptions when your API is already GraphQL, and for plain HTTP APIs, Server-Sent Events or WebSocket alongside REST. See testing Server-Sent Events and testing a WebSocket API.

Try it on your own API

Swastik Codes runs in the browser and is free to use. Create an account to send requests, sync your workspaces and invite your team.