Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REST API versioning #102

Closed
otaciliolacerda opened this issue Jul 26, 2020 · 2 comments
Closed

REST API versioning #102

otaciliolacerda opened this issue Jul 26, 2020 · 2 comments
Labels
question Further information is requested

Comments

@otaciliolacerda
Copy link
Collaborator

otaciliolacerda commented Jul 26, 2020

Olá. Acabei de conhecer o projeto e vi que já houve uma discussão sobre versionamento #12 que levou ao PR #25. Apesar de não existir uma forma correta de se fazer versionamento[1], gostaria de propor um modelo diferente que acho que seria mais vantajoso para esse projeto.

Contexto

Levando em conta que esse projeto criará uma API pública que potencialmente será utilizada por diversos clientes, acredito que é de extrema importância manter o processo de desenvolvimento o mais eficiente possível. Em minha experiência, resource versioning ou global uri versioning adicionam algumas complexidades no desenvolvimento da API que acabam desacelerando a sua evolução:

  • Manutenção: breaking changes normalmente levam a criação de outro endpoint, o que leva a manutenção de duas ou mais implementações distintas (no caso de múltiplas versões) além do planejamento paralelo para descontinuar cada versão. Imaginem como será para resolver bugs que afetam múltiplas versões por exemplo.
  • Obriga atualização nos clientes: Esse modelo de versionamento normalmente adiciona mais trabalho para os clientes se manterem atualizados.
  • Pessoalmente eu também acho que URI versioning tira um pouco o charme de uma REST(ful) API já que não considero a versão um recurso ou sub-recurso. Lembrando que o pilar de uma REST API são os recursos e as operações que posso fazer nos mesmos.

Proposta

Manter a URI consistente (sem versão) e utilizar content negotiation (utilizando Accepts e Content-Type headers) para negociar qual conteúdo o cliente precisa. Em outras palavras, negociar a versão. Na prática a API evoluiria mantendo compatibilidade com as versões anteriores e os endpoints seriam os mesmos. Essa abordagem é utilizada no ambiente empresarial[2 e 3].

Mas enfim, como seria isso na prática?

Antes:

GET /api/v1/cep/[cep]

Depois:

# O serviço é uma API (e nada mais). Não vejo necessidade de adicionar /api na URI, mas não tenho nada contra
/cep/:id

Guidelines

Considerando o Pull Request #101 e Issue #14 , teríamos os seguintes guidelines:

Usar semantic versioning v2

OpenAPI permite adicionar a versão em #/info/version (recomendo não usar as regras 9 e 10). Exemplo:

openapi: 3.0.1
info:
    title: CEP API
    description: API for <...>
    version: 1.3.7
    <...>

Apesar de não ter um impacto direto no funcionamento da API, esses metadados dão visibilidade e informações sobre a API para os clientes.

Usar media type versioning

Versionamento baseado em negociação de conteúdo reduz o acoplamento e facilita a evolução da API. A versão (ou conteúdo) são negociados usando o cabeçalho Content-Type e Accept do protocolo HTTP. Por exemplo: application/brasil.cep+json;version=2. Para manter compatibilidade, cada breaking change (major release) cria um novo tipo (media type). Assim, os recursos (URI) e a semântica das operações são mantidas enquanto o versionamento é limitado ao tipo de conteúdo.

Teríamos as seguintes regras:

  • Usar media types personalizados para versões diferentes. No jargão de semantic release seria criar um novo media type quando tivermos um major release. Enquanto não tivermos nenhuma mudança de versão é melhor se manter utilizando o padrão: application/json
  • Incluir versões no cabeçalho do request e response para dar visibilidade
  • Incluir Content-Type no cabeçalho Vary para permitir cache de versões diferentes em qualquer proxy que esteja no caminho:
    Vary: Content-Type
    

Formato / esquema:

application/<custom-media-type>+json;version=<version>
  • custom-media-type é o recurso. Exemplo: brasil.cep
  • version é a versão. Exemplo: 2

Exemplo

Suponha que o cliente queira apenas a nova versão:

Accept: application/brasil.ce+json;version=2

Um servidor deve responder, assim como um cliente enviando uma requisição com conteúdo (POST por exemplo), usando o cabeçalho Content-Type do protocolo HTTP para declarar explicitamente qual versão do conteúdo esta sendo enviada:

Content-Type: application/brasil.cep+json;version=2

Próximos passos

Definir regras/guidelines para os demais pontos não comentados: metadados, segurança, compatibilidade, desativação de endpoints (como se traduz deprecation para pt-br?), performance, semântica das operações, cabeçalhos utilizados, etc.

Referências (infelizmente não conheço muito material em português)

  1. API Versioning Has No "Right Way"
  2. Enterprise Integration Using REST
  3. Zalando RESTful API Guidelines
  4. Evolving HTTP APIS
@otaciliolacerda otaciliolacerda changed the title API versioning REST API versioning Jul 26, 2020
@RodriAndreotti
Copy link
Collaborator

Achei a proposta bastante interessante.

@rafaelbmateus
Copy link

Foi a primeira coisa que notei quando acessei o projeto, o versionamento na uri.

Estou de acordo com @otaciliolacerda, que colocou muito bem a proposta. Penso que a parte de remover o /api da URI já é até uma outra questão (na qual refleti, e parece fazer sentido tbm), que pode ser discutido em outra issue.

@lucianopf lucianopf added the question Further information is requested label Dec 1, 2020
@Caaddss Caaddss closed this as completed Feb 8, 2021
@BrasilAPI BrasilAPI locked and limited conversation to collaborators Feb 8, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants