REST vs GraphQL vs gRPC: Choosing the Right API Style
The first architectural decision for any mobile backend is the API paradigm itself. In 2026, three styles dominate modern mobile development: REST, GraphQL, and gRPC. Each has distinct strengths, and the right answer depends on your app's query patterns, team size, and performance requirements. At Frenchy Digital, we've shipped APIs in all three styles — and choosing well at the start saves months of rework later.
According to the Postman State of the API 2026 report, REST still powers 83% of production APIs, GraphQL adoption has climbed to 38%, and gRPC has become the default for internal service-to-service traffic at 47% of surveyed engineering teams. Many mature architectures use all three: REST or GraphQL at the edge, gRPC between microservices.
| Dimension | REST | GraphQL | gRPC |
|---|---|---|---|
| Transport | HTTP/1.1 or HTTP/2 | HTTP over POST | HTTP/2 with binary Protobuf |
| Payload | JSON | JSON | Protocol Buffers (binary) |
| Query Flexibility | Fixed endpoints | Client-defined queries | Fixed RPC methods |
| Over/Under-fetching | Common problem | Solved by design | Avoided via method granularity |
| Caching | Excellent (HTTP cache) | Difficult (single endpoint) | Manual / limited |
| Tooling Maturity | Universal | Strong (Apollo, Relay) | Growing (grpc-web, Connect) |
| Browser Support | Native | Native | Requires proxy (grpc-web) |
| Best For | Most public mobile APIs | Data-dense apps, BFF pattern | Internal microservices, streaming |
- Pick REST when: you need maximum ecosystem support, HTTP caching, and simple CRUD semantics
- Pick GraphQL when: the mobile app has many screens consuming overlapping data and you want one round trip per screen
- Pick gRPC when: you're building internal service-to-service calls or need bidirectional streaming at low latency
- Hybrid is often best: REST or GraphQL at the mobile edge, gRPC between backend microservices
- Avoid mixing styles in the same product surface — pick one contract per client-facing domain
The real question is never "which API style is best" — it's "which API style best matches my team, my clients, and the queries my product actually needs." Optimize for your constraints, not the trend cycle.
— Frenchy Digital API Platform Team
For most LA startups and mid-market clients we work with, we recommend starting with REST, layering GraphQL on top as a backend-for-frontend (BFF) once you have 10+ screens, and introducing gRPC internally when service count exceeds five. This pragmatic progression is battle-tested across our mobile app development projects.
API Versioning Strategy: Shipping Without Breaking Mobile Clients
Unlike web apps that refresh instantly, mobile apps live in the wild for months — even years — after release. A user on an old iPhone running v1.2 of your app will still hit your API long after you've moved on to v3. A disciplined versioning strategy is the single most important practice for keeping mobile clients alive while you evolve the backend.
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL path | /v1/users, /v2/users | Explicit, cache-friendly, discoverable | URL changes force routing logic |
| Header-based | Accept: application/vnd.api.v2+json | Clean URLs, RESTful purity | Harder to debug, breaks CDN caching |
| Query param | /users?v=2 | Easy to try in browser | Messy, discouraged for production |
| Subdomain | api-v2.example.com | DNS-level routing, isolation | Certificate and CORS complexity |
For mobile APIs, URL-based versioning wins nearly every time. It's explicit, works cleanly with CDNs and HTTP caching, and makes it trivial to roll out a new version in parallel while the old one keeps serving pinned mobile clients. Reserve header-based versioning for internal APIs where you control both sides.
The 12-Month Backward Compatibility Rule
When you ship /v2, keep /v1 running for at least 12 months. Mobile users upgrade slowly — 15-20% of a typical app's install base is on a release older than 6 months. Use Sunset and Deprecation response headers to signal upcoming removal, and send server-side notifications to clients approaching the cutoff.
Non-Breaking Changes Don't Need a New Version
Adding a new optional field, adding a new endpoint, or adding a new response header are backward compatible — ship them within the current version. Only bump to /v2 when you rename a field, change a type, remove a field, or alter required authentication. This keeps your version count low and your clients happy.
Feature Flags for Safe Rollouts
Pair versioning with server-side feature flags (LaunchDarkly, Flagsmith, or a homegrown table) to gate risky changes behind per-user or per-region switches. This lets you ship code continuously while controlling exposure — critical when mobile clients can't rollback.
- Use URL-based versioning (/v1/, /v2/) as the default for mobile-facing APIs
- Maintain every public version for at least 12 months after its successor ships
- Expose Deprecation and Sunset headers on deprecated endpoints
- Add new optional fields freely — they're backward compatible by definition
- Pair versioning with feature flags for controlled, reversible rollouts
- Track version usage in analytics — you need data to safely retire /v1
Authentication: OAuth 2.0, JWT, and API Keys
Broken authentication remains the #1 category in the OWASP API Security Top 10, responsible for over 60% of reported API breaches. For mobile apps, the stakes are even higher — tokens stored on a device can be extracted if storage isn't done right, and a leaked refresh token can mean months of unauthorized access.
Modern mobile APIs in 2026 should combine three primitives: OAuth 2.0 with PKCE for the login flow, JWT access tokens for short-lived authorization, and opaque refresh tokens stored securely on-device. API keys have a narrow but valid role for server-to-server calls and non-sensitive public endpoints.
| Method | Use Case | Token Lifetime | Storage | Security Level |
|---|---|---|---|---|
| OAuth 2.0 + PKCE | Mobile user login (recommended) | Access: 15 min / Refresh: 30 days | Keychain / Keystore | High |
| JWT access token | Per-request authorization | 5-15 minutes | Memory (never disk plain) | High (if short-lived) |
| Refresh token | Renewing access tokens | 30-90 days, rotating | Secure enclave | High (with rotation) |
| API key | Server-to-server, public data | Long-lived, rotatable | Server env vars / vault | Medium |
| Basic auth | Internal tools only | Per-request | Never in mobile apps | Low — avoid publicly |
| mTLS | High-security B2B / partner APIs | Certificate lifetime | Device certificate store | Very High |
OAuth 2.0 with PKCE: The Mobile Standard
Proof Key for Code Exchange (PKCE, RFC 7636) is mandatory for mobile OAuth flows. It prevents authorization code interception attacks by having the client generate a cryptographic verifier that only it can prove knowledge of. Every production mobile app at Frenchy Digital uses OAuth 2.0 + PKCE via our identity provider (Auth0, Supabase Auth, or Cognito depending on the project).
JWT Best Practices
Keep access tokens short-lived (5-15 minutes) and stateless. Sign with RS256 or ES256 — never HS256 for public APIs, because the shared secret must then live in every service. Always validate the issuer (iss), audience (aud), expiration (exp), and not-before (nbf) claims. Never store sensitive PII in the JWT payload — it's only base64-encoded, not encrypted.
Refresh Token Rotation
Rotate refresh tokens on every use — the old token becomes invalid the moment a new one is issued. If your server ever sees a reused refresh token, it's a signal of theft: revoke the entire session family immediately. Store refresh tokens in iOS Keychain (with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) or Android EncryptedSharedPreferences / Keystore.
- OAuth 2.0 + PKCE is non-negotiable for mobile login flows in 2026
- JWT access tokens: short-lived (5-15 min), asymmetric signing (RS256/ES256), no PII in payload
- Refresh tokens: rotate on every use, revoke entire family on reuse detection
- Store tokens in iOS Keychain or Android Keystore — never plain UserDefaults or SharedPreferences
- Use API keys only for server-to-server; never embed them in mobile binaries
- For healthcare or finance, layer biometric re-authentication on sensitive actions
For a deeper treatment of mobile-specific token storage, threat modeling, and jailbreak detection, see our companion article on mobile app security best practices.
Rate Limiting and Throttling: Protecting Your API at Scale
Rate limiting protects your API from abuse, runaway clients, and cost overruns. A well-designed rate limiter is transparent to legitimate users, firm with abusers, and informative to mobile clients so they can back off gracefully. Done badly, it produces random errors that tank your app store rating.
| Algorithm | How It Works | Pros | Cons |
|---|---|---|---|
| Token Bucket | Bucket refills at a fixed rate; each request consumes one token | Allows bursts, simple to reason about | Tuning burst vs sustained requires care |
| Leaky Bucket | Requests queue and drain at a constant rate | Smooth output, predictable load | Latency added when bucket is full |
| Fixed Window | Counter resets every N seconds | Dead simple to implement | Burst at window boundary |
| Sliding Window | Weighted average of current and previous window | Smoother than fixed, cheap in Redis | Slightly more complex |
| Sliding Log | Store timestamp of every request | Most accurate | High memory / storage cost |
Layered Limits Are the Right Default
Apply rate limits at multiple layers simultaneously: per-IP (to stop scrapers), per-user (to prevent account abuse), per-endpoint (to protect expensive operations like search or AI inference), and per-API-key (for B2B partners). Each layer has its own budget; a request must pass all applicable layers.
Communicate Limits Explicitly
Return standard headers on every response — X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. When a request is rejected, return HTTP 429 Too Many Requests with a Retry-After header specifying when to retry. Well-built mobile clients read these headers and back off automatically; clients without them will simply retry and make the problem worse.
Distinguish Abuse From Bursts
Legitimate users occasionally burst — a user who taps refresh five times when the network is slow. Abusive clients hammer endpoints thousands of times. Use a token bucket with a generous burst allowance (2-3x the sustained rate) and alert on sustained 429s from a single actor. Route repeat offenders to a WAF (Cloudflare, AWS WAF) that can block at the network edge.
- Token bucket or sliding window are the two best defaults for modern APIs
- Apply layered limits: per-IP, per-user, per-endpoint, per-API-key
- Always return 429 with Retry-After — never a generic 500 for rate-limited requests
- Expose X-RateLimit-Limit / Remaining / Reset on every response so clients can self-regulate
- Protect expensive endpoints (search, AI inference, reports) with tighter dedicated limits
- Combine application-level rate limits with a WAF for network-level abuse protection
A rate limit without a Retry-After header is just a random 429. A rate limit with clear headers is a contract — and clients who respect contracts scale alongside you instead of against you.
— Frenchy Digital Platform Engineering
Error Handling and HTTP Status Codes
Consistent, predictable error responses are one of the most under-invested parts of API design — and one of the biggest productivity multipliers for mobile teams. A well-designed error contract lets the client distinguish "retry me", "show a friendly message", and "log out the user" without parsing prose.
In 2026, the de-facto standard for HTTP error bodies is RFC 9457 Problem Details for HTTP APIs. Adopt it or a close variant across every endpoint. The key idea: always return a stable, machine-readable error type or code in addition to the HTTP status.
| Status | Meaning | When to Use | Mobile Client Action |
|---|---|---|---|
| 200 OK | Success | Request completed with result | Render data |
| 201 Created | Resource created | POST that produces a new entity | Update local cache |
| 204 No Content | Success, no body | DELETE or idempotent PUT | Confirm action |
| 400 Bad Request | Client validation error | Malformed body, invalid params | Show form errors |
| 401 Unauthorized | No / invalid credentials | Missing or expired token | Refresh token, else log out |
| 403 Forbidden | Authenticated but not allowed | RBAC denial, tier restriction | Show upgrade / denied UI |
| 404 Not Found | Resource missing | Entity does not exist | Show empty state |
| 409 Conflict | State conflict | Duplicate, version mismatch | Prompt user to reconcile |
| 422 Unprocessable | Semantic validation failure | Body valid JSON but invalid data | Show field-level errors |
| 429 Too Many Requests | Rate limited | Quota exceeded | Back off using Retry-After |
| 500 Internal Server Error | Unexpected server fault | Bug or dependency failure | Retry with backoff, toast |
| 502 / 503 / 504 | Upstream / overload | Gateway, maintenance, timeout | Retry with exponential backoff |
The Error Envelope
Every error response should include: a stable error code (e.g. "INVALID_COUPON"), a human-readable message in the user's locale, a request ID for support/logs, and for validation errors a per-field error map. Never include stack traces or internal exception names in production responses — they leak implementation detail and help attackers.
Idempotency Keys
For any POST that creates a resource (payment, order, signup), accept an Idempotency-Key header. Mobile networks are flaky; a user tapping "Pay" twice on a weak connection should never result in two charges. Your server should return the same response for the same key within a 24-48 hour window.
- Adopt RFC 9457 Problem Details or a consistent custom envelope across every endpoint
- Include a stable machine-readable code (INVALID_COUPON) plus a human message
- Always include a request ID in the error body — support and debugging depend on it
- Use 422 for semantic validation, 400 for syntax errors — the distinction helps clients
- Accept Idempotency-Key on all resource-creating POSTs to survive flaky mobile networks
- Never leak stack traces, SQL errors, or internal service names in production responses
Documentation: OpenAPI, Swagger, and Postman
An API is only as good as its documentation. A beautifully designed API with vague or drifting docs will be misused, abandoned, or worked around. In 2026, the winning formula is spec-first development: the OpenAPI document is the source of truth, and code, docs, SDKs, and mocks are all generated or validated against it.
OpenAPI 3.1 (formerly Swagger) aligns with JSON Schema and supports webhooks, reusable components, and detailed security definitions. Combine it with Postman collections for exploration and contract tests for drift detection.
| Tool | Purpose | Why It Matters |
|---|---|---|
| OpenAPI 3.1 Spec | Source of truth contract | One file drives docs, SDKs, mocks, and tests |
| Swagger UI / Redoc | Interactive browser docs | Developers try endpoints without writing code |
| Postman Collection | Exploration and team sharing | Environments, scripts, and CI test runs |
| Stoplight / ReadMe | Hosted API portals | Polished docs with auth, SDKs, changelog |
| Prism / WireMock | Mock server from spec | Mobile teams unblocked before backend ships |
| Schemathesis / Dredd | Contract tests | Catches drift between spec and implementation |
Spec-First, Not Spec-After
Write or update the OpenAPI spec before writing endpoint code. Review the spec like you review code — with comments, approvals, and style checks. This forces design conversations early, when changes are cheap. Frameworks like FastAPI, NestJS, and tRPC can generate spec from code, but spec-first still wins for cross-team APIs.
Generate SDKs Automatically
Tools like openapi-generator, Kiota, and Speakeasy produce typed SDKs in Swift, Kotlin, TypeScript, Python, and more directly from your OpenAPI spec. Mobile teams get compile-time safety and auto-completion instead of hand-crafted request models. When the API changes, the SDK regenerates — and the compiler catches breaking changes before runtime does.
Keep Examples in Sync With Production
Every endpoint should have realistic request and response examples in the spec. Automate verification: run a contract test nightly that hits your staging API and compares responses against the spec. Drift between docs and reality is the #1 source of mobile developer frustration.
- OpenAPI 3.1 as the single source of truth — versioned in the repo, reviewed in PRs
- Publish interactive docs with Swagger UI or Redoc on a stable public URL
- Maintain a Postman collection synced from the OpenAPI spec for exploration
- Auto-generate typed SDKs for Swift, Kotlin, and TypeScript — never hand-write request models
- Run contract tests (Schemathesis, Dredd) in CI to catch spec/implementation drift
- Mock servers generated from the spec let mobile teams start work before the backend ships
API Security: HTTPS, CORS, and Input Validation
Security is not a feature — it's a property of the entire system. The OWASP API Security Top 10 is the minimum baseline every production API must address in 2026. Below are the hardening practices we apply to every API Frenchy Digital ships, from our web platforms to our mobile backends.
HTTPS Everywhere, HTTP/2 or HTTP/3 Preferred
TLS 1.3 is the 2026 baseline. Redirect all HTTP to HTTPS, set HSTS with a max-age of at least 12 months, and use HTTP/2 or HTTP/3 for multiplexing. For mobile apps, enforce certificate pinning when you control both client and server — it neutralizes rogue CAs and man-in-the-middle proxies. Accept a short list of strong cipher suites and nothing else.
CORS: Restrictive by Default
CORS only matters for browser clients — mobile apps bypass it entirely — but a permissive CORS policy is still a frequent bug. Explicitly list allowed origins; never use "*" with credentials. Restrict allowed methods and headers to the minimum needed. Preflight requests (OPTIONS) should be cached via Access-Control-Max-Age to reduce latency.
Input Validation and Output Encoding
Validate every input against a strict schema — prefer JSON Schema or Zod/Pydantic-style runtime validators. Reject unknown fields. Enforce length, type, format, and range. For outputs, encode context-appropriately: HTML-escape for web, parameterize for SQL, and never use string concatenation for queries. The goal is zero trust in user input at every boundary.
Secrets Management
Never commit secrets to git. Use a secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Doppler) and inject at runtime. Rotate database passwords, API keys, and signing keys on a schedule. Keep an audit trail of secret access — most breaches come from stolen credentials, not novel zero-days.
| OWASP API Risk | Example | Mitigation |
|---|---|---|
| Broken Object Level Auth | /orders/123 leaks other user's order | Enforce ownership checks on every resource access |
| Broken Authentication | Weak JWT signing, no refresh rotation | OAuth 2.0 + PKCE, short-lived tokens, rotation |
| Broken Object Property Auth | PATCH updates fields user shouldn't | Explicit allowlist of mutable fields per role |
| Unrestricted Resource Consumption | Unbounded pagination, huge uploads | Max page sizes, body size limits, timeouts |
| Broken Function Level Auth | Admin endpoint callable by any user | Role checks on every privileged endpoint |
| Server-Side Request Forgery | User-provided URL fetched server-side | Allowlist domains, block private IP ranges |
| Security Misconfiguration | Debug mode on, verbose errors | Hardened production configs, secret scanning |
| Improper Inventory Management | Shadow APIs, old versions live | API inventory, automated discovery, retirement SLA |
- TLS 1.3 + HSTS + certificate pinning on mobile for defense in depth
- CORS allowlist specific origins — never wildcard with credentials
- Strict schema validation on every input; reject unknown fields
- Enforce object-level authorization on every resource read and write
- Secrets in a vault, rotated on schedule, never in source control
- Maintain a living API inventory — unknown APIs are unpatched APIs
Monitoring and Observability
You can't improve what you can't see. Modern API observability rests on three pillars — logs, metrics, and traces — unified by OpenTelemetry, which has become the industry-standard instrumentation layer across languages and vendors in 2026.
| Pillar | Purpose | Tools | Key Practice |
|---|---|---|---|
| Structured Logs | Event detail for debugging | Datadog, Loki, CloudWatch, Elastic | JSON format, correlation IDs, PII-free |
| RED Metrics | Rate, Errors, Duration per endpoint | Prometheus, Datadog, New Relic | p50 / p95 / p99 latency, SLOs |
| Distributed Tracing | Request path across services | Jaeger, Tempo, Honeycomb | OpenTelemetry SDK in every service |
| Real User Monitoring | Client-side experience | Datadog RUM, Sentry, Firebase Perf | Capture API latency from the mobile app |
| Synthetic Monitoring | Active uptime probes | Checkly, Pingdom, Grafana Synthetic | Probe critical user journeys every minute |
Define and Honor SLOs
Service Level Objectives translate reliability into a budget. For a typical mobile API we target 99.9% success rate and p95 latency under 400ms. When the SLO error budget is being burned too fast, the on-call engineer is paged and feature work pauses until the budget recovers. This is how mature teams balance velocity and reliability.
Correlation IDs Across the Stack
Generate a unique request ID at the API gateway and propagate it through every log line, trace span, and downstream service call. Return it in response headers (X-Request-Id) so mobile clients can include it in crash reports. When a user contacts support, one ID unlocks the entire request history.
Alert on Symptoms, Not Causes
Page on user-visible symptoms — elevated error rate, high latency, missed SLO — not internal signals like CPU or memory. Those are interesting for diagnosis but shouldn't wake anyone up. Every alert should map to a runbook and a clear remediation. Noisy alerts cause alert fatigue, which is how real incidents get missed.
- Structured JSON logs with correlation IDs — never free-text, never PII
- RED metrics (Rate, Errors, Duration) per endpoint, plotted at p50/p95/p99
- OpenTelemetry tracing across every service — no black-box hops
- Real User Monitoring on the mobile side to catch what server metrics miss
- SLOs with error budgets — not uptime targets on a slide
- Alerts target user-visible symptoms with clear runbooks
The difference between a 99.9% API and a 99.95% API is rarely better code — it's better observability. You cannot close what you cannot see, and five nines of uptime is earned one alert at a time.
— Frenchy Digital SRE Playbook
Observability is the final layer that turns a functional API into a dependable one. Combined with the seven practices above — sound protocol choice, thoughtful versioning, strong authentication, graceful rate limiting, consistent errors, spec-first docs, and security in depth — you have the full toolkit for building APIs that mobile applications can rely on for years. If you want a team that has shipped this playbook across 50+ mobile products, Frenchy Digital would love to talk.
Build an API That Your Mobile App Can Depend On
From OpenAPI-first design to OAuth 2.0, rate limiting, and OpenTelemetry observability — Frenchy Digital's engineering team ships production-grade APIs for mobile apps in Los Angeles and beyond. Get a free architecture review.
Need an API That Scales With Your Mobile App?
Frenchy Digital's 49-person engineering team designs and builds production-grade APIs for mobile apps — secure, observable, and battle-tested. Book a free API architecture review.
1517 S Bentley Ave Unit 204, Los Angeles CA 90025

