SevyDevy

Guide · Backend

REST vs GraphQL vs gRPC: Pick a Style Per Use Case

26 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • REST vs GraphQL vs gRPC has no single winner. Each is an API style, a way for one program to ask another for data, and each fits a different caller.
  • REST gives every resource, such as one listing, its own web address (URL). It uses HTTP, the protocol browsers and servers speak, with methods like GET to read. Every tool speaks it, and GET replies can be cached.
  • GraphQL gives one address where an app sends a query naming exactly the fields a screen needs. One trip replaces several, but caching, costly queries and error handling become your job.
  • gRPC lets one service call a function on another. Both sides share a contract file listing the functions and fields, and send compact binary messages (bytes, not readable text) over HTTP/2, the newer HTTP. It suits calls between your own services.
  • Beginners can stop after the section on choosing a style. Engineers get what we ran: GraphQL error codes, the extra lookups one list query triggers, the bytes a gRPC call sends, and time limits that can leave server work running.

What goes wrong when one API style serves every caller?

One style used for every caller leaves some of them slow and others stuck. That is why REST vs GraphQL vs gRPC is really a question about callers, not about which style wins.

A phone on weak signal, a partner's script and a busy internal service each need something different from an API.

Picture a common setup. A rental-listings app lets tenants search for flats on a web site and in Android and iPhone apps. Owners post listings, and about a dozen internal services handle search, photos and rent estimates. The setup is made up, but the problem is real.

Everything talks through one REST API. To draw one listing card, the app makes four calls: the listing, the owner, the photos and the nearby places. The four replies add up to about 210 KB of JSON, a plain text format for data, yet the card shows only 15 values.

On a weak 4G signal the card takes 3.4 seconds to appear (made up). At the team's review, one developer wants to move everything to GraphQL. Another says gRPC is faster, so every API should be gRPC.

Both answers skip the real question: who calls this API, over what network, and what do they need? This guide answers it one caller at a time, on the same rental app throughout.

REST vs GraphQL vs gRPC: what is the difference?

REST vs GraphQL vs gRPC comes down to what the caller asks for. REST asks for a resource at a URL, GraphQL asks for exact fields from a schema, and gRPC calls a named function on another service.

A few words first. An API (application programming interface) is the agreed way one program asks another for something. An endpoint is one address an API listens on.

A contract, or schema, is a written description of what requests and replies look like. All three styles usually travel over HTTP, the protocol browsers and servers use to talk.

One everyday picture helps: the xerox shop on your street serves people in three ways.

  • The printed price list. The wall lists A4 copies, colour prints and spiral binding, and anyone can order by name. That is REST: fixed, public items anyone can ask for.
  • The custom job. You hand over a thick book with a slip saying copy only pages 3, 7 and 12 to 15. You get exactly those pages. That is GraphQL.
  • The standing coded order. The office next door sends a chit reading Job 7, 40 copies. Both sides hold the same code sheet, so nothing needs explaining. That is gRPC.

Where the picture breaks: a shop serves one slip at a time, while gRPC carries many calls at once on one connection. And a GraphQL server can prepare a custom job in advance when the app registers its queries.

What is a REST API and how does it work?

A REST API treats your data as resources, each with its own URL, and uses standard HTTP methods to read or change them. Roy Fielding described REST as an architectural style in his 2000 PhD dissertation.

A resource is any thing your API talks about, such as one listing or one owner. In the rental app, listing 2041 lives at the path /listings/2041, and its owner lives at /owners/77.

The HTTP method says what you want to do. GET reads, POST creates, PUT replaces, PATCH changes part of a resource, and DELETE removes it. The reply carries a status code, such as 200 for success or 404 for not found.

RFC 9110, the HTTP standard, gives some methods useful promises. GET, HEAD, OPTIONS and TRACE are safe: the client asks only to read. PUT, DELETE and the safe methods are idempotent: sending the request twice has the same intended effect as once.

These promises are why REST spreads so easily. RFC 9110 says a reply to GET is cacheable unless its Cache-Control header says otherwise. So browsers and a CDN (content delivery network, servers near users that keep copies) can reuse it.

Fielding's design also makes REST stateless: each request carries everything the server needs to understand it. He lists scalability as a benefit, because the server need not store anything between requests.

Now look at the listing card built with REST. Count the arrows leaving the app. Each one is a separate trip across a slow mobile network.

One listing card, four REST calls

