Phase 5 · Professional ApplicationsModule 29~50 min read

REST APIs & JSON

Design predictable JSON APIs with resource URLs, correct HTTP semantics, validation, errors, and documentation.

What you'll learn

Design an API that clients can predict. Resource URLs, HTTP semantics, stable representations, validation errors, pagination metadata, and idempotency form a public contract that outlives any controller implementation.

By the end of this lesson, you'll be able to:

  • Design resource-oriented endpoints
  • Return consistent success and error documents
  • Apply validation, status codes, pagination, and idempotency

Core mental model

Professional PHP is less about memorizing APIs and more about choosing a clear boundary for each responsibility. Use this table as a decision guide while reading the examples.

ConceptWhat it protectsDecision rule
ResourceStable API vocabularyModel nouns and manipulate them with HTTP methods.
RepresentationClient-facing data shapeSerialize deliberately; never expose tables directly.
IdempotencySafe retriesA repeated request should not duplicate a completed effect.

Professional workflow

Build the feature in small, verifiable steps. Each step leaves the system in a state you can test.

  1. Describe the JSON API contract boundary: its inputs, outputs, invariants, and expected failures.
  2. Implement the smallest happy path behind an explicit contract.
  3. Add validation and translate low-level failures into language the caller understands.
  4. Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
  5. Refactor only after behavior is protected by a repeatable check.

Make the boundary visible

Name inputs, outputs, side effects, and failure cases before adding framework or infrastructure code. That habit keeps advanced PHP understandable as the application grows.

Guided code lab

Return a resource envelope

The serializer controls public fields and the response includes a useful Location header.

create-course.php
<?php
$course = $createCourse->handle(CreateCourseData::fromArray($request->json()));

return Response::json([
    'data' => [
        'id' => $course->id,
        'type' => 'course',
        'attributes' => ['title' => $course->title, 'status' => $course->status->value],
    ],
], status: 201, headers: ['Location' => "/api/courses/{$course->id}"]);

Make validation errors actionable

A stable code serves software clients; field details support forms without leaking internals.

validation-error.php
<?php
return Response::json([
    'error' => [
        'code' => 'validation_failed',
        'message' => 'The request contains invalid fields.',
        'fields' => [
            'title' => ['The title is required.'],
            'price' => ['The price must be a non-negative integer.'],
        ],
        'request_id' => $request->id(),
    ],
], status: 422);

Production practice

Contract

Version behavior intentionally and document examples, status codes, authentication, limits, and error codes.

Verification

Use contract tests for response schemas and feature tests for authorization, retries, malformed JSON, and limits.

Operations

Track latency and error rate by route; apply request IDs, rate limits, payload limits, and deprecation windows.

Common failure mode

Returning a database row ties clients to private column names and makes schema changes breaking API changes.

Independent workshop

Create a versioned course API with list, create, partial update, delete, filtering, cursor pagination, and a duplicate-safe enrollment command.

Your finished workshop must include:

  • OpenAPI-style endpoint document
  • Stable serializer and error format
  • Idempotency and authorization tests

Definition of done

Run the happy path and at least two failure paths, explain one design tradeoff in a short README, and leave the code formatted and ready for review.

Recap & quick check

Key takeaways

  • An API is a long-lived contract.
  • HTTP methods and status codes communicate semantics.
  • Serializers protect the public representation.
  • Idempotency makes network retries safe.

Quick check

1. Which status fits successful creation?

2. Why use a serializer?

3. What does idempotency prevent on retry?

Keep the workshop: later phases deliberately build on these boundaries, so today's small example can become part of your portfolio architecture.