As APIs RESTful transformaram-se no padrão de comunicação entre aplicações modernas, oferecendo interoperabilidade e flexibilidade across diferentes plataformas. Este tutorial apresenta a implementação completa de uma API RESTful utilizando PHP e MySQL, focando nas operações CRUD (Create, Read, Update, Delete) e seguindo as melhores práticas de desenvolvimento.

Fundamentos das APIs RESTful

REST (Representational State Transfer) utiliza os métodos HTTP padrão para realizar operações em recursos. Cada recurso possui uma URL única e as operações são definidas pelos verbos HTTP:

  • GET: Recuperar dados
  • POST: Criar novos recursos
  • PUT: Atualizar recursos existentes
  • DELETE: Remover recursos

Esta arquitetura oferece simplicidade comparada a alternativas como SOAP, resultando em implementações mais leves e eficientes. Segundo pesquisas da Web.dev, APIs REST podem ser até 40% mais rápidas em cenários típicos de aplicações web.

Estrutura do Projeto

Organizaremos nossa API em múltiplos arquivos para manter a separação de responsabilidades e facilitar a manutenção:

ArquivoResponsabilidadeFunção Principal
config/db.phpConexão com bancoGerenciar conexões PDO com MySQL
api/index.phpRoteamentoProcessar requisições HTTP e rotear
models/Item.phpLógica de negócioOperações CRUD e validações
utils/Response.phpRespostas padronizadasFormatar retornos JSON

Configuração do Ambiente

Configure seu ambiente de desenvolvimento com Apache/Nginx, PHP 7.4+ e MySQL 8.0+. Para desenvolvimento local, utilize XAMPP, WAMP ou configure um servidor VPS para testes mais realistas. Certifique-se de habilitar as extensões PDO e pdo_mysql no PHP.

Criação da Base de Dados

Execute este script SQL para criar a estrutura inicial:

CREATE DATABASE api_restful;
USE api_restful;

CREATE TABLE items (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description TEXT,
  price DECIMAL(10,2),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Configuração da Conexão MySQL

Crie o arquivo config/db.php para gerenciar conexões de forma segura:

conn = null;
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name,
                $this->username,
                $this->password
            );
            $this->conn->exec("set names utf8");
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $exception) {
            error_log("Connection error: " . $exception->getMessage());
        }
        return $this->conn;
    }
}

Implementação do Modelo de Dados

Desenvolva a classe models/Item.php contendo toda a lógica de acesso aos dados:

conn = $db;
    }
    
    // READ - Buscar todos os itens
    public function read() {
        $query = "SELECT id, name, description, price, created_at FROM " . $this->table_name . " ORDER BY created_at DESC";
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
        return $stmt;
    }
    
    // CREATE - Criar novo item
    public function create() {
        $query = "INSERT INTO " . $this->table_name . " SET name=:name, description=:description, price=:price";
        $stmt = $this->conn->prepare($query);
        
        // Sanitização dos dados
        $this->name = htmlspecialchars(strip_tags($this->name));
        $this->description = htmlspecialchars(strip_tags($this->description));
        $this->price = htmlspecialchars(strip_tags($this->price));
        
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":price", $this->price);
        
        return $stmt->execute();
    }
    
    // READ SINGLE - Buscar item específico
    public function readOne() {
        $query = "SELECT * FROM " . $this->table_name . " WHERE id = :id LIMIT 0,1";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(\':id\', $this->id);
        $stmt->execute();
        
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if($row) {
            $this->name = $row[\'name\'];
            $this->description = $row[\'description\'];
            $this->price = $row[\'price\'];
            return true;
        }
        return false;
    }
    
    // UPDATE - Atualizar item
    public function update() {
        $query = "UPDATE " . $this->table_name . " SET name=:name, description=:description, price=:price WHERE id=:id";
        $stmt = $this->conn->prepare($query);
        
        $this->name = htmlspecialchars(strip_tags($this->name));
        $this->description = htmlspecialchars(strip_tags($this->description));
        $this->price = htmlspecialchars(strip_tags($this->price));
        $this->id = htmlspecialchars(strip_tags($this->id));
        
        $stmt->bindParam(\':name\', $this->name);
        $stmt->bindParam(\':description\', $this->description);
        $stmt->bindParam(\':price\', $this->price);
        $stmt->bindParam(\':id\', $this->id);
        
        return $stmt->execute();
    }
    
    // DELETE - Deletar item
    public function delete() {
        $query = "DELETE FROM " . $this->table_name . " WHERE id = :id";
        $stmt = $this->conn->prepare($query);
        
        $this->id = htmlspecialchars(strip_tags($this->id));
        $stmt->bindParam(\':id\', $this->id);
        
        return $stmt->execute();
    }
}

Controlador Principal da API

O arquivo api/index.php gerencia todas as requisições HTTP e implementa o roteamento:

getConnection();
$item = new Item($db);

$request_method = $_SERVER[\'REQUEST_METHOD\'];
$path_info = isset($_SERVER[\'PATH_INFO\']) ? $_SERVER[\'PATH_INFO\'] : \'/\';

switch($request_method) {
    case \'GET\':
        if ($path_info == \'/\') {
            // GET /api/ - Listar todos os itens
            $stmt = $item->read();
            $num = $stmt->rowCount();
            
            if($num > 0) {
                $items_arr = array();
                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                    extract($row);
                    $item_data = array(
                        "id" => $id,
                        "name" => $name,
                        "description" => $description,
                        "price" => $price,
                        "created_at" => $created_at
                    );
                    array_push($items_arr, $item_data);
                }
                http_response_code(200);
                echo json_encode(array("data" => $items_arr));
            } else {
                http_response_code(404);
                echo json_encode(array("message" => "Nenhum item encontrado."));
            }
        } else {
            // GET /api/{id} - Buscar item específico
            $id = basename($path_info);
            $item->id = $id;
            
            if($item->readOne()) {
                $item_data = array(
                    "id" => $item->id,
                    "name" => $item->name,
                    "description" => $item->description,
                    "price" => $item->price
                );
                http_response_code(200);
                echo json_encode($item_data);
            } else {
                http_response_code(404);
                echo json_encode(array("message" => "Item não encontrado."));
            }
        }
        break;
        
    case \'POST\':
        // POST /api/ - Criar novo item
        $data = json_decode(file_get_contents("php://input"));
        
        if(!empty($data->name) && !empty($data->price)) {
            $item->name = $data->name;
            $item->description = $data->description ?? \'\';
            $item->price = $data->price;
            
            if($item->create()) {
                http_response_code(201);
                echo json_encode(array("message" => "Item criado com sucesso."));
            } else {
                http_response_code(503);
                echo json_encode(array("message" => "Não foi possível criar o item."));
            }
        } else {
            http_response_code(400);
            echo json_encode(array("message" => "Dados incompletos. Nome e preço são obrigatórios."));
        }
        break;
        
    case \'PUT\':
        // PUT /api/{id} - Atualizar item
        $id = basename($path_info);
        $data = json_decode(file_get_contents("php://input"));
        
        $item->id = $id;
        $item->name = $data->name;
        $item->description = $data->description;
        $item->price = $data->price;
        
        if($item->update()) {
            http_response_code(200);
            echo json_encode(array("message" => "Item atualizado com sucesso."));
        } else {
            http_response_code(503);
            echo json_encode(array("message" => "Não foi possível atualizar o item."));
        }
        break;
        
    case \'DELETE\':
        // DELETE /api/{id} - Deletar item
        $id = basename($path_info);
        $item->id = $id;
        
        if($item->delete()) {
            http_response_code(200);
            echo json_encode(array("message" => "Item deletado com sucesso."));
        } else {
            http_response_code(503);
            echo json_encode(array("message" => "Não foi possível deletar o item."));
        }
        break;
        
    default:
        http_response_code(405);
        echo json_encode(array("message" => "Método não permitido."));
        break;
}

Testando a API

Utilize ferramentas como Postman ou curl para testar os endpoints. Exemplos de requisições:

# Listar todos os itens
curl -X GET http://localhost/api/

# Criar novo item
curl -X POST http://localhost/api/ \\
-H "Content-Type: application/json" \\
-d \'{"name":"Produto Teste","description":"Descrição do produto","price":29.99}\'

# Buscar item específico
curl -X GET http://localhost/api/1

# Atualizar item
curl -X PUT http://localhost/api/1 \\
-H "Content-Type: application/json" \\
-d \'{"name":"Produto Atualizado","description":"Nova descrição","price":39.99}\'

# Deletar item
curl -X DELETE http://localhost/api/1

Melhores Práticas e Otimizações

Para APIs em produção, implemente autenticação JWT, validação robusta de dados, rate limiting e logs detalhados. Considere utilizar um hosting otimizado para aplicações PHP que suporte alta disponibilidade.

Validação e Sanitização

Sempre valide e sanitize os dados de entrada para prevenir SQL injection e XSS. Implemente validações específicas para cada campo conforme as regras de negócio.

Tratamento de Erros

Configure logs apropriados e retorne códigos HTTP consistentes. Utilize try-catch blocks para capturar exceções e fornecer feedback adequado aos clientes da API.

Esta implementação fornece uma base sólida para APIs RESTful com PHP e MySQL. A estrutura modular facilita expansões futuras, permitindo adicionar autenticação, cache, documentação automática e outros recursos conforme necessário.