---
title: "06 — API Styles — REST, SOAP, GraphQL, and OpenAPI as One Spectrum"
uid: backend-api-styles
tags: ["roadmap:backend", "rest", "openapi", "soap", "api", "rpc", "graphql"]
excerpt: "REST, SOAP, GraphQL, OpenAPI: not a menu to pick from. One spectrum — how rigidly the server fixes the shape of requests and responses — with OpenAPI as the contract layer that makes any of them safe."
date: 2026-08-13T03:28:26+0000
source: https://www.aveshina.my.id/en/blog/backend-api-styles
---

REST, SOAP, GraphQL, OpenAPI — four names I used to treat as a menu I had to pick from. Writing them down collapsed them into one spectrum: **they differ along a single axis — how rigidly the server fixes the shape of requests and responses — and OpenAPI is the contract layer that makes any of them safe to consume.** [1]

The framing that helped is what each style assumes about the client. SOAP assumes the client wants a strict, formal contract and will generate code from it. REST assumes the client speaks HTTP verbs on resource URLs and is happy with whatever shape the server returns. GraphQL assumes the client knows exactly which fields it wants and should be allowed to declare them. OpenAPI isn't an API style at all — it's a way to write the contract down so any REST API can be documented, validated, and code-generated against.

```figure
<svg viewBox="0 0 740 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A horizontal spectrum arrow from 'server fixes shape' on the left to 'client declares shape' on the right. Four pills sit on the arrow: SOAP (rigid XML contract), REST (HTTP verbs on resources), gRPC (typed binary RPC), GraphQL (client-declared fields). Above the REST pill, a document icon labelled 'OpenAPI' is connected by a dashed line, indicating OpenAPI is a contract layer, not a style.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- spectrum arrow -->
    <line x1="60" y1="170" x2="680" y2="170" stroke="#64748b" stroke-width="2"/>
    <polygon points="680,170 670,165 670,175" fill="#64748b"/>
    <text x="60" y="195" font-size="11" font-weight="700" fill="#64748b">server fixes shape (rigid)</text>
    <text x="680" y="195" font-size="11" font-weight="700" fill="#64748b" text-anchor="end">client declares shape (flexible)</text>

    <!-- SOAP -->
    <rect x="80" y="140" width="110" height="40" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="135" y="165" font-size="13" font-weight="700" fill="#7f1d1d" text-anchor="middle">SOAP</text>
    <text x="135" y="115" font-size="10" fill="#7f1d1d" text-anchor="middle">strict XML contract</text>

    <!-- REST -->
    <rect x="240" y="140" width="110" height="40" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="295" y="165" font-size="13" font-weight="700" fill="#422006" text-anchor="middle">REST</text>
    <text x="295" y="115" font-size="10" fill="#422006" text-anchor="middle">HTTP verbs on resources</text>

    <!-- gRPC -->
    <rect x="410" y="140" width="110" height="40" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="465" y="165" font-size="13" font-weight="700" fill="#1e1b4b" text-anchor="middle">gRPC</text>
    <text x="465" y="115" font-size="10" fill="#1e1b4b" text-anchor="middle">typed binary RPC</text>

    <!-- GraphQL -->
    <rect x="575" y="140" width="110" height="40" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="630" y="165" font-size="13" font-weight="700" fill="#052e16" text-anchor="middle">GraphQL</text>
    <text x="630" y="115" font-size="10" fill="#052e16" text-anchor="middle">client-declared fields</text>

    <!-- OpenAPI hovering above REST -->
    <rect x="250" y="50" width="90" height="36" rx="6" fill="#ffffff" stroke="#ca8a04" stroke-width="1.5" stroke-dasharray="4 3"/>
    <text x="295" y="73" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">OpenAPI</text>
    <line x1="295" y1="86" x2="295" y2="138" stroke="#ca8a04" stroke-width="1.5" stroke-dasharray="4 3"/>

    <text x="370" y="245" font-size="10" font-style="italic" fill="#64748b" text-anchor="middle">OpenAPI is a contract layer, not a style — it documents REST</text>
  </g>
</svg>
```

## REST: resources, verbs, and statelessness

