Home >Backend Development >PHP Tutorial >Build a REST API from Scratch: Implementation

Build a REST API from Scratch: Implementation

William Shakespeare
William ShakespeareOriginal
2025-02-20 10:13:10681browse

Build a REST API from Scratch: Implementation

This tutorial's first part established our API's foundational layers: server setup, authentication, JSON handling, error management, and initial routes. Crucially, we defined resources and actions in the README. Now, let's build upon this foundation by implementing those resources.

Key Concepts:

  • RESTful Best Practices: Create and update operations return resource representations. Robust ORM/Model and validation libraries are essential for production applications.
  • Error Handling & Validation: Rigorous error handling and validation ensure data integrity and appropriate responses. This includes mandatory fields (like first name) and unique email checks.
  • Advanced Querying: Implement filtering, sorting, and pagination for efficient data retrieval and improved client-side performance.
  • Caching: Utilize ETag and server-side caching (e.g., APC) with middleware for optimized response times and reduced server load.
  • Rate Limiting: Middleware-based rate limiting protects against API overuse, ensuring availability and reliability.
  • Future Enhancements: Plan for scalability by considering advanced ORM/Model solutions, external validation libraries, and alternative storage options beyond APC.

Contact Management: Creation and Updates

Starting with contact creation, REST best practices dictate returning a resource representation after creation or update. While the database interaction in this example is simplified for clarity, a production API would leverage a more robust ORM/Model and validation library.

<code class="language-php">$app->post(
'/contacts',
function () use ($app, $log) {
    $body = $app->request()->getBody();
    $errors = $app->validateContact($body);

    if (empty($errors)) {
        $contact = \ORM::for_table('contacts')->create();

        if (isset($body['notes'])) {
            $notes = $body['notes'];
            unset($body['notes']);
        }

        $contact->set($body);

        if ($contact->save()) {
            if (!empty($notes)) {
                $contactNotes = [];
                foreach ($notes as $item) {
                    $item['contact_id'] = $contact->id;
                    $note = \ORM::for_table('notes')->create();
                    $note->set($item);
                    if ($note->save()) {
                        $contactNotes[] = $note->asArray();
                    }
                }
            }

            $output = $contact->asArray();
            if (!empty($contactNotes)) {
                $output['notes'] = $contactNotes;
            }
            echo json_encode($output, JSON_PRETTY_PRINT);
        } else {
            throw new Exception("Unable to save contact");
        }
    } else {
        throw new ValidationException("Invalid data", 0, $errors);
    }
}
);</code>

This POST /contacts endpoint processes the request body, validates data, creates a contact record, handles associated notes (if provided), and returns a JSON representation of the created contact. Update operations (PUT and PATCH) follow a similar pattern, checking for contact and note existence before processing. The PUT and PATCH methods are mapped to the same code for efficiency:

<code class="language-php">$app->map(
'/contacts/:id',
function ($id) use ($app, $log) {
    // Update code here...
})->via('PUT', 'PATCH');</code>

Contact Listing and Filtering

Basic contact listing is straightforward:

<code class="language-php">$app->get(
'/contacts',
function () use ($app, $log) {
    $contacts = \ORM::forTable('contacts')->findArray();
    echo json_encode($contacts, JSON_PRETTY_PRINT);
}
);</code>

However, a robust API supports advanced querying: /api/v1/contacts?fields=firstname,email&sort=-email&firstname=Viola&q=vitae This example demonstrates filtering by firstname, searching within firstname or email using q, sorting by email, and selecting specific fields. The implementation involves sanitizing inputs and dynamically building the database query:

<code class="language-php">$app->post(
'/contacts',
function () use ($app, $log) {
    $body = $app->request()->getBody();
    $errors = $app->validateContact($body);

    if (empty($errors)) {
        $contact = \ORM::for_table('contacts')->create();

        if (isset($body['notes'])) {
            $notes = $body['notes'];
            unset($body['notes']);
        }

        $contact->set($body);

        if ($contact->save()) {
            if (!empty($notes)) {
                $contactNotes = [];
                foreach ($notes as $item) {
                    $item['contact_id'] = $contact->id;
                    $note = \ORM::for_table('notes')->create();
                    $note->set($item);
                    if ($note->save()) {
                        $contactNotes[] = $note->asArray();
                    }
                }
            }

            $output = $contact->asArray();
            if (!empty($contactNotes)) {
                $output['notes'] = $contactNotes;
            }
            echo json_encode($output, JSON_PRETTY_PRINT);
        } else {
            throw new Exception("Unable to save contact");
        }
    } else {
        throw new ValidationException("Invalid data", 0, $errors);
    }
}
);</code>

This section would include the detailed code for handling fields, sort, page, per_page parameters, building the query, and managing pagination including the generation of Link headers.

Contact Details and Embedded Resources

Retrieving individual contact details is simple:

<code class="language-php">$app->map(
'/contacts/:id',
function ($id) use ($app, $log) {
    // Update code here...
})->via('PUT', 'PATCH');</code>

To improve efficiency, embedded resources (e.g., notes) can be fetched using query parameters like /api/v1/contacts/1?embed=notes. The code would be modified to include an additional query for notes if the embed parameter is present.

Caching and Rate Limiting

Caching and rate limiting are implemented using middleware, enhancing performance and API protection. The middleware code (for both caching and rate limiting) would be similar to the original example, handling cache hits/misses, ETag generation, expiration, and rate limit checks, including the appropriate HTTP headers.

Further Development

This enhanced API provides a solid foundation. Future improvements include migrating to a more robust ORM/Model, integrating a dedicated validation library, exploring alternative storage solutions, implementing API discovery (e.g., Swagger), and creating a comprehensive test suite. The complete source code (as mentioned in the original) would provide the full implementation details.

The above is the detailed content of Build a REST API from Scratch: Implementation. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn