Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

REST API Design Best Practices in 2026

By Kokil Thapa | Last reviewed: September 2026

Your mobile app, partner dashboard, and checkout flow all depend on the same contract. Poor REST API design best practices in 2026 create breaking changes, duplicate endpoints, and support tickets that never end. I've shipped REST APIs on Laravel production systems since 2010, from legal-tech portals to eCommerce carts. This guide covers what still matters in 2026: resource naming, versioning, auth, pagination, error shape, and documentation you can hand to the next developer without a meeting.

What Are the Core Principles of REST API Design in 2026?

REST still means resources, not actions. You model things—orders, users, documents—and use HTTP verbs to change state. A common mistake is encoding verbs in URLs like /getUserById. That breaks caching, confuses clients, and ages badly when you add GraphQL or webhooks later.

Start with a resource map before you write a controller. On a legal-tech portal I built, we grouped endpoints around case files, appointments, and payments. Each collection got one plural noun. Sub-resources used nesting only when ownership was strict.

REST API Resource ArchitectureMobile AppJSON over HTTPSWeb SPABearer tokenPartner APIAPI key + scopeWebhooksSigned eventsAPI Gateway / Rate LimitAuth, throttling, request ID/v1/ordersCollection + item/v1/usersProfile + roles/v1/paymentsIdempotent POST
REST API design maps clients to versioned resource collections through a single gateway layer

URL and HTTP verb rules that survive refactors

Use plural nouns, kebab-case paths, and predictable nesting. Keep nesting shallow—two levels is usually enough.

  • GET /v1/orders — list with filters
  • GET /v1/orders/{id} — single resource
  • POST /v1/orders — create
  • PATCH /v1/orders/{id} — partial update
  • DELETE /v1/orders/{id} — remove when safe

Return correct status codes. 201 Created with a Location header beats 200 OK on POST. Use 204 No Content for deletes that need no body. Reserve 409 Conflict for duplicate idempotency keys or version clashes.

The HTTP semantics in RFC 9110 remain the authoritative reference for status codes and method safety. Your framework wraps these rules; it does not replace them.

How Should You Version and Document a REST API in 2026?

Version in the URL path for public APIs: /v1/, /v2/. Header-only versioning sounds elegant until a partner bookmarks the wrong host. I've seen production incidents from "temporary" unversioned routes that lived for three years.

Pair every version with an OpenAPI 3.1 specification. Generate it from annotations or attributes, then publish it beside your docs. On Laravel 12 or 13 projects, tools like Scribe produce readable docs from controllers—see our guide on API documentation with Scribe for Laravel.

  1. Define breaking vs non-breaking changes before you ship v1.
  2. Publish deprecation headers: Sunset and Deprecation per RFC 8594.
  3. Keep v1 read-only for six months after v2 ships for write paths.
  4. Run contract tests so response shapes cannot drift silently.

For a deeper lifecycle playbook, read API deprecation and sunset best practices. Versioning strategy for Laravel apps is covered in Laravel API versioning strategy.

Example versioned route group in Laravel 13

// routes/api.php
Route::prefix('v1')->middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::apiResource('orders', OrderController::class)->only(['index', 'show', 'store']);
    Route::post('orders/{order}/pay', [PaymentController::class, 'store'])->middleware('idempotent');
});

Route::prefix('v2')->middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::apiResource('orders', V2\OrderController::class);
});

Symfony 8.1 API Platform projects follow the same idea with /api/v1 prefixes and separate serialization groups per version. See Symfony API Platform for REST and GraphQL for framework-specific patterns.

What Authentication and Security Patterns Should REST APIs Use?

Public mobile and SPA clients should use short-lived access tokens with refresh rotation. Machine-to-machine integrations fit API keys scoped to least privilege. Never pass tokens in query strings—they land in logs and referrer headers.

On production Laravel applications I default to Sanctum for first-party SPAs and Passport when third-party OAuth clients need scopes. Compare both in Laravel Passport vs Sanctum. Broader OAuth guidance lives in OAuth security best practices.

REST Request Lifecycle1. ClientHTTPS + token2. AuthScope check3. ValidateForm Request4. HandlerBusiness logic5. SerializeAPI Resource / DTO6. JSON ResponseStatus + envelopeError Path (4xx / 5xx)Same JSON shape alwayscode, message, details[]request_id for support
Every REST API request should pass auth, validation, and serialization before returning a consistent JSON envelope

Security checklist for production APIs

  • Enforce TLS 1.2+ everywhere; HSTS on public domains.
  • Rate-limit by token and IP; return 429 with Retry-After.
  • Validate all input server-side—never trust client JSON schema alone.
  • Log request IDs, not passwords, tokens, or PAN data.
  • Rotate keys and revoke compromised tokens through an admin endpoint.

Server hardening overlaps with Ubuntu server security best practices and Linux system administration work. Treat the API layer and the host as one surface.

How Do You Handle Pagination, Filtering, and Large Payloads?

Offset pagination (?page=2&per_page=50) is fine for admin screens under ten thousand rows. For high-volume lists—orders, audit logs, webhook deliveries—use cursor pagination keyed on a stable sort column.

GET /v1/orders?limit=50&cursor=eyJpZCI6MTAwMjN9
{
  "data": [ /* orders */ ],
  "meta": {
    "next_cursor": "eyJpZCI6MTAwNzN9",
    "has_more": true
  }
}

Expose filtering through whitelisted query params, not raw SQL fragments. ?status=paid&created_after=2026-01-01 beats a generic ?filter=... string clients must parse.

Compress large JSON with gzip at the reverse proxy. Cap per_page at 100 unless the client has a signed enterprise agreement. For heavy exports, return 202 Accepted and poll a job status URL—or push completion through webhook design patterns.

Validate sample payloads with the free JSON formatter during development. It catches trailing commas and schema drift before they hit staging.

REST vs GraphQL: Which Should You Choose in 2026?

REST wins when you have diverse clients, strong caching needs, and a team that already runs Laravel or Symfony monoliths. GraphQL wins when one frontend needs flexible field selection across many entities—and you accept query cost limits and N+1 risk.

Most Nepal SMB and agency projects I see still ship REST first. The Quick And Easy Nepalese Grocery Laravel cart and Mijar Law Associates client portal both started REST-only. GraphQL arrived only where mobile bandwidth truly justified it.

REST vs GraphQL DecisionNew public API?Multiple clients + caching?YesChoose RESTVersioned resourcesNoSingle rich UI?Check query costGraphQL may fitAdd cost limitsDefault: REST firstAdd GraphQL only with proofNot as a default choice
REST API design best practices in 2026 still favour REST unless GraphQL solves a measured client problem
CriterionRESTGraphQL
Caching (CDN/browser)Strong via URLs and ETagsWeak; mostly POST queries
VersioningExplicit /v1, /v2 pathsSchema evolution; deprecate fields
Over-fetchingFixed response shapesClient selects fields
Team learning curveLow; HTTP-nativeHigher; resolvers, cost analysis
File uploadsMultipart POST worksNeeds separate upload flow
Best fit in 2026Public APIs, partners, mobile backendsComplex single-page apps

Read the full trade-off analysis in GraphQL vs REST trade-offs. If you expose Shopify or WooCommerce data, platform REST APIs remain the stable path—see WooCommerce REST API for mobile apps and Magento 2 REST API for headless storefronts.

How Should Error Responses and Idempotency Work?

Every error should share one JSON envelope. Clients parse one shape forever. Field-level validation lives in a details array.

{
  "error": {
    "code": "order_not_found",
    "message": "Order 8842 does not exist.",
    "details": [],
    "request_id": "req_8f3a2c"
  }
}

Map domain errors to HTTP codes deliberately. 422 Unprocessable Entity for validation. 403 Forbidden when auth passed but policy blocked the action. Do not use 500 for business rule failures—that triggers pager duty for no reason.

Idempotent POST for payments and webhooks

Payment endpoints must accept an Idempotency-Key header. Store the key with the result for 24 hours. Duplicate requests return the original response without double-charging.

POST /v1/payments
Idempotency-Key: pay_20260908_001
Content-Type: application/json

{ "order_id": 8842, "gateway": "khalti", "amount": 150000 }

I've debugged duplicate charges on eCommerce systems where this header was missing. The fix is always server-side storage, not client retries alone. Local gateways like Khalti and eSewa expect your app to treat callback URLs as at-least-once delivery.

Contract testing catches envelope drift before release. See API contract testing with Pact and testing and optimization services for teams without dedicated QA.

API Version Lifecyclev1 ActiveAll clientsv2 RolloutNew writes migratev1 SunsetRead-only, then offCommunicate: docs, changelog, Deprecation headerMinimum 90-day notice for breaking changesMonitor v1 trafficDashboard by API keyIntegration tests on bothCI blocks silent breaks
Plan REST API version sunset early so partners migrate before you remove v1 endpoints

What Laravel and Symfony Patterns Speed Up Good API Design?

