Encore

Stay in touch

Product updates and engineering deep-dives.

DiscordGitHubYouTube

© 2026 Encore

Product
Encore PlatformEncore Platform
Encore.tsEncore.ts
Encore.goEncore.go
InstallInstall
PricingPricing
Customers
Case StudiesCase Studies
ShowcaseShowcase
Book a DemoBook a Demo
Resources
DocsDocs
Example AppsExample Apps
Demo videoDemo video
ArticlesArticles
GitHub ReleasesGitHub Releases
Systems Operational
Company
AboutAbout
Swag ShopSwag Shop
ContactContact
JobsJobs
PressPress
SecuritySecurity
Legal
TermsTerms
Privacy PolicyPrivacy Policy
Data Processing AgreementData Processing Agreement
Enterprise SLAEnterprise SLA
← All articles

GraphQL vs REST APIs

Choosing the right API architecture for your backend

03/20/26
5 Min Read
Ivan Cernja
← All articles
03/20/26

GraphQL vs REST APIs

Choosing the right API architecture for your backend

Ivan Cernja
5 Min Read

REST gives you predictable endpoints, HTTP caching, and universal tooling. GraphQL gives you flexible queries, a typed schema, and fewer round trips. The tradeoff is where the complexity lives: REST puts it on the client (multiple requests, over-fetching), GraphQL puts it on the server (resolvers, data loaders, depth limiting).

For most backend services, REST is the simpler and better-supported path. GraphQL earns its place when multiple clients need different views of deeply nested data.

REST

REST APIs are built around resources. Each resource has a URL. You interact with it using HTTP methods:

  • GET /users/42 - retrieve a user
  • POST /users - create a new user
  • PUT /users/42 - replace a user
  • DELETE /users/42 - remove a user

Responses are JSON. Status codes tell you what happened. A typical endpoint:

GET /api/orders/123 { "id": 123, "status": "shipped", "customer_id": 42, "items": [ { "product_id": 7, "name": "Keyboard", "quantity": 1, "price": 89.99 }, { "product_id": 12, "name": "Mouse", "quantity": 1, "price": 49.99 } ], "total": 139.98 }

Why it works: HTTP methods are well-understood. Caching works at every layer - browser, CDN, reverse proxy. Every monitoring tool, HTTP client, and debugging proxy supports REST natively. Each request is self-contained, so horizontal scaling is straightforward.

GraphQL

GraphQL exposes a single endpoint where clients specify exactly what data they need. A typed schema defines all available data and operations:

type User { id: ID! name: String! email: String! orders: [Order!]! } type Query { user(id: ID!): User }

Clients query for specific fields:

query { user(id: "42") { name orders { id status total } } }

Only the requested fields come back. One request can traverse relationships that would take three REST calls.

Why it works: No over-fetching or under-fetching. The schema doubles as documentation. Adding fields is non-breaking since clients only request what they need. Especially valuable when mobile apps, web apps, and third-party consumers all need different views of the same data.

How they compare

DimensionRESTGraphQL
Data fetchingFixed response per endpointClient specifies exact fields
Over/under-fetchingCommon (mitigate with sparse fieldsets)Solved by design
CachingHTTP caching works nativelyRequires application-level caching
Type safetyOpt-in (OpenAPI + codegen)Built into the schema
ToolingUniversalSpecialized (GraphiQL, Apollo Studio)
Server complexityLow (route → handler)High (schema, resolvers, data loaders, depth limits)
PerformancePredictable per endpointVariable (clients can construct expensive queries)
AuthPer-endpointPer-field (more granular, more work)

Caching is REST's biggest structural advantage. Every URL is a cache key. CDNs, browsers, and reverse proxies all work without configuration. GraphQL sends POST requests to a single URL, which breaks URL-based caching entirely. You need Apollo Client's normalized cache, persisted queries, or a CDN layer like Stellate.

Flexibility is GraphQL's biggest structural advantage. A mobile app that only needs a user's name and avatar makes one request and gets exactly that. The same API serves a web dashboard that needs the user's full profile, orders, and payment history - also one request. REST would need separate endpoints or sparse fieldsets for each.

Complexity is the tiebreaker. REST is straightforward to build, monitor, and debug. Each endpoint does one thing, and performance is predictable. GraphQL requires resolvers, data loaders (to avoid N+1 queries), query depth limiting, and field-level authorization. That complexity is worth it when client flexibility is genuinely needed. It's overhead when it's not.

When to use REST

  • CRUD on well-defined resources
  • Caching matters (content delivery, read-heavy workloads)
  • Service-to-service APIs where both sides are under your control
  • Small teams that want to minimize backend complexity
  • Broad compatibility with third-party tools and integrations

When to use GraphQL

  • Multiple clients (web, mobile, third-party) need different views of the same data
  • Deep, interconnected data relationships
  • Public APIs where consumer flexibility is a priority
  • Mobile apps where reducing round trips on poor connections matters
  • Frontend teams that want to iterate on data requirements without backend changes

Building REST APIs with Encore.ts

REST is the right default for most backends. Encore.ts is built around that assumption.

Endpoints are TypeScript functions with typed request and response objects. Encore handles routing, validation, serialization, and documentation:

import { api } from "encore.dev/api"; interface Order { id: number; status: string; items: OrderItem[]; total: number; } export const getOrder = api( { method: "GET", path: "/orders/:id", expose: true }, async ({ id }: { id: number }): Promise<Order> => { return await orderService.findById(id); } );

You get type-safe request validation, automatic API documentation that stays in sync with your code, type-safe service-to-service calls, and built-in distributed tracing - without configuring any of it. The api() function pattern is also straightforward for AI coding agents to follow. Agents generate endpoints matching your existing structure, and TypeScript types ensure the generated code is valid. No separate schema definitions to maintain.

For a complete walkthrough, see How to Build a REST API with TypeScript in 2026. Encore.ts also includes primitives for databases, Pub/Sub, cron jobs, caching, and secrets. When you deploy with Encore Cloud, everything provisions automatically in your own AWS or GCP account.

Related reading

  • API Types: REST, GraphQL, gRPC, and More
  • gRPC vs JSON for Microservices
  • Best TypeScript Backend Frameworks in 2026
  • Event-Driven Architecture
Contents
REST
GraphQL
How they compare
When to use REST
When to use GraphQL
Building REST APIs with Encore.ts
Related reading
Encore

Automated infrastructure for humans and agents

Let agents build and validate features with real infrastructure in the dev loop. Encore automatically provisions infrastructure, from local dev to production in your cloud on AWS/GCP.

Get started

Automated infrastructure for humans and agents

Let agents build and validate features with real infrastructure in the dev loop. Encore automatically provisions infrastructure, from local dev to production in your cloud on AWS/GCP.

Get Started