Notice four arrows leave the phone. Each reply is a whole resource, so the app downloads about 210 KB to show 15 values (made up).

Diagram connections:

  • Listing card screen to GET /listings/2041 (Call 1)
  • Listing card screen to GET /owners/77 (Call 2)
  • Listing card screen to GET /listings/2041/photos (Call 3)
  • Listing card screen to GET /places?near=2041 (Call 4)
  • GET /listings/2041 to Listings database
  • GET /owners/77 to Listings database
  • GET /listings/2041/photos to Listings database
  • GET /places?near=2041 to Listings database
  • Listing card screen: Needs 15 values from 4 places
  • GET /listings/2041: Title, rent, address and 60 more fields
  • GET /owners/77: Name, badge and the full profile
  • GET /listings/2041/photos: All 24 photos in every size
  • GET /places?near=2041: Metro, hospitals, shops nearby
  • Listings database: Read by every REST handler

These problems have names. Over-fetching means getting more data than you need, like a listing with over 60 fields when the card shows three of them. Under-fetching means one call is not enough, so you make more trips.

Neither is a flaw in REST itself. You could add one endpoint shaped exactly for the card. Many teams do this and call it a backend for frontend, or BFF: a small API built for one app's screens.

What is GraphQL and what problem does it solve?

GraphQL is a query language for APIs: the client sends one request naming exactly the fields it wants, and gets back that shape. It solves over-fetching and under-fetching for screens that combine many things.

The GraphQL specification says GraphQL was created in 2012, and work on it as an open standard began in 2015. Its September 2025 edition defines three kinds of operation: a query reads, a mutation writes, and a subscription streams updates.

A GraphQL server publishes a schema, a typed list of every object and field it offers. The rental app's Listing type has fields like title, rentPerMonth, photos and owner. The server checks each request against the schema before running it.

On the server, each field is filled by a small function called a resolver. The owner resolver might call the owner service, while the photos resolver reads photo storage. Most servers expose all of this at one endpoint, usually /graphql.

Here is the whole listing card as one GraphQL request, sent from a small Python script with the requests library. GRAPHQL_URL comes from the environment, and the field names are the rental app's own.

python
import os
import requests

GRAPHQL_URL = os.environ["GRAPHQL_URL"]
CARD_QUERY = """
query ListingCard($id: ID!) {
  listing(id: $id) {
    title  rentPerMonth  locality
    photos(first: 5) { thumbUrl }
    owner { name  isVerified }
    nearbyPlaces(first: 5) { name }
  }
}"""
resp = requests.post(GRAPHQL_URL, timeout=5,
                     json={"query": CARD_QUERY, "variables": {"id": "2041"}})
body = resp.json()
print("HTTP status:", resp.status_code)
if body.get("errors"):
    print("Some fields failed:", [e["message"] for e in body["errors"]])
print(body.get("data"))

Three parts matter. The query asks for exactly the card's 15 values: three listing fields, five photo thumbnails, two owner fields and five nearby places. The variables keep the listing id out of the query text, so one query serves every listing.

We ran the script against a small stand-in server built on graphql-js 17.0.2 and graphql-http 1.23.1. With the owner service failing, it printed HTTP status 200, the error owner service timed out, and the card data with owner set to None.

What just happened? GraphQL returned the fields that worked plus an errors list for the one that failed. The specification calls this a partial response. That is why the script checks body errors and never trusts the status code alone.

Now the same card with GraphQL. Notice the phone makes one trip. The calls to many services happen inside the company's own network, where each hop is short.

One listing card, one GraphQL query

Notice one arrow leaves the phone; the other calls happen inside the data centre. The reply is about 9 KB and the card appears in 1.1 seconds (made up).

Diagram connections:

  • Listing card screen to GraphQL server (One request)
  • GraphQL server to Listing service (listing resolver)
  • GraphQL server to Owner service (owner resolver)
  • GraphQL server to Photo storage (photos resolver)
  • GraphQL server to Places service (places resolver)
  • Listing card screen: Sends one query for 15 values
  • GraphQL server: Checks the query against the schema
  • Listing service: Title, rent and locality
  • Owner service: Name and verified badge
  • Photo storage: Five small thumbnails
  • Places service: Nearby metro and hospitals

On a test build of the app, the card needs one call and about 9 KB, and it appears in 1.1 seconds on the same weak signal (made up). You can measure reply sizes yourself with curl, which prints the bytes it downloaded.