Laravel 13 with PHP 8.3+ gives you Form Requests, API Resources, policies, and queues out of the box. Symfony 8.1 with API Platform auto-generates CRUD from entities when you need speed—but never expose entities raw to the public internet.

Follow how to build a REST API in Laravel the right way and Sanctum authentication setup for a solid baseline. Laravel API best practices and modern Laravel architecture cover service layers and testing.

Response transformation example

// app/Http/Resources/OrderResource.php (Laravel 13)
public function toArray(Request $request): array
{
    return [
        'id' => $this->uuid,
        'status' => $this->status,
        'total' => Money::ofMinor($this->total_paisa, 'NPR')->format(),
        'created_at' => $this->created_at->toIso8601String(),
    ];
}

Never leak internal IDs, stack traces, or SQL errors. Log the full exception server-side; return request_id to the client.

Gateways like Kong or Traefik sit in front when you run multiple services. See Kong API gateway guide and Traefik as an API gateway. For AI features, isolate LLM calls behind internal routes—OpenAI API integration in Laravel shows the pattern.

Need a team to design or refactor your surface? Custom software development and enterprise application development cover greenfield and legacy API work. Browse the portfolio for shipped examples.

Key Takeaways

  • Model nouns, not verbs; use HTTP methods and status codes as RFC 9110 defines them.
  • Version in the URL, document with OpenAPI, and sunset old versions with explicit headers.
  • Authenticate with scoped tokens, rate-limit aggressively, and never log secrets.
  • Return one error envelope everywhere; add idempotency keys on payment and create endpoints.
  • Prefer cursor pagination for large collections; cap page size and offload exports to jobs.
  • Default to REST in 2026; add GraphQL only when a measured client problem justifies the ops cost.

People Also Ask

Should REST APIs use PUT or PATCH for updates?

Use PATCH for partial updates—changing one or two fields on a resource. PUT replaces the entire resource and should require all mandatory fields. Most Laravel and Symfony apps expose PATCH on collection members and reserve PUT for idempotent full replacements.

What is the best way to paginate REST API results?

Use offset pagination for small, stable admin lists. Use cursor pagination keyed on an indexed column for feeds, orders, and logs that grow daily. Always return has_more or a next link so clients stop guessing.

How long should API access tokens last?

Access tokens of 15–60 minutes with refresh rotation fit most SPAs and mobile apps. Machine clients can use longer-lived API keys stored in secrets managers. Shorter lifetimes limit damage when a token leaks.

Is REST still relevant with GraphQL and gRPC in 2026?

Yes. REST remains the default for public HTTP APIs, partner integrations, and cached read-heavy workloads. GraphQL and gRPC complement REST—they rarely replace it entirely on business systems built in PHP or WordPress.

Ship APIs Your Next Developer Will Thank You For

Good REST API design best practices in 2026 are boring on purpose. Clear URLs, explicit versions, honest errors, and docs that match production beat clever abstractions every time. Start with one resource, one OpenAPI file, and one integration test—then grow from there.

If you want help auditing an existing API or designing v1 before mobile launch, contact us for a scoped review. You can also explore building RESTful APIs with Laravel and SDK design for your public API for the next layer after your HTTP surface is stable.

Frequently Asked Questions

Noun-based URLs, explicit /v1/ versioning, consistent JSON error envelopes, cursor pagination for large lists, OAuth or token auth, idempotent writes, and OpenAPI docs tested against real responses.

Use PATCH for partial updates on one or two fields. PUT replaces the entire resource and requires all mandatory fields. Most Laravel and Symfony apps expose PATCH on collection members.

Access tokens of 15–60 minutes with refresh rotation fit most SPAs and mobile apps. Machine clients can use longer-lived API keys stored in secrets managers.

REST models resources, not actions. Avoid verb-heavy paths like /getUserById—they break caching, confuse clients, and age badly when you add webhooks or GraphQL later. Start with a resource map before writing controllers. Use plural nouns, kebab-case paths, and shallow nesting—two levels is usually enough. On a legal-tech portal I built, endpoints grouped around case files, appointments, and payments, each as one plural collection with sub-resources nested only when ownership was strict.

Version in the URL path: /v1/, /v2/. Header-only versioning sounds elegant until a partner bookmarks the wrong host—I have seen production incidents from temporary unversioned routes that lived for years. Pair every version with an OpenAPI 3.1 specification generated from annotations or attributes. Define breaking versus non-breaking changes before shipping v1. Publish Sunset and Deprecation headers per RFC 8594, keep v1 read-only for six months after v2 ships write paths, and run contract tests so response shapes cannot drift silently.

