Nesta aula, vamos mergulhar na construção de uma API REST utilizando PHP. Você aprenderá os fundamentos essenciais para criar uma API moderna, desde os verbos HTTP até o tratamento de respostas em JSON. Ao final, terá uma base sólida para desenvolver seus próprios serviços web.

Uma API (Application Programming Interface) permite que diferentes sistemas se comuniquem. No contexto web, as APIs REST (Representational State Transfer) são amplamente utilizadas por serem simples, escaláveis e baseadas em protocolos padrão como HTTP. Vamos explorar cada componente necessário para criar uma API REST funcional em PHP.

Verbos HTTP

Os verbos HTTP (ou métodos) indicam a ação que deseja realizar sobre um recurso. Em uma API REST, os verbos mais comuns são: GET (recuperar dados), POST (criar um novo recurso), PUT (atualizar um recurso existente) e DELETE (remover um recurso). Cada verbo tem um significado semântico claro, e sua correta utilização é fundamental para o design da API.

No PHP, você pode acessar o método HTTP utilizado na requisição através da variável superglobal $_SERVER['REQUEST_METHOD']. Isso permite que você direcione o fluxo da aplicação de acordo com o verbo recebido. Por exemplo, uma rota /produtos pode responder de maneiras diferentes dependendo do verbo: GET para listar produtos, POST para criar um novo, etc.

Exemplo de verificação de método:

<?php

// Verifica o método HTTP da requisição
$method = $_SERVER['REQUEST_METHOD'];

switch ($method) {
    case 'GET':
        // lógica para GET
        break;
    case 'POST':
        // lógica para POST
        break;
    case 'PUT':
        // lógica para PUT
        break;
    case 'DELETE':
        // lógica para DELETE
        break;
    default:
        // método não suportado
        http_response_code(405);
        echo json_encode(['erro' => 'Método não permitido']);
        break;
}

É importante tratar também o método OPTIONS, que é frequentemente usado em requisições CORS (Cross-Origin Resource Sharing) para verificar as permissões antes de uma requisição real.

Rotas

Rotas são os caminhos que definem os recursos da API. Em uma API REST, cada rota deve representar um recurso (ex.: /usuarios) e, combinada com o verbo HTTP, define uma ação específica. No PHP, você pode implementar rotas de várias maneiras: desde um simples if/else baseado na URL, até o uso de bibliotecas como o Symfony Routing ou o FastRoute.

Para uma API simples, podemos analisar a URL requisitada e combinar com padrões. A variável $_SERVER['REQUEST_URI'] contém o caminho da requisição. Vamos criar um pequeno roteador:

<?php

// Obtém o caminho da URL, removendo query strings
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

// Define uma rota base
if ($uri === '/produtos' && $_SERVER['REQUEST_METHOD'] === 'GET') {
    // retorna lista de produtos
} elseif ($uri === '/produtos' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    // cria um novo produto
} elseif (preg_match('#^/produtos/(\d+)$#', $uri, $matches) && $_SERVER['REQUEST_METHOD'] === 'GET') {
    $id = $matches[1];
    // retorna produto específico
} else {
    http_response_code(404);
    echo json_encode(['erro' => 'Rota não encontrada']);
}

Em projetos maiores, é recomendável usar um microframework como Slim ou Lumen, que fornecem um sistema de rotas mais robusto e seguro. Eles permitem definir rotas com parâmetros, middlewares e validação, facilitando a manutenção.

JSON

JSON (JavaScript Object Notation) é o formato mais comum para troca de dados em APIs REST. Ele é leve, legível por humanos e facilmente interpretado por máquinas. No PHP, você pode converter arrays ou objetos em JSON usando a função json_encode() e decodificar JSON recebido com json_decode().

Ao enviar respostas, você deve definir o cabeçalho Content-Type: application/json para que o cliente saiba que o conteúdo é JSON. Da mesma forma, ao receber dados JSON no corpo da requisição, você deve capturar o conteúdo e decodificá-lo.

Exemplo de resposta JSON:

<?php

// Dados de exemplo
$produtos = [
    ['id' => 1, 'nome' => 'Notebook', 'preco' => 2500],
    ['id' => 2, 'nome' => 'Mouse', 'preco' => 50]
];

// Define o cabeçalho de conteúdo como JSON
header('Content-Type: application/json');

// Converte o array para JSON e imprime
echo json_encode($produtos);

Para ler dados JSON enviados pelo cliente (por exemplo, em uma requisição POST), você pode usar o fluxo de entrada php://input:

<?php

// Lê o corpo da requisição
$json = file_get_contents('php://input');

// Decodifica o JSON para um array associativo
$data = json_decode($json, true);

// Agora $data contém os dados enviados

É importante validar se o JSON é válido. A função json_decode() retorna null se o JSON for inválido, então você pode verificar com json_last_error().

Status codes

Os códigos de status HTTP indicam o resultado da requisição. Eles são essenciais para que o cliente entenda se a operação foi bem-sucedida, se houve erro, etc. Em uma API REST, você deve definir códigos apropriados para cada situação, como 200 (OK), 201 (Created), 404 (Not Found), 400 (Bad Request) e 500 (Internal Server Error).

No PHP, você pode definir o código de status usando a função http_response_code(). É uma boa prática retornar um corpo JSON com uma mensagem de erro quando algo der errado, para que o cliente possa exibir uma resposta amigável.

Exemplo de uso:

<?php

// Suponha que o recurso não foi encontrado
http_response_code(404);
echo json_encode(['erro' => 'Recurso não encontrado']);

// Para sucesso ao criar um recurso
http_response_code(201);
echo json_encode(['mensagem' => 'Produto criado com sucesso']);

A tabela abaixo lista os códigos mais usados em APIs REST:

  • 200 OK - Requisição bem-sucedida.
  • 201 Created - Recurso criado com sucesso.
  • 204 No Content - Sucesso, mas sem conteúdo para retornar.
  • 400 Bad Request - Requisição malformada ou dados inválidos.
  • 401 Unauthorized - Autenticação necessária.
  • 403 Forbidden - Sem permissão para acessar o recurso.
  • 404 Not Found - Recurso não encontrado.
  • 405 Method Not Allowed - Método HTTP não suportado para a rota.
  • 422 Unprocessable Entity - Dados semânticamente inválidos.
  • 500 Internal Server Error - Erro inesperado no servidor.

Referências

Exercícios

  1. Escreva um código PHP que verifique o método HTTP da requisição e retorne uma mensagem diferente para cada método (GET, POST, PUT, DELETE).

    ✓ Resposta:
    <?php
    
    $method = $_SERVER['REQUEST_METHOD'];
    
    switch ($method) {
        case 'GET':
            echo "Método GET";
            break;
        case 'POST':
            echo "Método POST";
            break;
        case 'PUT':
            echo "Método PUT";
            break;
        case 'DELETE':
            echo "Método DELETE";
            break;
        default:
            echo "Método não suportado";
    }
    
  2. Crie uma rota que retorne um produto específico com base em um ID passado na URL (ex.: /produtos/10). Use expressões regulares para capturar o ID.

    ✓ Resposta:
    <?php
    
    $uri = $_SERVER['REQUEST_URI'];
    
    if (preg_match('#^/produtos/(\d+)$#', $uri, $matches)) {
        $id = $matches[1];
        $produto = ['id' => $id, 'nome' => 'Produto ' . $id];
        header('Content-Type: application/json');
        echo json_encode($produto);
    } else {
        http_response_code(404);
        echo json_encode(['erro' => 'Rota não encontrada']);
    }
    
  3. Implemente um endpoint que receba dados JSON via POST e os retorne de volta como resposta, com status 201.

    ✓ Resposta:
    <?php
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $json = file_get_contents('php://input');
        $data = json_decode($json, true);
        if ($data === null) {
            http_response_code(400);
            echo json_encode(['erro' => 'JSON inválido']);
        } else {
            http_response_code(201);
            header('Content-Type: application/json');
            echo json_encode(['recebido' => $data]);
        }
    } else {
        http_response_code(405);
        echo json_encode(['erro' => 'Método não permitido']);
    }
    
  4. Escreva um código que retorne o status 404 com uma mensagem JSON de erro quando uma rota não for encontrada.

    ✓ Resposta:
    <?php
    
    $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    
    if ($uri !== '/produtos') {
        http_response_code(404);
        header('Content-Type: application/json');
        echo json_encode(['erro' => 'Rota não encontrada']);
    } else {
        // lógica para a rota /produtos
    }
    
  5. Crie um script que simule uma API de usuários com as rotas GET /usuarios (lista) e GET /usuarios/{id} (detalhe). Use um array fixo como base de dados.

    ✓ Resposta:
    <?php
    
    $usuarios = [
        1 => ['id' => 1, 'nome' => 'João'],
        2 => ['id' => 2, 'nome' => 'Maria']
    ];
    
    $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    
    if ($uri === '/usuarios' && $_SERVER['REQUEST_METHOD'] === 'GET') {
        header('Content-Type: application/json');
        echo json_encode(array_values($usuarios));
    } elseif (preg_match('#^/usuarios/(\d+)$#', $uri, $matches) && $_SERVER['REQUEST_METHOD'] === 'GET') {
        $id = $matches[1];
        if (isset($usuarios[$id])) {
            header('Content-Type: application/json');
            echo json_encode($usuarios[$id]);
        } else {
            http_response_code(404);
            echo json_encode(['erro' => 'Usuário não encontrado']);
        }
    } else {
        http_response_code(404);
        echo json_encode(['erro' => 'Rota não encontrada']);
    }
    

Boas Práticas e Observações Finais

Ao construir uma API REST em PHP, lembre-se de:

  • Usar sempre o cabeçalho Content-Type adequado para JSON.
  • Validar todos os dados de entrada para evitar erros e ataques.
  • Retornar códigos de status precisos para que o cliente possa agir de forma adequada.
  • Estruturar suas rotas de forma consistente e semântica.
  • Tratar erros de forma centralizada, se possível, para manter o código limpo.

A prática constante e a leitura da documentação oficial ajudarão você a evoluir. Continue explorando e construindo APIs cada vez mais robustas!