Construindo uma API REST
Nesta aula, você aprenderá a construir uma API REST em PHP do zero, cobrindo os fundamentos essenciais: verbos HTTP, definição de rotas, manipulação de JSON e uso correto dos códigos de status. Ao final, você terá um conhecimento sólido para criar endpoints RESTful profissionais e bem estruturados.
Construir uma API REST é uma habilidade fundamental para qualquer desenvolvedor PHP moderno. Nesta aula, vamos mergulhar nos pilares do REST e aplicá-los na prática, criando uma API simples, mas completa, sem depender de frameworks. Você aprenderá a lidar com os verbos HTTP, a estruturar rotas, a trabalhar com JSON e a responder com os códigos de status adequados, garantindo que sua API seja robusta, previsível e fácil de consumir.
Embora existam frameworks como Laravel e Slim que facilitam esse processo, entender o funcionamento por baixo dos panos é crucial. Isso permite que você tenha controle total sobre o comportamento da sua aplicação e cria uma base sólida para aprender qualquer framework. Vamos começar!
Verbos HTTP
Os verbos HTTP (ou métodos) são a forma de indicar a intenção da requisição feita ao servidor. Em uma API REST, cada verbo está associado a uma operação específica: GET para consultar, POST para criar, PUT/PATCH para atualizar, DELETE para remover. Essa padronização permite que a API seja intuitiva e que clientes (como aplicações web ou mobile) saibam exatamente o que esperar de cada endpoint.
Na prática, o PHP identifica o verbo utilizado através da variável superglobal $_SERVER['REQUEST_METHOD']. É a partir dela que decidimos qual ação executar. Vamos criar um exemplo simples que responde de forma diferente para cada verbo:
<?php
// api.php
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
case 'GET':
echo "Você fez uma requisição GET";
break;
case 'POST':
echo "Você fez uma requisição POST";
break;
case 'PUT':
echo "Você fez uma requisição PUT";
break;
case 'DELETE':
echo "Você fez uma requisição DELETE";
break;
default:
http_response_code(405);
echo "Método não permitido";
}Observe que para métodos não suportados, retornamos o código 405 (Method Not Allowed). Isso é uma boa prática: a API deve comunicar claramente quando uma operação não é suportada.
Rotas
Rotas são a forma de mapear URLs para ações específicas. Em uma API REST, cada rota geralmente corresponde a um recurso (como 'usuarios', 'produtos') e o verbo HTTP define a operação. Como não usaremos um framework, precisamos interpretar a URL manualmente.
A variável $_SERVER['REQUEST_URI'] contém o caminho solicitado. Podemos extrair o caminho e dividi-lo em segmentos. Vamos criar um roteador simples que responde a 'usuarios' e 'produtos':
<?php
// router.php
$uri = $_SERVER['REQUEST_URI'];
$path = parse_url($uri, PHP_URL_PATH);
$segments = explode('/', trim($path, '/'));
$resource = $segments[0] ?? null;
$id = $segments[1] ?? null;
switch ($resource) {
case 'usuarios':
if ($id) {
// rota /usuarios/{id}
echo "Usuário com ID $id";
} else {
// rota /usuarios
echo "Lista de usuários";
}
break;
case 'produtos':
echo "Produtos";
break;
default:
http_response_code(404);
echo "Rota não encontrada";
}Esse roteador é básico, mas suficiente para entender o conceito. Em uma aplicação real, você provavelmente usaria um arquivo de rotas mais sofisticado, mas o princípio é o mesmo: analisar a URL e direcionar para o handler correto.
JSON
JSON (JavaScript Object Notation) é o formato padrão para troca de dados em APIs REST. Ele é leve, legível e suportado nativamente pelo PHP. Para enviar uma resposta JSON, usamos a função json_encode() e definimos o cabeçalho Content-Type: application/json. Para receber dados JSON (em POST, PUT, etc.), usamos json_decode() no corpo da requisição.
Vamos criar um exemplo de API que retorna um usuário em JSON e também recebe dados JSON para criar um novo usuário:
<?php
// json_example.php
// Definir cabeçalho para JSON
header('Content-Type: application/json; charset=utf-8');
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
// Retorna um usuário fictício
$usuario = ['id' => 1, 'nome' => 'Maria', 'email' => 'maria@exemplo.com'];
echo json_encode($usuario);
} elseif ($method === 'POST') {
// Recebe dados JSON do corpo da requisição
$dados = json_decode(file_get_contents('php://input'), true);
if ($dados === null) {
http_response_code(400);
echo json_encode(['erro' => 'JSON inválido']);
} else {
// Aqui você salvaria no banco de dados
echo json_encode(['status' => 'sucesso', 'recebido' => $dados]);
}
}Note que usamos file_get_contents('php://input') para ler o corpo da requisição, que contém os dados JSON. A função json_decode com o segundo argumento true converte o JSON em array associativo.
Status codes
Os códigos de status HTTP são essenciais para que o cliente saiba o resultado da requisição. Eles são divididos em categorias: 2xx para sucesso, 3xx para redirecionamento, 4xx para erros do cliente e 5xx para erros do servidor. Em uma API REST, é crucial usar os códigos corretamente para que o cliente possa tratar erros de forma adequada.
No PHP, usamos http_response_code() para definir o código. Vamos ver alguns exemplos:
- 200 OK - Requisição bem-sucedida (GET, PUT, DELETE).
- 201 Created - Recurso criado com sucesso (POST).
- 204 No Content - Sucesso, mas sem corpo na resposta (DELETE).
- 400 Bad Request - Requisição inválida (dados mal formatados).
- 401 Unauthorized - Autenticação necessária.
- 403 Forbidden - Sem permissão para acessar.
- 404 Not Found - Recurso não encontrado.
- 405 Method Not Allowed - Método não suportado para a rota.
- 422 Unprocessable Entity - Dados válidos, mas falharam na validação.
- 500 Internal Server Error - Erro no servidor.
Abaixo, um exemplo de API que utiliza status codes de forma adequada:
<?php
// status_codes.php
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
// Simula um banco de dados
$usuarios = [
1 => ['id' => 1, 'nome' => 'João'],
2 => ['id' => 2, 'nome' => 'Ana']
];
if ($method === 'GET') {
$id = $_GET['id'] ?? null;
if ($id && isset($usuarios[$id])) {
http_response_code(200);
echo json_encode($usuarios[$id]);
} else {
http_response_code(404);
echo json_encode(['erro' => 'Usuário não encontrado']);
}
} elseif ($method === 'POST') {
$dados = json_decode(file_get_contents('php://input'), true);
if (!$dados || !isset($dados['nome'])) {
http_response_code(422);
echo json_encode(['erro' => 'Campo nome é obrigatório']);
} else {
// Cria novo usuário (simulado)
http_response_code(201);
echo json_encode(['id' => 3, 'nome' => $dados['nome']]);
}
} else {
http_response_code(405);
echo json_encode(['erro' => 'Método não permitido']);
}Perceba que cada situação retorna um código adequado, facilitando o tratamento de erros no cliente.
Boas práticas
Ao construir uma API REST, siga estas boas práticas: use substantivos no plural para recursos (ex.: /usuarios), mantenha a consistência nas rotas, sempre valide os dados de entrada, retorne mensagens de erro claras, e utilize HTTPS em produção. Além disso, considere versionar sua API (ex.: /v1/usuarios) para evitar quebrar clientes existentes.
Outra prática importante é documentar sua API, seja com OpenAPI/Swagger ou manualmente. Isso facilita o consumo por outros desenvolvedores e melhora a manutenção.
Referências
- Métodos de requisição HTTP - MDN
- Códigos de status HTTP - MDN
- json_encode - Documentação PHP
- json_decode - Documentação PHP
- $_SERVER - Manual PHP
- REST API Tutorial
Exercícios
- Crie um script PHP que responda com o método HTTP usado (GET, POST, etc.) e, se for POST, leia o corpo JSON e retorne os dados recebidos em formato JSON.✓ Resposta:
<?php header('Content-Type: application/json'); $method = $_SERVER['REQUEST_METHOD']; if ($method === 'POST') { $dados = json_decode(file_get_contents('php://input'), true); echo json_encode(['metodo' => $method, 'dados' => $dados]); } else { echo json_encode(['metodo' => $method]); } - Implemente um roteador simples que responda a /produtos e /produtos/{id}, onde {id} é um número. Se a rota não existir, retorne 404.✓ Resposta:
<?php $uri = $_SERVER['REQUEST_URI']; $path = parse_url($uri, PHP_URL_PATH); $segments = explode('/', trim($path, '/')); if ($segments[0] === 'produtos') { if (isset($segments[1]) && is_numeric($segments[1])) { echo "Produto ID: {$segments[1]}"; } else { echo "Lista de produtos"; } } else { http_response_code(404); echo "Rota não encontrada"; } - Escreva uma função que receba dados de um formulário (simulados como array) e retorne em JSON com status 201. Se os dados estiverem vazios, retorne 400.✓ Resposta:
<?php function criarUsuario($dados) { if (empty($dados)) { http_response_code(400); echo json_encode(['erro' => 'Dados não fornecidos']); } else { http_response_code(201); echo json_encode($dados); } } // Exemplo de uso: criarUsuario(['nome' => 'Carlos']); - Crie um endpoint que aceite GET e DELETE para /usuario/{id}. Para GET, retorne os dados do usuário (pode ser fixo); para DELETE, retorne 204 No Content.✓ Resposta:
<?php $method = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; $path = parse_url($uri, PHP_URL_PATH); $segments = explode('/', trim($path, '/')); if ($segments[0] === 'usuario' && isset($segments[1])) { if ($method === 'GET') { http_response_code(200); echo json_encode(['id' => $segments[1], 'nome' => 'Fulano']); } elseif ($method === 'DELETE') { http_response_code(204); } else { http_response_code(405); } } else { http_response_code(404); } - Implemente um endpoint que receba um JSON com campos 'nome' e 'email' via POST. Se o e-mail não contiver '@', retorne 422. Caso contrário, retorne 201 e os dados.✓ Resposta:
<?php header('Content-Type: application/json'); $dados = json_decode(file_get_contents('php://input'), true); if (!isset($dados['nome'], $dados['email'])) { http_response_code(422); echo json_encode(['erro' => 'Campos nome e email são obrigatórios']); } elseif (strpos($dados['email'], '@') === false) { http_response_code(422); echo json_encode(['erro' => 'Email inválido']); } else { http_response_code(201); echo json_encode($dados); }