Return correct status codes aligned with RFC 9110 semantics—your framework wraps these rules but does not replace them. Return 201 Created with a Location header on POST instead of 200 OK. Use 204 No Content for deletes that need no body. Reserve 409 Conflict for duplicate idempotency keys or version clashes. Map validation failures to 422 Unprocessable Entity, policy blocks to 403 Forbidden, and never return 500 for business rule failures—that triggers pager duty for no reason.

Pair every version with an OpenAPI 3.1 specification and publish it beside your docs. On Laravel 12 or 13 projects, tools like Scribe produce readable docs from controllers. Generate the spec from annotations or attributes, then keep it tested against real responses—not ad-hoc endpoints that mirror database tables. Contract tests catch envelope drift before release. Good documentation means the next developer can integrate without a meeting, because the published spec matches what production actually returns.

Public mobile and SPA clients should use short-lived access tokens with refresh rotation. Machine-to-machine integrations fit API keys scoped to least privilege. Never pass tokens in query strings—they land in logs and referrer headers. On production Laravel applications I default to Sanctum for first-party SPAs and Passport when third-party OAuth clients need scopes. Every request should pass auth, validation, and serialization before returning a consistent JSON envelope. Rotate keys and revoke compromised tokens through an admin endpoint.

Enforce TLS 1.2+ everywhere with HSTS on public domains. Rate-limit by token and IP, returning 429 with Retry-After when limits are hit. Validate all input server-side—never trust client JSON schema alone. Log request IDs, not passwords, tokens, or PAN data. Treat the API layer and the host as one surface: server hardening overlaps with broader Ubuntu and Linux administration work. Scoped tokens, aggressive rate limiting, and honest error envelopes form the baseline checklist before you expose any public endpoint.

Offset pagination with ?page=2&per_page=50 is fine for admin screens under ten thousand rows. For high-volume lists—orders, audit logs, webhook deliveries—use cursor pagination keyed on a stable, indexed sort column. Return next_cursor and has_more in a meta object so clients stop guessing. Expose filtering through whitelisted query params like ?status=paid&created_after=2026-01-01, not raw SQL fragments. Cap per_page at 100 unless the client has a signed enterprise agreement, and compress large JSON with gzip at the reverse proxy.

REST wins when you have diverse clients, strong caching needs via URLs and ETags, and a team already running Laravel or Symfony monoliths. GraphQL wins when one frontend needs flexible field selection across many entities—and you accept query cost limits and N+1 risk. Most Nepal SMB and agency projects I see still ship REST first. Default to REST in 2026; add GraphQL only when a measured client problem justifies the operational cost. Platform APIs from WooCommerce and Magento also remain REST-first for headless storefronts.

Every error should share one JSON envelope so clients parse one shape forever. Include a stable code, human-readable message, a details array for field-level validation, and a request_id for support correlation. Map domain errors to HTTP codes deliberately: 422 for validation, 403 when auth passed but policy blocked the action. Never leak internal IDs, stack traces, or SQL errors—log the full exception server-side and return request_id to the client. Contract testing catches envelope drift before release.

Payment endpoints must accept an Idempotency-Key header. Store the key with the result for 24 hours—duplicate requests return the original response without double-charging. I have debugged duplicate charges on eCommerce systems where this header was missing; the fix is always server-side storage, not client retries alone. Local gateways like Khalti and eSewa expect your app to treat callback URLs as at-least-once delivery. Reserve 409 Conflict for duplicate idempotency keys. Idempotent POST is non-negotiable for payments and webhook-driven creates.

Yes. REST remains the default for public HTTP APIs, partner integrations, and cached read-heavy workloads. GraphQL and gRPC complement REST—they rarely replace it entirely on business systems built in PHP or WordPress. REST offers strong CDN and browser caching, explicit /v1 and /v2 versioning, and a low HTTP-native learning curve. GraphQL adds resolver complexity and weaker caching. Ship REST first unless bandwidth or field-selection requirements are measured and documented, not assumed.

Laravel 13 with PHP 8.3+ gives you Form Requests, API Resources, policies, and queues out of the box. Use API Resources to transform responses—expose UUIDs and formatted values, never raw internal IDs. Symfony 8.1 with API Platform auto-generates CRUD from entities when you need speed, but never expose entities raw to the public internet. Version route groups with Sanctum auth and throttle middleware. Gateways like Kong or Traefik sit in front when you run multiple services. Isolate LLM calls behind internal routes when adding AI features.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: