Makrosites

Suas ideias em realidade digital!

Versionamento de API em PHP: como criar /v1 e /v2 sem quebrar o sistema

Versionamento de API em PHP: como criar /v1 e /v2 sem quebrar o sistema >

Conforme uma API cresce, novas regras, campos, formatos de resposta e integrações começam a surgir. O problema é que alterar diretamente um endpoint que já está em uso pode quebrar sistemas antigos, aplicativos mobile, integrações externas e painéis administrativos que dependem do comportamento atual.

Para evitar esse problema, usamos versionamento de API. Em PHP puro, uma forma simples e eficiente de fazer isso é criar rotas como /api/v1 e /api/v2, permitindo que a versão antiga continue funcionando enquanto a nova evolui.

Neste artigo, você vai aprender como criar versionamento de API em PHP, organizar arquivos por versão, manter compatibilidade e evoluir endpoints sem quebrar o sistema.

Resumo prático: use versionamento quando precisar alterar contratos de resposta, regras de negócio, nomes de campos ou comportamento de endpoints que já estão em produção.

O que é versionamento de API?

Versionamento de API é a prática de separar diferentes versões dos endpoints para permitir mudanças sem afetar quem ainda usa a versão antiga.

Exemplo:

/api/v1/usuarios
/api/v2/usuarios

A rota /api/v1/usuarios pode continuar retornando o formato antigo, enquanto /api/v2/usuarios retorna uma estrutura nova, com novos campos, novas regras ou novo padrão de resposta.

Isso evita que você precise mudar tudo de uma vez e dá tempo para os consumidores da API migrarem com segurança.

Por que versionar uma API PHP?

Muitas APIs começam simples. Primeiro existe um endpoint de cadastro, depois login, listagem, atualização, upload, relatórios, filtros, permissões e integrações externas.

Em algum momento, pode surgir a necessidade de mudar algo que já está sendo usado. Sem versionamento, uma alteração pequena pode causar erro em vários lugares.

Alguns exemplos de mudanças que podem quebrar integrações:

O versionamento permite criar uma versão nova sem destruir a versão antiga.

Quando criar uma nova versão da API?

Nem toda alteração precisa de uma nova versão. Pequenas correções internas podem ser feitas na mesma versão. O versionamento é mais necessário quando a mudança altera o contrato da API.

O contrato da API é o conjunto de regras esperadas por quem consome o endpoint: URL, método HTTP, parâmetros, corpo da requisição, resposta JSON, status HTTP e regras principais.

Tipo de mudança Precisa nova versão? Exemplo
Adicionar campo opcional na resposta Nem sempre Adicionar telefone sem remover nada
Corrigir bug interno Não Ajustar cálculo sem mudar resposta
Remover campo da resposta Sim Remover nome_completo
Renomear campo Sim Trocar nome por name
Mudar formato de data Sim De 04/05/2026 para 2026-05-04
Mudar autenticação Sim De token simples para JWT Bearer

Principais formas de versionar uma API

Existem diferentes formas de versionar uma API. As mais comuns são por URL, por cabeçalho e por parâmetro.

Versionamento pela URL

/api/v1/usuarios
/api/v2/usuarios

É o modelo mais simples de entender, testar e documentar. Também é muito usado em APIs públicas e sistemas internos.

Versionamento por cabeçalho

Accept: application/vnd.suaapi.v1+json
Accept: application/vnd.suaapi.v2+json

Esse modelo deixa a URL mais limpa, mas pode ser menos intuitivo para quem está começando.

Versionamento por parâmetro

/api/usuarios?version=1
/api/usuarios?version=2

Funciona, mas geralmente é menos organizado que separar por URL.

Recomendação prática: para APIs PHP puro, principalmente em projetos próprios, o versionamento por URL costuma ser o mais simples e fácil de manter.

Estrutura de pastas para API com v1 e v2

Uma organização simples pode ser:

/api
  /core
    response.php
    database.php
    auth.php
  /v1
    /controllers
      UsuarioController.php
    routes.php
  /v2
    /controllers
      UsuarioController.php
    routes.php
  index.php

Nessa estrutura, arquivos comuns ficam em /core, enquanto os controladores e rotas específicas ficam separados por versão.

Assim, você consegue manter comportamentos diferentes para v1 e v2 sem misturar tudo no mesmo arquivo.

Exemplo de .htaccess para enviar tudo ao index.php

Em muitos projetos PHP puro, o Apache usa .htaccess para redirecionar as chamadas da API para um arquivo central.

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php [QSA,L]

Com isso, chamadas como /api/v1/usuarios e /api/v2/usuarios podem ser tratadas pelo seu roteador PHP.

Capturando a versão pela URL em PHP

No arquivo index.php, você pode capturar a URL acessada usando REQUEST_URI.

<?php

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = trim($uri, '/');

$partes = explode('/', $uri);

print_r($partes);

Para uma URL como:

/api/v1/usuarios

O array pode ficar assim:

[
  0 => "api",
  1 => "v1",
  2 => "usuarios"
]

Roteador simples com versão da API

Agora vamos criar um roteador simples identificando versão e recurso.

<?php

header('Content-Type: application/json; charset=utf-8');

function jsonResponse(int $statusCode, array $data): void
{
    http_response_code($statusCode);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = trim($uri, '/');
$partes = explode('/', $uri);

// Exemplo esperado: /api/v1/usuarios
$versao = $partes[1] ?? null;
$recurso = $partes[2] ?? null;

if (!in_array($versao, ['v1', 'v2'], true)) {
    jsonResponse(404, [
        'success' => false,
        'message' => 'Versão da API não encontrada.'
    ]);
}

if ($recurso === 'usuarios') {
    if ($versao === 'v1') {
        require __DIR__ . '/v1/routes.php';
        exit;
    }

    if ($versao === 'v2') {
        require __DIR__ . '/v2/routes.php';
        exit;
    }
}

jsonResponse(404, [
    'success' => false,
    'message' => 'Rota não encontrada.'
]);

Esse exemplo é simples, mas mostra a ideia principal: identificar a versão e carregar as rotas correspondentes.

Exemplo de resposta na versão v1

Imagine que na versão 1 a API retorna usuários assim:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "nome": "João Silva",
      "email": "joao@exemplo.com"
    }
  ]
}

Esse formato já está sendo usado por um frontend antigo. Se você mudar nome para name, esse frontend pode quebrar.

Exemplo de resposta na versão v2

Na versão 2, você pode criar uma resposta mais completa:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "João Silva",
      "email": "joao@exemplo.com",
      "status": "active",
      "created_at": "2026-05-04T10:30:00-03:00"
    }
  ],
  "meta": {
    "version": "v2"
  }
}

Assim, quem usa v1 continua funcionando, enquanto novos sistemas podem consumir v2.

Controller v1 de usuários

Um controlador simples para a versão 1 poderia ser:

<?php

class UsuarioControllerV1
{
    public function listar(): void
    {
        jsonResponse(200, [
            'success' => true,
            'data' => [
                [
                    'id' => 1,
                    'nome' => 'João Silva',
                    'email' => 'joao@exemplo.com'
                ]
            ]
        ]);
    }
}

Controller v2 de usuários

Já a versão 2 pode ter outro formato:

<?php

class UsuarioControllerV2
{
    public function listar(): void
    {
        jsonResponse(200, [
            'success' => true,
            'data' => [
                [
                    'id' => 1,
                    'name' => 'João Silva',
                    'email' => 'joao@exemplo.com',
                    'status' => 'active',
                    'created_at' => date('c')
                ]
            ],
            'meta' => [
                'version' => 'v2'
            ]
        ]);
    }
}

O banco pode ser o mesmo, mas a forma como a API transforma e entrega os dados pode mudar entre versões.

Arquivo de rotas da v1