**REST** (Representational State Transfer) is the default — an architectural style that maps data to **resources** identified by URLs, and uses standard HTTP verbs to act on them [2]. GET /users/42 fetches a user; POST /users creates one; PUT /users/42 updates; DELETE /users/42 removes. The protocol is HTTP; the data format is almost always JSON.

Two principles define REST in practice:

- **Statelessness.** Every request contains everything the server needs to handle it. The server keeps no client state between requests — which makes REST horizontally scalable (any server can handle any request) and simple to reason about.
- **Resource orientation.** URLs name nouns (/users, /orders), not verbs. The HTTP method is the verb. This convention makes REST APIs discoverable and uniform.

REST's strength is its simplicity and the fact that it speaks HTTP natively — every platform, every tool, every firewall understands it. Its weakness is the fixed-shape problem: the server decides what a /users/42 response looks like, and every client gets that same shape whether it needs two fields or forty. That tension is exactly what GraphQL was built to address.

## SOAP: the strict-contract enterprise option

**SOAP** (Simple Object Access Protocol) is the formal, XML-based message protocol that predates REST [3]. A SOAP API exposes operations (not resources) described by a WSDL contract — a strict XML schema that defines every operation, its inputs, and its outputs. Clients generate strongly-typed code from the WSDL; messages are XML envelopes exchanged over HTTP (or other transports).

SOAP's appeal is its rigor: the contract is formal, the messaging has built-in error handling and ACID-style reliability extensions (WS-*), and the code generation makes large APIs tractable. Its cost is weight — XML is verbose, the envelope overhead is significant, and the tooling is heavy. SOAP is now mostly an enterprise and legacy concern (payment gateways, banking, SOAP-era integrations). New APIs rarely choose SOAP, but backend devs meet it in existing systems.

## gRPC: typed binary RPC for service-to-service

**gRPC** sits off the HTTP-JSON path — it's a high-performance RPC framework using **Protocol Buffers** as the interface definition language and binary wire format [4][5]. A .proto file declares the service (a set of methods with typed request/response messages), and the gRPC tooling generates client and server stubs in multiple languages. The wire format is binary (compact, fast), and gRPC runs over HTTP/2.

gRPC's strengths are performance and strong typing across languages — a Go service and a Python client share the same proto-defined contract. It's the common choice for internal service-to-service communication in microservice architectures where latency and type safety matter more than human-readability. Its weakness for public APIs is exactly that — the binary format and proto requirement make it less approachable than JSON-over-HTTP for outside consumers.

## GraphQL: the client declares the shape

**GraphQL** flips the authority: instead of the server fixing the shape, the client writes a query describing exactly the fields it wants, and the server returns precisely those fields [6]. One endpoint, a typed schema, and queries that match the shape of the response.

The problem GraphQL solves is the fixed-shape tension under REST:

- **Over-fetching** — GET /users/42 returns thirty fields when the client needs two.
- **Under-fetching** — a screen needs a user and their posts, forcing two REST round trips.

GraphQL lets the client ask for name and posts { title } and get back exactly that. The trade-off is complexity: the server needs resolvers, the schema becomes a governance concern, and N+1 queries, authorization at every field, and caching all become the team's problem. GraphQL pays off when clients are varied (mobile, web, different screens wanting different slices); it's overkill for a uniform, stable API.

## OpenAPI: the contract layer, not a style

**OpenAPI** (formerly Swagger) is the one that doesn't fit the spectrum because it isn't an API style — it's a **specification format** for describing REST APIs [7]. An OpenAPI document is a YAML or JSON file that declares every endpoint, its parameters, its request body schema, its response schemas, and its authentication requirements. It's the contract REST lacked by default.

What OpenAPI unlocks is tooling. From an OpenAPI spec I can generate:

- **Interactive documentation** (Swagger UI) that lets consumers try endpoints in the browser.
- **Client libraries** in any language — typed code that calls my API, generated automatically.
- **Server stubs** — the skeleton of the API implementation, in my language.
- **Validation** — every request and response checked against the schema at runtime.

The reason I flag OpenAPI separately is that it's the part of REST that brings it close to SOAP and gRPC's contract-first rigor without the weight. A REST API with an OpenAPI spec gets the documentation, type safety, and code generation that SOAP's WSDL used to provide. For any REST API with more than one external consumer, writing and maintaining the OpenAPI spec is now table stakes.

