Documentação de APIs em .NET: OpenAPI, Swagger e Scalar na Prática

Entender o OpenAPI no .NET é o primeiro passo para documentar APIs de forma profissional, porque muita gente ainda associa documentação diretamente a ferramentas como Swagger ou Scalar. Essa associação parece inofensiva, mas leva a um erro conceitual importante: confundir interface com documentação.

Neste artigo, você vai entender o que é documentação de API de verdade, qual é o papel do OpenAPI e onde entram o Swagger UI e o Scalar. Além disso, vai ver qual é o padrão recomendado atualmente pela Microsoft e como estruturar uma documentação versionada usando Minimal API.

A ideia aqui não é apenas configurar uma tela bonita. Na prática, o objetivo é entender a arquitetura da documentação e aplicá-la da forma correta.

Por que interface não é documentação de API

Swagger UI e Scalar são ferramentas extremamente úteis no dia a dia. Ainda assim, é importante deixar algo claro desde o início: eles não são a documentação da API.

O que essas ferramentas fazem é exibir informações que já existem em outro lugar, como:

  • Rotas disponíveis
  • Métodos HTTP de cada rota
  • Tipos de dados de entrada e saída
  • Exemplos de requisição e resposta

Porém, tudo isso existe antes da interface. A documentação real é um arquivo estruturado, legível por máquina, que descreve completamente o contrato da API.

Esse arquivo reúne os endpoints, os verbos HTTP, os schemas, as entradas e saídas, as versões e os servidores disponíveis. Em outras palavras, ele é o contrato, enquanto a tela é apenas uma forma de lê-lo.

Esse arquivo segue um padrão chamado OpenAPI. Por isso, vale guardar duas ideias simples: a interface consome a documentação, e a documentação não depende da interface.

O que é OpenAPI no .NET e fora dele

OpenAPI é um padrão, e não uma ferramenta, uma UI ou algo exclusivo do .NET. A especificação OpenAPI é mantida pela OpenAPI Initiative e descreve APIs HTTP de forma independente de linguagem.

Ela define como a documentação deve ser escrita, como rotas, métodos e schemas são descritos e como as ferramentas interpretam esse contrato. Assim, qualquer ecossistema consegue ler o mesmo documento.

Algumas características importantes do padrão:

  • É um arquivo de texto
  • Pode ser escrito em JSON ou YAML
  • É independente de linguagem
  • Pode existir sozinho, sem qualquer interface gráfica

Na prática, você pode abrir um documento OpenAPI em um editor de texto e entender como consumir aquela API sem nunca rodar o projeto. Essa independência é justamente o que torna o OpenAPI tão poderoso.

Também vale lembrar a origem do nome Swagger. O projeto Swagger foi doado à OpenAPI Initiative em 2015, e desde então "OpenAPI" se refere à especificação, enquanto "Swagger" se refere à família de ferramentas que trabalham com ela.

Para que serve um documento OpenAPI

Uma documentação OpenAPI bem estruturada vai muito além de uma página de consulta. Ela permite, por exemplo:

  • Entender como consumir a API sem acessar o código
  • Gerar clientes automaticamente em C#, JavaScript, Java e outras linguagens
  • Validar contratos entre sistemas
  • Versionar APIs com clareza
  • Alimentar ferramentas de teste e mock
  • Ser consumida por diferentes interfaces ao mesmo tempo

A geração de clientes é um bom exemplo desse valor. Ferramentas como o Kiota leem o documento e criam um SDK tipado, como mostramos no artigo sobre como usar o Kiota para gerar SDKs de API no .NET.

Por isso, a documentação deve fazer parte do contrato da API, e não ficar como um recurso opcional que alguém configura no fim do projeto.

No ecossistema Microsoft, esse raciocínio vale igualmente: o OpenAPI no .NET é o contrato, e todo o resto deriva dele.

Onde entram Swagger e Scalar

Swagger UI e Scalar não criam o padrão OpenAPI. Na verdade, eles consomem um documento OpenAPI existente e o transformam em uma interface web.

Tecnicamente, ambos são renderizadores de documentação OpenAPI escritos em HTML, CSS e JavaScript. Ou seja, recebem um JSON e devolvem uma página navegável.

Swagger UI

O Swagger UI é a interface mais conhecida do mercado. Ele é amplamente adotado, simples de configurar e familiar para a maioria dos times e empresas.

Por outro lado, sua interface segue um estilo mais tradicional. No .NET, o pacote Swashbuckle.AspNetCore.SwaggerUI entrega os arquivos estáticos dessa interface prontos para uso.

Scalar

O Scalar é uma alternativa de código aberto mais recente. Ele oferece uma UI mais limpa e organizada, além de exportação de código integrada para várias linguagens.

Com isso, a experiência de quem consome a API costuma melhorar bastante. No .NET, a integração acontece pelo pacote Scalar.AspNetCore.

Um ponto importante: não existe uma interface gráfica oficial de OpenAPI no .NET. Swagger UI e Scalar são bibliotecas independentes, mantidas pelas suas próprias comunidades.

O problema do modelo antigo com Swashbuckle

Durante muito tempo, era comum usar apenas o Swashbuckle para duas tarefas ao mesmo tempo. Ele gerava a documentação com AddSwaggerGen e também renderizava a interface com UseSwaggerUI.

Esse arranjo criava um acoplamento desnecessário. A documentação passava a depender do Swagger, trocar a interface exigia reconfigurar tudo e o contrato da API ficava preso a uma biblioteca de terceiros.

A partir do .NET 9, o ASP.NET Core passou a oferecer suporte nativo à geração de documentos OpenAPI pelo pacote Microsoft.AspNetCore.OpenApi. Além disso, os templates de Web API deixaram de incluir o Swashbuckle por padrão.

A documentação oficial da Microsoft deixa a divisão clara. O ASP.NET Core gera apenas os documentos, enquanto interfaces como Swagger UI e Scalar precisam ser adicionadas separadamente.

Em resumo, a recomendação atual é separar a construção da documentação da renderização da interface.

Na prática, isso significa que o OpenAPI no .NET passou a ter uma fonte oficial, independente de qualquer tela.

Cenário de exemplo com Minimal API

Para os exemplos deste artigo, vamos considerar uma API simples, construída com ASP.NET Core Minimal API. Se você ainda não conhece esse modelo, o artigo sobre Minimal APIs no ASP.NET é um bom ponto de partida.

A API terá duas versões, v1 e v2, com endpoints básicos apenas para visualização. Dessa forma, o foco fica totalmente na documentação, e não em regra de negócio.

O objetivo aqui é mostrar estrutura e conceito, não complexidade. Portanto, os handlers devolvem dados fixos.

Como gerar o documento OpenAPI no .NET

O .NET possui uma biblioteca oficial responsável pela geração do documento OpenAPI. Primeiro, adicione o pacote ao projeto:

dotnet add package Microsoft.AspNetCore.OpenApi

Essa biblioteca tem uma responsabilidade clara: gerar o contrato da API. Em seguida, registre o serviço no container de injeção de dependência:

// Habilita a geração do documento OpenAPI (nome padrão: v1)
builder.Services.AddOpenApi();

Esse passo não cria nenhuma interface gráfica. Ele apenas habilita a geração da documentação, que o framework monta a partir dos metadados dos endpoints.

Vale notar que o pacote gera documentos na versão 3.1 da especificação a partir do .NET 10. Caso precise de compatibilidade com ferramentas antigas, a propriedade OpenApiVersion permite escolher outra versão.

Configurando múltiplos documentos OpenAPI no .NET por versão

Cada versão da API deve possuir seu próprio documento. Para isso, chame AddOpenApi uma vez para cada documento, passando um nome diferente no primeiro parâmetro.

Além do nome, cada chamada aceita suas próprias opções. É ali que entram os transformers de documento, que permitem ajustar informações como título, descrição, servidores e links externos:

builder.Services.AddOpenApi("v1", options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken) =>
    {
        // Informações gerais da versão 1
        document.Info = new()
        {
            Title = "My API v1",
            Description = "First version of the API",
            Version = "v1"
        };

        // Servidores onde a API está disponível
        document.Servers =
        [
            new() { Url = "https://localhost:7044/", Description = "Local server" },
            new() { Url = "https://api.example.com", Description = "Production server" }
        ];

        // Link para uma documentação complementar
        document.ExternalDocs = new()
        {
            Description = "External documentation",
            Url = new Uri("https://example.com/docs")
        };

        return Task.CompletedTask;
    });
});