Exemplo de /api/v1/routes.php:

<?php

require_once __DIR__ . '/controllers/UsuarioController.php';

$method = $_SERVER['REQUEST_METHOD'];

$controller = new UsuarioControllerV1();

if ($recurso === 'usuarios' && $method === 'GET') {
    $controller->listar();
}

jsonResponse(404, [
    'success' => false,
    'message' => 'Rota não encontrada na v1.'
]);

Arquivo de rotas da v2

Exemplo de /api/v2/routes.php:

<?php

require_once __DIR__ . '/controllers/UsuarioController.php';

$method = $_SERVER['REQUEST_METHOD'];

$controller = new UsuarioControllerV2();

if ($recurso === 'usuarios' && $method === 'GET') {
    $controller->listar();
}

jsonResponse(404, [
    'success' => false,
    'message' => 'Rota não encontrada na v2.'
]);

Reaproveitando lógica comum entre versões

Nem tudo precisa ser duplicado entre v1 e v2. O ideal é reaproveitar serviços, conexão com banco, autenticação e validações comuns.

Exemplo de estrutura:

/api
  /core
    database.php
    response.php
  /services
    UsuarioService.php
  /v1
    /controllers
      UsuarioController.php
  /v2
    /controllers
      UsuarioController.php

O UsuarioService pode buscar os dados no banco, enquanto cada controller decide como transformar a resposta para sua versão.

Exemplo de service compartilhado

<?php

class UsuarioService
{
    public function listar(): array
    {
        return [
            [
                'id' => 1,
                'nome' => 'João Silva',
                'email' => 'joao@exemplo.com',
                'status' => 'ativo',
                'created_at' => '2026-05-04 10:30:00'
            ]
        ];
    }
}

A v1 pode retornar nome e a v2 pode transformar para name, usando a mesma fonte de dados.

Transformando dados para cada versão

Uma boa prática é criar métodos específicos para transformar a saída.

<?php

private function transformarUsuarioV1(array $usuario): array
{
    return [
        'id' => $usuario['id'],
        'nome' => $usuario['nome'],
        'email' => $usuario['email']
    ];
}

private function transformarUsuarioV2(array $usuario): array
{
    return [
        'id' => $usuario['id'],
        'name' => $usuario['nome'],
        'email' => $usuario['email'],
        'status' => $usuario['status'] === 'ativo' ? 'active' : 'inactive',
        'created_at' => date('c', strtotime($usuario['created_at']))
    ];
}

Esse tipo de transformação evita que a estrutura do banco dite diretamente o contrato público da API.

Documentando versões no Swagger/OpenAPI

Se sua API usa Swagger ou OpenAPI, cada versão deve estar bem documentada. Você pode separar documentos por versão:

/api/docs/v1
/api/docs/v2

Ou indicar a versão no próprio arquivo OpenAPI:

info:
  title: Minha API
  version: 2.0.0

Isso ajuda outros desenvolvedores a entenderem quais endpoints pertencem a cada versão e quais mudanças foram feitas.

Como avisar que uma versão será descontinuada

Quando uma versão antiga for sair de uso, você pode avisar os consumidores com antecedência.

Uma forma simples é adicionar informações na resposta:

{
  "success": true,
  "data": [],
  "meta": {
    "version": "v1",
    "deprecated": true,
    "message": "A versão v1 será descontinuada. Migre para /api/v2."
  }
}

Também é possível usar headers:

Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT

O mais importante é não desligar uma versão antiga sem aviso, principalmente se houver integrações externas usando a API.

Cuidados ao criar a versão v2

Ao criar uma nova versão, não copie tudo sem necessidade. Primeiro identifique exatamente o que mudou.

Se a mudança for grande o suficiente para quebrar consumidores antigos, crie uma nova versão.

Erros comuns no versionamento de API

1. Alterar a v1 diretamente

Se a v1 já está em produção, evite alterar o contrato dela. Crie a v2 para mudanças incompatíveis.