## How I use this

The decision is a matching exercise, not a ranking:

- **Default public API → REST + OpenAPI.** HTTP verbs on resources, JSON responses, and an OpenAPI spec for documentation and code generation. The combination gets 90% of the benefit at 30% of the complexity.
- **Internal service-to-service, latency-sensitive → gRPC.** Typed proto contracts, binary wire, multi-language stubs. The contract is the proto file.
- **Varied clients wanting different slices of the same data → GraphQL.** When over-fetching and under-fetching are real and ongoing pains.
- **Existing enterprise/legacy integration → SOAP.** Not a greenfield choice, but one I'll encounter.

The habit that carries across all of them: **the contract is the API.** Whether it's an OpenAPI spec, a proto file, a GraphQL schema, or a WSDL, the written-down, validated, versioned contract is what makes an API safe to consume and evolve. Without it, I'm shipping vibes over HTTP.

## References

[1] AWS, "What is an API?," 2024. [Online]. Available: [https://aws.amazon.com/what-is/api/](https://aws.amazon.com/what-is/api/)

[2] Red Hat, "What is a REST API?," 2024. [Online]. Available: [https://www.redhat.com/en/topics/api/what-is-a-rest-api](https://www.redhat.com/en/topics/api/what-is-a-rest-api)

[3] "SOAP Web Services Tutorial for Beginners," guru99. [Online]. Available: [https://www.guru99.com/soap-simple-object-access-protocol.html](https://www.guru99.com/soap-simple-object-access-protocol.html)

[4] gRPC Authors, "gRPC." [Online]. Available: [https://grpc.io/](https://grpc.io/)

[5] "What Is gRPC?," Wallarm. [Online]. Available: [https://www.wallarm.com/what/the-concept-of-grpc](https://www.wallarm.com/what/the-concept-of-grpc)

[6] GraphQL Foundation, "Introduction to GraphQL," graphql.org, 2024. [Online]. Available: [https://graphql.org/learn/](https://graphql.org/learn/)

[7] "OpenAPI Specification," swagger.io. [Online]. Available: [https://swagger.io/specification/](https://swagger.io/specification/)

```quiz
Q: Along what single axis do REST, SOAP, gRPC, and GraphQL differ?
- How rigidly the server fixes the shape of requests and responses
- What programming language the server is written in
correct: 0
explain: The spectrum runs from server-fixed shape (SOAP, REST) to client-declared shape (GraphQL). gRPC is typed-binary but still server-defined. The language is orthogonal — each style supports many languages.

Q: REST is stateless. What does that mean, and why does it matter?
- Each request contains everything the server needs; the server keeps no client state between requests, enabling horizontal scalability
- The server remembers every client between requests for performance
correct: 0
explain: Statelessness means any server can handle any request (no session affinity needed), which makes REST horizontally scalable and simple. Sessions/tokens are bolted on top to fake persistence the protocol refuses to provide.

Q: What is OpenAPI, and how does it relate to REST?
- An API style competing with REST
- A specification format for describing REST APIs — a contract layer that enables docs, validation, and code generation
correct: 1
explain: OpenAPI isn't a style; it's a spec format (YAML/JSON) describing endpoints, schemas, and auth. It brings REST the contract-first rigor (docs, typed clients, server stubs) that SOAP's WSDL historically provided.

Q: A screen needs a user plus their three recent posts plus comments on those posts. Under REST this is multiple round trips. Which style was built specifically to solve this?
- SOAP
- GraphQL
correct: 1
explain: GraphQL lets the client declare the nested shape (user, posts, comments) and get it in one query, eliminating the over-/under-fetching that forces multiple REST round trips.

Q: When is gRPC the natural choice over JSON-over-HTTP REST?
- For internal service-to-service calls where latency, binary efficiency, and cross-language typed contracts matter
- For public-facing APIs targeting broad, casual consumers
correct: 0
explain: gRPC's binary proto contracts and HTTP/2 streaming suit internal calls where performance and strong typing across services matter. For public APIs, JSON-over-HTTP's human-readability and ubiquity usually win.
```
