Foto av Logan Voss via Unsplash
GraphQL vs REST: När ska du välja vad?
En pragmatisk jämförelse av GraphQL och REST baserad på verkliga projekt. Prestanda, komplexitet, caching och organisatoriska faktorer som avgör valet.
Det handlar inte om bäst — utan om rätt
Debatten GraphQL vs REST framställs ofta som ett antingen-eller. I verkligheten handlar det om att välja rätt verktyg för uppgiften. REST har tjänat oss väl i 20 år och fortsätter vara det bästa valet i många scenarier. GraphQL löser specifika problem som REST hanterar dåligt.
Jag har arbetat med båda i produktionssystem och sett organisationer göra kostsamma misstag åt båda hållen: team som tvingade in GraphQL i ett simpelt CRUD-API, och team som kämpade med REST trots komplexa, sammanflätade datamodeller.
Här är en ärlig jämförelse med konkreta riktlinjer.
När REST vinner
REST är det rätta valet oftare än GraphQL-entusiaster vill erkänna. Om ditt API har tydliga, väldefinierade resurser med förutsägbara åtkomstmönster, ger REST enklare implementation, bättre caching och lägre inträdeströskel.
HTTP-caching fungerar out-of-the-box med REST. GET-requests cachas av CDN:er, webbläsare och proxies utan konfiguration. Med GraphQL, där alla queries går via POST till en endpoint, behöver du implementera applikationsnivå-caching manuellt.
REST är också enklare att övervaka. Varje endpoint har en tydlig metod och URL, vilket gör monitoring och rate limiting per operation naturligt.
// REST: Naturlig HTTP-caching
GET /api/products/abc-123
Cache-Control: public, max-age=3600
ETag: "v1-abc123-1709654321"
// Conditional request — sparar bandbredd
GET /api/products/abc-123
If-None-Match: "v1-abc123-1709654321"
// Svar: 304 Not Modified (ingen body)
// Enkel monitoring per endpoint
GET /api/products → avg 45ms, 0.1% errors
POST /api/orders → avg 120ms, 0.3% errors
GET /api/users/:id → avg 30ms, 0.05% errorsNär GraphQL vinner
GraphQL exceller i tre scenarier. Först: när klienten behöver flexibilitet. Mobilappar, dashboards och komplexa UI:er som kräver olika datakombinationer beroende på vy och enhet. Med REST resulterar detta i antingen over-fetching eller en explosion av specialändamåls-endpoints.
Andra: när du har en komplex datamodell med djupa relationer. En e-handelsplattform där produkter har varianter, varianter har lager per lagerplats, lagerplatser har leveranstider — GraphQL löser detta i en query istället för 4+ REST-requests.
Tredje: som API-gateway framför flera mikrotjänster. GraphQL Federation samlar flera domän-API:er till ett enhetligt schema.
// GraphQL: En query istället för 4 REST-anrop
query DashboardData($userId: ID!) {
user(id: $userId) {
name
avatar
orders(last: 5) {
id
status
total
items {
product { name, thumbnail }
quantity
}
tracking {
carrier
estimatedDelivery
}
}
recommendations(limit: 4) {
product { name, price, rating }
reason
}
paymentMethods {
type
lastFour
}
}
}
// Med REST: 4+ separata requests
// GET /api/users/123
// GET /api/users/123/orders?last=5
// GET /api/users/123/recommendations
// GET /api/users/123/payment-methods
// + N requests för order itemsPrestanda och komplexitet
GraphQL har dolda komplexitetskostnader. Query-djupsanalys, kostnadsberäkning och N+1-prevention (DataLoader) är inte valfria i produktion — de är obligatoriska. Utan dem kan en enda query sänka din databas.
Persisterade queries (hash istället för query-sträng) löser säkerhetsrisken med godtyckliga queries, men kräver build-steg och deployment-koordinering.
REST har enklare operationell profil: standard HTTP-tools fungerar, caching är gratis och du vet exakt vad varje endpoint gör. Men REST skalar sämre designmässigt — med 50+ endpoints blir det svårt att hålla konsistens.
// GraphQL: Obligatorisk kostnadsbegränsning
import {
createComplexityPlugin
} from "graphql-query-complexity";
const complexityPlugin = createComplexityPlugin({
maximumComplexity: 1000,
estimators: [
fieldExtensionsEstimator(),
simpleEstimator({ defaultComplexity: 1 }),
],
});
// DataLoader — förhindrar N+1
import DataLoader from "dataloader";
function createLoaders() {
return {
userLoader: new DataLoader(
async (ids: readonly string[]) => {
const users = await db.users.findMany({
where: { id: { in: [...ids] } },
});
return ids.map(
(id) => users.find((u) => u.id === id)
);
}
),
};
}
// Utan DataLoader:
// 100 orders → 100 separata user-queries
// Med DataLoader:
// 100 orders → 1 batched user-queryBeslutsmatris för ditt projekt
Välj REST om: ditt API har tydliga CRUD-operationer, du behöver maximal HTTP-caching, teamet har begränsad GraphQL-erfarenhet, eller API:et konsumeras primärt av tredjeparter.
Välj GraphQL om: klienterna behöver varierande datasubsets, datamodellen har komplexa relationer med 3+ nivåer, du bygger ett BFF (Backend For Frontend) för flera klienttyper, eller du behöver federation över mikrotjänster.
I praktiken ser jag ofta en hybrid: publika API:er exponeras via REST (enklare för externa konsumenter) medan interna API:er använder GraphQL (teamet kontrollerar både klient och server). Det är en pragmatisk approach som ger det bästa av båda världar.
Läs mer om grunderna i vår REST API-guide eller vår GraphQL-introduktion.