bash
# Reply size of a small GraphQL query, in bytes
curl -s -o /dev/null -w '%{size_download} bytes\n' \
  -H 'content-type: application/json' \
  --data '{"query":"{ listing(id: \"2041\") { title rentPerMonth locality } }"}' \
  "$GRAPHQL_URL"

Against our stand-in server this printed 94 bytes. Run the same flag against each REST call of a screen, add the numbers, and compare them with the one GraphQL reply.

What does GraphQL cost you?

GraphQL moves work from the phone to the server, and that work brings its own costs.

  • Caching gets harder. Most requests are POSTs to one endpoint, and RFC 9110 lets caches store POST replies only in narrow cases. Apps usually cache results inside the app instead.
  • The N+1 problem. A list of 20 listings can trigger 20 separate owner lookups, one per listing. A batching helper, often called a data loader, groups them into one.
  • Costly queries. A client can ask for listings, their owners, each owner's listings, and so on. The graphql.org security page advises limits on query depth and on estimated cost.
  • Errors inside success. A reply can be HTTP 200 and still carry an errors list, so your monitoring must read the body, not just the status.
  • Access checks per field. One query touches many objects, so the graphql.org authorization page puts access rules in the business logic that each resolver calls.

That last idea is called trusted documents, or persisted queries. The app sends a short id instead of the full query text, and the server runs only ids it knows. The graphql.org security page notes this cannot work for public APIs, whose queries are unknown in advance.

What is gRPC and why is it fast?

gRPC is a framework for remote procedure calls: your code calls a function, and the function runs on another service. It is light because, by default, messages are compact binary and many calls share one connection.

Whether it is faster for your service depends on message sizes and the network, so measure before you switch. We did not benchmark speed for this guide; we measured sizes.

You start with a contract written in Protocol Buffers, or protobuf. The gRPC docs call it an Interface Definition Language, because it describes both the service and its messages. A tool then generates client and server code in many languages.

In the rental app, the search service needs a rent estimate for every listing it shows. The rent estimate service offers one method, Estimate. It takes a listing id and returns rupees per month.

protobuf
syntax = "proto3";

package rent;

service RentEstimate {
  rpc Estimate (EstimateRequest) returns (EstimateReply);
}

message EstimateRequest {
  string listing_id = 1;
}

message EstimateReply {
  int32 rupees_per_month = 1;
}

Each field has a number, such as listing_id = 1. On the wire, protobuf sends that number instead of the field name. We serialised the reply with rupees_per_month set to 28500: protobuf took 4 bytes, and the same reply as compact JSON took 26.

That gap comes mostly from leaving out field names, so it shrinks for long text values. With a 200-character listing id, the request took 203 bytes as protobuf and 217 as JSON.

gRPC runs over HTTP/2, a newer version of HTTP that carries many requests at once on one connection.

gRPC has four call types. Unary is one request and one reply, like a normal function call. Server streaming sends one request and reads many replies. Client streaming and bidirectional streaming let the client, or both sides, send streams.

Deadlines are built in. The caller says how long it will wait, and if time runs out the call ends with the status DEADLINE_EXCEEDED. The gRPC deadlines guide warns that gRPC sets no deadline by default.

Here is the search service calling the rent estimate service from Python. The files rent_pb2 and rent_pb2_grpc are generated from the contract with grpcio-tools, so the method and field names come from there.

python
import time
import grpc
import rent_pb2
import rent_pb2_grpc  # both generated from rent.proto

channel = grpc.insecure_channel("localhost:50051")
stub = rent_pb2_grpc.RentEstimateStub(channel)

def estimate_rent(listing_id):
    request = rent_pb2.EstimateRequest(listing_id=listing_id)
    try:
        reply = stub.Estimate(request, timeout=0.2)  # 200 ms deadline
        return reply.rupees_per_month
    except grpc.RpcError as err:
        if err.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
            return None  # show the listing without an estimate
        raise

start = time.monotonic()
result = estimate_rent("2041")
print(result, f"after {time.monotonic() - start:.2f} s")

The line that matters is the call with timeout=0.2, a deadline of 200 milliseconds. If the estimate is late, search shows the listing without it, instead of making the tenant wait. Other failures, such as UNAVAILABLE when the service is down, still raise.

To check it, we ran this on grpcio 1.84.0 against a stand-in server. With a fast server it printed 28500 after about 0.01 s. When the server slept for 1.5 seconds, it printed None after 0.20 s.