2. Duplicar regra de negócio desnecessariamente

Versionar não significa duplicar tudo. Reaproveite serviços e deixe diferente apenas a camada de entrada ou saída.

3. Não documentar diferenças entre versões

Se a v2 muda campos ou formatos, isso precisa estar claro na documentação.

4. Descontinuar versão sem aviso

Quem consome a API precisa de tempo para migrar. Avise com antecedência.

5. Criar nova versão para qualquer pequena mudança

Nem toda alteração exige v2. Adicionar um campo opcional, por exemplo, normalmente pode ser feito na mesma versão.

Checklist para versionamento de API PHP

Exemplo de resposta comparando v1 e v2

Item v1 v2
Campo de nome nome name
Status ativo active
Data 2026-05-04 10:30:00 2026-05-04T10:30:00-03:00
Metadados Não possui meta.version

Conclusão

Versionar uma API PHP é uma prática importante para evoluir o sistema sem quebrar integrações existentes. Quando você separa rotas como /api/v1 e /api/v2, consegue manter a compatibilidade com aplicações antigas enquanto cria melhorias para novos consumidores.

O ideal é usar versionamento quando houver mudanças incompatíveis no contrato da API, como alteração de nomes de campos, remoção de dados, mudança no formato de resposta, autenticação ou regras importantes.

Mesmo em PHP puro, é possível criar uma estrutura organizada, com rotas por versão, controllers separados e serviços compartilhados. Dessa forma, sua API fica mais profissional, escalável e segura para crescer.

Perguntas frequentes sobre versionamento de API PHP

O que é versionamento de API?

É a prática de separar versões diferentes da API, como /api/v1 e /api/v2, para evoluir endpoints sem quebrar sistemas que usam a versão antiga.

Quando devo criar uma nova versão da API?

Quando a mudança altera o contrato da API e pode quebrar quem já consome o endpoint, como renomear campos, remover dados ou mudar o formato da resposta.

Qual o melhor jeito de versionar uma API PHP?

Para projetos PHP puro, o versionamento pela URL, como /api/v1/usuarios, costuma ser o mais simples de implementar, testar e documentar.

Preciso duplicar todo o código para criar a v2?

Não. O ideal é reaproveitar serviços, conexão com banco, autenticação e validações. A diferença deve ficar principalmente nos controllers, rotas e transformação das respostas.

Posso manter v1 e v2 usando o mesmo banco de dados?

Sim. As versões podem usar o mesmo banco. O que muda é a forma como os dados são recebidos, tratados e retornados pela API.

Como avisar que uma versão antiga será descontinuada?

Você pode documentar a descontinuação, adicionar informações no campo meta da resposta ou usar headers como Deprecation e Sunset.


Perguntas frequentes sobre versionamento de API PHP

O que é versionamento de API?

É a prática de separar versões diferentes da API, como /api/v1 e /api/v2, para evoluir endpoints sem quebrar sistemas que usam a versão antiga.

Quando devo criar uma nova versão da API?

Quando a mudança altera o contrato da API e pode quebrar quem já consome o endpoint, como renomear campos, remover dados ou mudar o formato da resposta.

Qual o melhor jeito de versionar uma API PHP?

Para projetos PHP puro, o versionamento pela URL, como /api/v1/usuarios, costuma ser o mais simples de implementar, testar e documentar.

Preciso duplicar todo o código para criar a v2?

Não. O ideal é reaproveitar serviços, conexão com banco, autenticação e validações. A diferença deve ficar principalmente nos controllers, rotas e transformação das respostas.

Posso manter v1 e v2 usando o mesmo banco de dados?

Sim. As versões podem usar o mesmo banco. O que muda é a forma como os dados são recebidos, tratados e retornados pela API.

Como avisar que uma versão antiga será descontinuada?

Você pode documentar a descontinuação, adicionar informações no campo meta da resposta ou usar headers como Deprecation e Sunset.