What Is REST and How It Differs From RPC and GraphQL
REST, RPC and GraphQL compared: resources versus actions versus queries, caching, versioning, tooling with curl, error handling and how to choose.

People argue about API styles as if one were correct. They are different ways to describe the same interactions, with different strengths in tooling, caching and flexibility. A script author needs to recognise each when reading documentation and know how to call it. The three styles show up in GraphQL from the command line and OpenAPI tools. The examples use curl and an invented orders API.
The same task, three ways
# REST: resource URLs and HTTP methods
curl -s https://api.example.test/orders/42
curl -s -X POST https://api.example.test/orders -d '{"item":"alpha","qty":2}' -H 'Content-Type: application/json'
curl -s -X DELETE https://api.example.test/orders/42
# RPC: call named operations
curl -s -X POST https://api.example.test/rpc -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"orders.get","params":{"id":42}}'
# GraphQL: one endpoint, ask for what you need
curl -s https://api.example.test/graphql -H 'Content-Type: application/json' \
-d '{"query":"{ order(id: 42) { id status items { name qty } } }"}'All three are just HTTP requests with JSON bodies, so any of them can be driven with curl and parsed with jq. They differ in what the request means and in how much the URL and method tell you.
REST
GET /orders list 200
GET /orders/42 read 200, 404
POST /orders create 201 + Location header
PUT /orders/42 replace 200, 204
PATCH /orders/42 update part 200
DELETE /orders/42 delete 204
REST organises an API around resources identified by URLs, and uses HTTP's own vocabulary: methods for the action, status codes for the outcome, headers for caching and content negotiation. Because GET is safe and cacheable, browsers, CDNs and proxies can cache responses without any special knowledge, and tools like curl give a readable experience. The weaknesses: a screen that needs data from several resources makes several requests, responses carry fields the client may not need, and operations that are not CRUD (cancel an order, run a report) fit awkwardly. Many "REST" APIs are in practice resource-style HTTP APIs without full adherence to the constraints, which is fine. The status codes are listed in HTTP status codes developers meet.
RPC
POST /rpc {"method": "orders.cancel", "params": {"id": 42}}
gRPC service Orders { rpc Cancel(CancelRequest) returns (Order); }
RPC (remote procedure call) exposes operations: you call a function by name with parameters and get a result. JSON-RPC wraps this in a small JSON protocol; gRPC uses Protocol Buffers over HTTP/2 with generated clients and streaming. RPC fits actions that do not map to resources, internal service-to-service traffic where speed and strict typing help, and APIs where the operation list is the model (Slack's Web API is largely RPC-shaped, with methods like chat.postMessage). Costs: generic HTTP caching does not apply because everything is POST to one URL, and you lose the discoverability of URLs. gRPC needs special tools (grpcurl) from the shell, not plain curl.
GraphQL
query { order(id: 42) { id status items { name qty } customer { email } } }
mutation { cancelOrder(id: 42) { id status } }
GraphQL has one endpoint and a typed schema; the client sends a query describing exactly the fields and nested relations it wants and gets that shape back. It solves over- and under-fetching for clients with different needs (a mobile app and a dashboard), and its schema can be introspected for tooling. The costs are on the server side (query cost control, caching by query, N+1 problems), and for scripts: every request is a POST with a query string, errors usually come back as HTTP 200 with an errors array, so status-code checks are not enough, as in parsing API errors in scripts. Public GraphQL APIs often limit query depth or complexity.
Choosing
Public API for many unknown clients, cacheable data REST (or REST + OpenAPI)
Actions and commands, internal services, streaming RPC / gRPC
Many client types needing different slices of related data GraphQL
Scripts, ops tooling, webhooks REST/JSON over HTTP is easiest
Consistency within one API beats purity: naming, error format, pagination and authentication should work the same everywhere. Document it with OpenAPI (REST) or a schema (GraphQL, protobuf) so clients can be generated and checked. For scripting, the best API is one with a good description and stable behaviour, whatever the style; see paginating an API in a shell script and rate limits and retries.
Reading an unfamiliar API
The first minutes with a new API go faster when you identify the style, because each has habits. A REST API tends to show URLs with nouns and has a documented list of resources. Look for an OpenAPI document, often at a path like /openapi.json or in the documentation, which describes every endpoint and can generate a client. An RPC API shows a single endpoint or a service definition with verb-like method names. A GraphQL API has one endpoint, usually /graphql, and supports introspection, which lets you ask the server for its own schema. Once you know the style, you know where to look for the contract and what tooling exists.
Use the contract. Generated clients, schema validators and mock servers all start from the machine-readable description, and a script that validates responses against it catches the day the provider changes a field. When the API has no description, write down the shapes you rely on in the script's comments and check them in a test.
Pagination, errors and filtering across styles
Each style handles the recurring problems differently, and a script must follow its host's conventions. REST lists use query parameters, page numbers or cursors, and a header or body field points to the next page. GraphQL uses connections with cursors and a flag telling you whether more data follows. RPC services define their own request and response fields, often with a page token. Errors differ too: REST relies on status codes plus a body, GraphQL commonly returns 200 with an errors array alongside partial data, and RPC protocols have their own status vocabulary. A script that checks only the HTTP status will miss GraphQL failures completely.
Filtering and field selection show the biggest contrast. In REST the server decides what a resource contains, and parameters like fields or expand are optional extras. In GraphQL the client chooses the fields in the query, which reduces transferred data and makes the request more expressive. In RPC the method signature decides. When writing a script, ask for only the fields you use; it is faster, cheaper and less exposed to fields that later change.
Versioning and evolution
APIs change, and the style shapes how. REST APIs often carry a version in the path or a header and keep old versions alive for a period. GraphQL prefers adding fields and deprecating old ones, so queries keep working as long as the fields they use remain. RPC with protocol buffers supports compatible evolution through field numbers, and breaking changes need a new service version. For a script, pin the version you target, subscribe to the provider's changelog and treat deprecation warnings in headers or schema as work items. The warning arrives long before the removal, and reading it costs nothing.
Practical advice for scripts
Whatever the style, a script benefits from the same discipline: set timeouts, check both the status and the body, handle pagination to the end, respect rate limits and log request identifiers. Keep the API-specific part in a small function and the business logic outside it, so that a change of provider or style touches one place. Prefer the simplest transport that the provider supports, since a plain HTTP call with a JSON body is easy to debug, and reserve generated clients and heavier tooling for programs that live long enough to repay the setup.
Where this does not apply
Real systems blur the lines: a "REST" API with a POST /orders/42/cancel action is RPC-flavoured, and a GraphQL server may wrap REST backends. Event-driven interfaces (webhooks, message queues) are another model. Do not rewrite a working API to match a label. See Roy Fielding's REST dissertation chapter and the GraphQL learning guide.