In production you would use an encrypted channel, not the insecure one in this short example. The standing coded order at the xerox shop works the same way: Job 7 needs no explaining, but a stranger saying Job 7 gets a blank look.

That blank look is gRPC's weak spot. The gRPC-Web protocol document says browsers do not expose the HTTP/2 framing that plain gRPC needs. So web apps use gRPC-Web through a proxy, a server in the middle that translates.

The gRPC-Web README names Envoy as the default proxy.

As of 19 October 2026, the gRPC-Web README says it supports unary calls, and server streaming only in its text mode. Client streaming and bidirectional streaming are not currently supported.

How do REST, GraphQL and gRPC compare side by side?

REST is the simplest and caches best, GraphQL fits flexible app screens, and gRPC has the strictest contracts between services. The table puts the rental app's findings in one place.

REST, GraphQL and gRPC in the rental app; listing card sizes and timings are made up
RESTGraphQLgRPC
What the client asks forA resource at a URLExact fields from a schemaA named function on a service
Usual data formatJSON textJSON textProtobuf binary
EndpointsMany, one per resourceUsually oneOne path per method
ContractOptional, such as an OpenAPI fileRequired schemaRequired protobuf file
HTTP cachingBuilt in for GETHard, unless queries go by GETNot used
StreamingNot built inSubscriptionsFour call types, built in
Browser supportEverywhereEverywhereThrough gRPC-Web and a proxy
Listing card on weak 4G4 calls, 210 KB, 3.4 s1 call, 9 KB, 1.1 sNot used in the app
Best fit in the rental appPartner APIWeb site and mobile appsCalls between services

Which API style is best for mobile apps?

For your own mobile app, GraphQL or a REST backend for frontend usually works best, because both cut round trips on slow networks. Plain resource-by-resource REST is often the slowest choice for busy screens.

A round trip is one request travelling to the server and the reply coming back. On weak signal, the wait for each trip can be longer than the server's actual work. Fewer, smaller replies matter more than raw server speed.

Old app versions matter too. Many users never update, so the API must keep serving builds from last year. With GraphQL, you can add new fields freely, and old apps simply never ask for them.

Removing a field breaks old callers in any style. Renaming one breaks REST and GraphQL callers too. In protobuf, a rename keeps the wire bytes the same, since only the field number travels, but code and JSON that use the old name break. gRPC can run inside mobile apps as well: its docs list Android, and it has Swift and Objective-C libraries. The rental app skips it because one GraphQL schema serves both web and mobile.

Can you use REST, GraphQL and gRPC together in one system?

Yes, and many large systems do: each style sits at the edge where it fits best. The rental app ends up using all three.

Here is the final design. Look at where each style appears: REST at the outer edge for partners, GraphQL for the company's own apps, and gRPC only on the inside.

The rental app's three API styles

Notice the apps and the partners never see gRPC; it stays between internal services.

Diagram connections:

  • Web site and mobile apps to GraphQL gateway (GraphQL over HTTPS)
  • Partner property agencies to Partner REST API (REST over HTTPS)
  • GraphQL gateway to Search service (gRPC)
  • GraphQL gateway to Listing service (gRPC)
  • Partner REST API to Listing service (gRPC)
  • Search service to Rent estimate service (gRPC, 200 ms deadline)
  • Listing service to Listings database (SQL)
  • Web site and mobile apps: Tenants and owners
  • Partner property agencies: Upload listings in bulk
  • GraphQL gateway: One schema for every screen
  • Partner REST API: Documented, versioned, cached
  • Search service: Finds listings for a query
  • Rent estimate service: Answers within 200 ms
  • Listing service: Owns listings and owners
  • Listings database: Source of truth for listings

Partners, such as agencies that upload hundreds of listings, get REST. They use many different tools, and every tool speaks HTTP and JSON. The team publishes an OpenAPI file, a standard description of every endpoint, so partners can generate their own clients.

The GraphQL gateway is the only thing the apps talk to. It owns no data. Its resolvers call internal services, so most changes inside the backend never force an app update.

Inside, services talk gRPC. Both sides use code generated from one contract, so a misspelt field fails loudly instead of arriving empty. Deadlines stop one slow service from freezing a whole page.

How do you choose between REST, GraphQL and gRPC?

Choose by asking who calls the API, what network they use, and how often the shape of the data changes. Fashion and benchmark charts come last.

Here is that turned into a short decision chart. Start at the top and follow the answer that fits your caller.

Which API style for a new API?

Follow one caller down the chart. It is a starting point, not a law: team skills and existing tools can tip the answer.

Diagram connections:

  • A new API to design to Who calls this API?
  • Who calls this API? to Use REST (Outside developers)
  • Who calls this API? to Screen mixes many sources? (Our own web or mobile apps)
  • Who calls this API? to Many calls, tight deadlines? (Our own services)
  • Screen mixes many sources? to Use GraphQL (Yes)
  • Screen mixes many sources? to Use REST (No)
  • Many calls, tight deadlines? to Use gRPC (Yes)
  • Many calls, tight deadlines? to Use REST (No)
  • A new API to design: Start from the caller, not the tool
  • Who calls this API?: Outsiders, our apps or our services
  • Screen mixes many sources?: Many objects on one screen
  • Many calls, tight deadlines?: Or streams between services
  • Use REST: Simple, cacheable, easy for anyone
  • Use GraphQL: One flexible schema for screens
  • Use gRPC: Typed contract, deadlines, streams

Behind the chart sit five checks worth writing down for every new API.

  1. Name the caller

    Write down who calls it: a browser, a mobile app, a partner or another service. This one fact rules out most options.

  2. Check the network

    Slow mobile networks reward fewer round trips. A fast network inside the data centre rewards small messages and strict deadlines.

  3. Check how data is read

    If many callers read the same thing, like the most viewed listings in a locality, REST with HTTP caching is hard to beat.

  4. Check how often the shape changes

    Screens that change every few weeks suit GraphQL. Stable contracts between services suit gRPC.

  5. Count your team's skills

    A style nobody on the team has run in production is a risk. Add it only where the gain is clear and measured.

One rule sits above all of these. Do not replace a working API because of fashion. Moving a stable REST API to gRPC can take months, and users may notice nothing.

What HTTP status does a GraphQL error return?

It depends on the kind of error, the reply's media type and your server, so always read the body. The media type is the Content-Type label naming the body's format.

A partial result, with some data and some errors, gets a 2xx success code, not an error code.

The GraphQL over HTTP specification is still a working draft as of 19 October 2026. It says a reply whose data is present and not null must use a 2xx success code.

For replies with both data and errors, the current draft recommends a custom code, 294 Partial Success, with the newer application/graphql-response+json media type. Clients that do not know 294 must treat it as 200, as RFC 9110 requires.

The reference server we ran, graphql-http 1.23.1, returned 200 for a partial result with every Accept header we tried; that header lists the formats a client can read. A query naming an unknown field returned 400 with the newer media type and 200 with plain application/json.

The current draft recommends 422 for that validation failure, so servers and the draft do not fully agree as of 19 October 2026. Pin your server version and test the codes your monitoring depends on.

So a dashboard that counts only 4xx and 5xx replies misses every partial failure. Log the errors list, with the path of each failed field, as its own metric.

How big is the N+1 problem in practice?

We asked the stand-in server for 20 listings with their owners. Without batching, the owner service was called 20 times. With DataLoader 2.2.3 collecting the ids, it was called once.

The graphql.org performance page describes the same fix: collect requests over a short period, then send them in a single request. Check yours by counting calls to the downstream service for one list query.

When can GraphQL use GET and the HTTP cache?

The draft says a server must accept POST and may accept GET. GET must never run a mutation, and 405 Method Not Allowed is the recommended reply; graphql-http 1.23.1 returned exactly that.

GET replies can be cached by browsers and CDNs, but long queries do not fit in a URL. Trusted documents fix that, since the URL carries a short id. With cache headers in the server's reply, that pairing is how GraphQL gets back the caching REST has by default.

What does a gRPC call look like on the wire?

Every gRPC call is an HTTP/2 POST to a path made of the package, service and method names, with a binary body. We pointed the Python client at a plain HTTP/2 listener and printed what arrived.

Request headers and body sent by grpcio 1.84.0 for one Estimate call with a 200 ms deadline
PartValue we sawWhat it means
:methodPOSTEvery gRPC call is a POST
:path/rent.RentEstimate/EstimatePackage and service, then the method
content-typeapplication/grpcTells proxies this is gRPC
tetrailersClient accepts trailers, where the status comes
grpc-timeout195m (190m to 199m across runs)The deadline left, in milliseconds
Body11 bytesA 5-byte prefix, then the 6-byte message

The gRPC HTTP/2 protocol document defines each part. The 5-byte prefix is one byte saying whether the message is compressed, then four bytes giving its length.