// A versão 2 segue o mesmo modelo, com suas próprias informações
builder.Services.AddOpenApi("v2", options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken) =>
    {
        document.Info = new()
        {
            Title = "My API v2",
            Description = "Second version of the API",
            Version = "v2"
        };

        return Task.CompletedTask;
    });
});

Os transformers de documento rodam por último, depois que todas as operações e schemas já foram adicionados. Além disso, quando a aplicação gera vários documentos, cada transformer roda de forma independente para cada um deles.

Essa separação garante documentação clara por versão e evolução sem quebra de contrato. Também abre espaço para que múltiplas interfaces consumam os mesmos documentos.

Expondo os documentos com MapOpenApi

Depois de configurar os documentos, é necessário expô-los por meio de um endpoint. O método MapOpenApi faz esse trabalho:

// Publica cada documento em /docs/{nome-do-documento}.json
app.MapOpenApi("/docs/{documentName}.json");

Nesse momento, /docs/v1.json passa a ser a documentação real da v1, enquanto /docs/v2.json representa o contrato da v2. O parâmetro {documentName} busca automaticamente os nomes registrados em AddOpenApi.

Sem um padrão personalizado, a rota padrão é /openapi/{documentName}.json. Se o padrão terminar em .yaml ou .yml, o framework entrega o documento em YAML.

Esses arquivos podem ser versionados, armazenados, compartilhados e consumidos por qualquer ferramenta compatível. Inclusive, o .NET também consegue gerar os documentos durante o build, como explicamos no artigo sobre OpenAPI no ASP.NET Core em tempo de build.

Vinculando endpoints à versão correta

Os endpoints precisam ficar associados à versão correta da API. Para isso, usamos grupos de rotas com MapGroup e WithGroupName:

// Endpoints da versão 1
var v1 = app.MapGroup("/v1").WithGroupName("v1");
v1.MapGet("/orders", () => Results.Ok(new[] { "order-1", "order-2" }));

// Endpoints da versão 2
var v2 = app.MapGroup("/v2").WithGroupName("v2");
v2.MapGet("/orders", () => Results.Ok(new[] { new { Id = 1, Status = "Created" } }));

O framework usa o delegate ShouldInclude das opções para decidir quais endpoints entram em cada documento. Por padrão, ele compara o nome do grupo com o nome do documento.

Com isso, apenas endpoints do grupo v1 aparecem no documento v1, e o mesmo vale para a v2. Um detalhe importante: endpoints sem nome de grupo aparecem em todos os documentos.

Esse passo não é documentação em si, mas organização da API. Mesmo assim, ele reflete diretamente no contrato gerado.

Assim, o OpenAPI no .NET passa a refletir com fidelidade a forma como você organizou as rotas.

Renderizando o OpenAPI no .NET com Swagger UI

Com a documentação pronta, o Swagger passa a ser apenas um consumidor. Para isso, instale somente o pacote da interface:

dotnet add package Swashbuckle.AspNetCore.SwaggerUI

Repare que não é necessário registrar nenhum serviço do Swagger no builder. A configuração acontece apenas no app, apontando para os documentos já publicados:

app.UseSwaggerUI(options =>
{
    // Cada entrada aponta para um documento gerado pelo MapOpenApi
    options.SwaggerEndpoint("/docs/v1.json", "My API v1");
    options.SwaggerEndpoint("/docs/v2.json", "My API v2");
});

Agora o Swagger não gera mais documentação. Ele apenas renderiza o OpenAPI existente, e trocar de interface não quebra o contrato.

Por padrão, a interface fica disponível em /swagger. Também é possível abrir essa rota automaticamente ao rodar o projeto, ajustando launchUrl no arquivo launchSettings.json.

Renderizando o OpenAPI no .NET com Scalar

O Scalar segue exatamente a mesma ideia: consumir o documento gerado. Assim como no Swagger UI, basta instalar o pacote da interface:

dotnet add package Scalar.AspNetCore

Mais uma vez, não há nada para registrar no builder. A configuração acontece apenas no app:

using Scalar.AspNetCore;

app.MapScalarApiReference("/scalar", options =>
{
    // Usa o mesmo padrão de rota definido no MapOpenApi
    options.WithOpenApiRoutePattern("/docs/{documentName}.json");
    options.AddDocument("v1", "My API v1");
    options.AddDocument("v2", "My API v2");
});

O padrão informado em WithOpenApiRoutePattern precisa ser o mesmo usado no MapOpenApi. Caso contrário, o Scalar procura os documentos em um endereço que não existe e exibe uma página vazia.

Aqui o OpenAPI continua sendo o mesmo. O que muda é apenas a experiência visual de quem consulta a API.

Em outras palavras, o OpenAPI no .NET não muda quando você troca o Swagger UI pelo Scalar.

Swagger ou Scalar: qual escolher

Não existe resposta absoluta para essa pergunta. Cada interface tem pontos fortes que fazem sentido em contextos diferentes.

O Swagger UI é um padrão consolidado e muito aceito no mercado. Além disso, é simples e direto, o que facilita a adoção em times que já conhecem a ferramenta.

O Scalar, por sua vez, entrega uma experiência melhor para quem consome APIs. Também é excelente para aprendizado e experiência do desenvolvedor, com recursos modernos de exportação de código.

Como o documento é o mesmo, nada impede que você publique as duas interfaces ao mesmo tempo. Dessa forma, cada pessoa escolhe a que preferir.

O ponto principal é este: quando o OpenAPI está bem estruturado, a interface se torna apenas um detalhe.

Boas práticas de OpenAPI no .NET

Algumas práticas ajudam a manter a documentação útil ao longo da vida da API:

  • Trate o OpenAPI como contrato
  • Separe a geração de documentação da interface
  • Documente versões desde o início
  • Informe os servidores disponíveis em cada ambiente
  • Evite acoplar a documentação ao Swagger
  • Lembre que a interface é descartável, mas o contrato não

Há ainda uma recomendação de segurança da própria Microsoft. As interfaces de OpenAPI, como Swagger UI, ReDoc e Scalar, devem ficar habilitadas apenas em ambiente de desenvolvimento, para limitar a exposição de informações:

if (app.Environment.IsDevelopment())
{
    // Documentos e interfaces disponíveis somente em desenvolvimento
    app.MapOpenApi("/docs/{documentName}.json");
    app.MapScalarApiReference("/scalar");
}

Se o documento precisar ficar público, avalie protegê-lo com autenticação. Afinal, o contrato descreve toda a superfície da sua API.

Por fim, aproveite os metadados do Minimal API, como WithSummary, WithDescription e Produces. Quanto mais ricos esses metadados, mais completo fica o contrato publicado.

Conclusão

Documentar APIs não é sobre gerar telas bonitas. Na verdade, é sobre definir contratos claros, versionados e reutilizáveis.

Quando você entende que o OpenAPI no .NET gera a documentação e que Swagger e Scalar apenas exibem esse conteúdo, a arquitetura fica muito mais simples. Afinal, a documentação deixa de depender da interface.

Com isso, sua API se torna mais profissional, mais flexível e mais preparada para crescer. Trocar de interface vira uma decisão de experiência, e não uma reescrita de configuração.

Quem controla o OpenAPI controla a documentação da API.

FAQ

Swagger e OpenAPI são a mesma coisa?

Não. OpenAPI é a especificação que descreve a API, enquanto Swagger é uma família de ferramentas, como o Swagger UI, que trabalha com essa especificação.

Preciso do Swashbuckle para gerar documentação no .NET 9 ou superior?

Não. O pacote Microsoft.AspNetCore.OpenApi gera o documento nativamente, e o Swashbuckle pode ser usado apenas para a interface, com o pacote Swashbuckle.AspNetCore.SwaggerUI.

Posso usar Swagger UI e Scalar ao mesmo tempo?

Sim. Como ambos consomem o mesmo documento OpenAPI, você pode publicar as duas interfaces apontando para as mesmas rotas.

Como gerar um documento separado para cada versão da API?

Chame AddOpenApi uma vez para cada versão e associe os endpoints com WithGroupName, usando o mesmo nome do documento.

Por que um endpoint aparece em todos os documentos?

Isso acontece quando o endpoint não tem nome de grupo. Por padrão, endpoints sem WithGroupName entram em todos os documentos gerados.

Devo expor a interface de documentação em produção?

A Microsoft recomenda habilitar as interfaces apenas em desenvolvimento. Se precisar expor o documento, proteja o endpoint com autenticação.