The timeout header carried 195 milliseconds in the capture shown, slightly less than the 200 we set. Across seven captures it ranged from 190 to 199. The server saw between 0.188 and 0.199 seconds left across our runs, so expect a little less than you asked for.

Does a gRPC deadline stop the server's work?

Not with Python's default thread-pool server, grpc.server, which runs each call on a worker thread. The client stops waiting and the server marks the call as over, but your handler keeps running until it finishes, or until your code checks and returns early.

We made the stand-in handler sleep for 1.5 seconds on grpcio 1.84.0. The client gave up at 0.20 s, yet the handler finished its sleep every time, and context.is_active() then returned False.

The asyncio server, grpc.aio, built on Python's async library, behaves differently. With the same client, our grpc.aio handler was cancelled at its next await, the point where it pauses to wait, about 0.2 s in. A blocking call such as time.sleep() still ran its full 1.5 s first.

The docs disagree with each other here. The core concepts page says a cancellation means no further work is done. The cancellation guide says an expired deadline triggers cancellation, yet the library generally cannot interrupt a running server handler.

So long handlers should check context.is_active() or context.time_remaining() between steps, and return early when time is up. They should also pass the remaining time to any calls they make, or downstream services keep working for nobody.

Why can one gRPC client overload one server?

Because one client channel keeps one long-lived connection and sends every call down it. In our test, 100 calls from one channel, 10 at a time, used one TCP connection.

A load balancer, the part that shares traffic across servers, can work per connection (layer 4) or per request (layer 7). A layer 4 balancer picks a server once per connection, so all 100 calls would land on one server.

The gRPC load balancing post says a layer 7 balancer can spread one client's HTTP/2 streams across many backends. The other choice the post describes is balancing inside the client itself.

How do you explain REST vs GraphQL vs gRPC in a system design interview?

Explain it as a choice per caller: show where each style fits in one design, and say what each one costs.

  1. Ask who the clients are

    Web, mobile, partners and internal services. Write them on the whiteboard before you name any style.

  2. Pick the edge style

    For a public or partner API, choose REST for reach and caching. For your own apps with busy screens, choose GraphQL or a backend for frontend.

  3. Pick the inside style

    Between services, choose gRPC for typed contracts, deadlines and streaming. Say that plain REST is fine for a small team.

  4. Name the costs

    GraphQL brings caching, N+1 and query limits. gRPC brings browser and load balancing limits. REST brings over-fetching and extra round trips.

  5. Cover operations

    Set deadlines on every internal call and say how the server stops work. Say how an error shows up in each style, including GraphQL errors inside a 200.

Questions people ask

Is GraphQL faster than REST?
Not by itself. GraphQL is often faster for busy screens, because one request replaces several and returns only the fields you need. For one simple resource, REST can be faster, because a cache near the user can answer without reaching your server at all.
Is gRPC better than REST?
For calls between your own services, gRPC is often the better fit, because typed contracts, compact binary messages, deadlines and streaming are built in. For public APIs and browsers, REST is usually better. Every tool speaks HTTP and JSON, while browsers need gRPC-Web and a proxy.
Can a browser call a gRPC service directly?
Not with plain gRPC. Web apps use gRPC-Web, which needs a proxy such as Envoy in front of the gRPC server to translate the calls. As of 19 October 2026, its README says it supports unary calls, and server streaming only in text mode. Client and bidirectional streaming are not supported.
Does GraphQL replace REST?
No. GraphQL usually serves a company's own apps, while REST stays common for public APIs, partner integrations and simple file downloads. Many companies run both. Often the GraphQL server and the REST API call the same internal services underneath.
Does GraphQL always use POST requests?
Mostly, but not always. The GraphQL over HTTP draft says servers must accept POST and may accept GET. Mutations must never run over GET, and 405 is the recommended reply. GET queries can be cached by browsers and CDNs, and trusted documents keep those GET URLs short.
Is REST the same as JSON over HTTP?
Not exactly. REST is an architectural style with rules such as statelessness, cacheable responses and resources identified by URLs. Many APIs called REST are really just JSON over HTTP, and that is fine in practice. What matters most is clear resources, correct methods and honest status codes.
What is the N+1 problem in GraphQL?
It is when one query for a list causes one extra lookup for every item in it. Asking for 20 listings with their owners meant 20 owner calls in our test. A batching helper such as DataLoader collected the 20 owner ids and fetched them in one call.

Sources and further reading

Practise this with Lala

Practise