A documentação é um componente essencial no desenvolvimento de APIs, facilitando tanto para os desenvolvedores que criam a API quanto para aqueles que a consomem. Uma ferramenta amplamente reconhecida para essa finalidade é o Swagger, que permite descrever a estrutura de suas APIs para que possam ser compreendidas e utilizadas por um público amplo. Neste artigo, exploraremos como implementar o Swagger em uma API Delphi usando o framework Horse e o middleware GBSwagger.
O que é Swagger?
Swagger é uma especificação e um conjunto de ferramentas de software para descrever, produzir, consumir e visualizar serviços web RESTful. Ele oferece um formato padrão para a descrição de uma API, incluindo rotas, métodos de requisição, parâmetros de entrada, formatos de resposta, e muito mais. Além disso, o Swagger inclui uma interface de usuário web que permite aos desenvolvedores interagir com a API diretamente através do navegador, facilitando o teste e a integração.
Vantagens de documentar a API
Documentar uma API tem várias vantagens significativas:
- Claridade operacional: Ajuda os desenvolvedores a entenderem rapidamente como a API funciona, quais serviços oferece e como integrá-la.
- Facilidade de integração: Consumidores da API podem testar os endpoints diretamente e ver como os sistemas respondem em tempo real.
- Redução de tempo de integração: Reduz o tempo necessário para que novos desenvolvedores se familiarizem com a API.
- Maior adesão e uso: Uma boa documentação pode aumentar a utilização da API, uma vez que é mais fácil de usar e entender.
- Feedback e melhorias: A documentação clara permite que os usuários finais forneçam feedback mais construtivo para melhorar a API.
Como podemos ver, o Swagger é essencial para que a API seja entendida na sua completude. Além disso, o Swagger é fortemente usando no mercado para documentar API’s em diversas linguagens de programação. Você pode, obviamente, criar sua documentação da forma como desejar, mas saiba que desenvolvedores Delphi ou de outras linguagens estão muito acostumados ao formato do Swagger. Outro ponto importante é que você pode simplesmente abrir um editor de código fonte como VSCode, SublimeText ou até bloco de notas como o Notepadd++ e criar sua documentação Swagger, entretanto o desafio aqui é criar a api ao mesmo tempo que documentamos ela, vamos lá?
Iniciando no GBSwagger
O desenvolvimento de apis no Delphi pode ser feito com diversos frameworks, nesse artigo veremos um exemplo com o Horse que já possui um middleware de terceiro para documentar usando Swagger. Vamos usar o GBSwagger do meu amigo Gabriel Baltazar. Ele se integra facilmente ao Horse e permite que a gente crie a API no mesmo momento que a documenta.
Exemplo:
Vamos criar um exemplo muito simples para que entenda como tudo funciona. Imagine uma API que irá listar uma tabela de Clientes de um banco de dados qualquer. Essa tabela terá apenas os campos:
1 2 3 4 5 6 7 |
ID : Integer; Nome: string; DataDeInclusao : TDateTime; CreditoPreAprovado: Double; DiasPraPagar: Integer; |
Algo bem simples como pudemos ver. Não vamos fazer conexão com banco de dados, tão pouco as operações de CRUD, o objetivo aqui é só criar a documentação.
Crie então um projeto novo, dê um nome a ele e faça a instalação do Horse juntamente com o middlware Jhonson. Não entrarei em detalhes, pois suponho nesse artigo que você já esteja familiarizado com o Horse.
Instalando o GBSwagger
A instalação do GBSwagger é bem fácil assim como qualquer outro middleware. Ele está listado entre os middlewares de terceiro na página do GitHub da HashLoad, time que desenvolve o Horse. Basta acessar o link do GBSwagger na página ou pra facilitar clique aqui. Recomendo fortemente que leia a documentação, é bem simples, mas tem detalhes que são importantes para composição da sua documentação.
Uma vez sua api criada, basta acessar o terminal no Windows, navegar até a pasta do projeto e digitar:
1 2 3 |
boss install github.com/gabrielbaltazar/gbswagger |
Caso não use o Boss para gerenciar suas dependências, baixe o projeto GBSwagger para o computador, descompacte a pasta em um local da sua preferência e adicione a referência da pasta no Library Path do projeto.
Uso do GBSwagger
Assim como qualquer outro middleware, basta adicionarmos a unit do projeto, nesse caso Horse.GBSwagger ao uses e então adicionar a linha abaixo no DPR da aplicação.
1 2 3 4 |
THorse .Use(HorseSwagger); // Access http://localhost:9000/swagger/doc/html |
Copiamos a linha de exemplo do GitHub do GBSwagger, perceba que o caminho default da documentação está comentada no próprio exemplo. Essa é uma das principais vantagens do middleware. Quando construímos a documentação de qualquer api é comum criarmos o arquivo HTML dessa documentação e publicarmos em um serviço de hospedagem para que fique visível, no caso do Horse e do GBSwagger, a documentação acompanha o executável da API. Isso mesmo! Em qualquer lugar que você publicar a API a documentação já estará pronta. Legal né?
Há uma variante do .Use, é possível você especificar o caminho onde essa documentação será apresentada. Por exemplo:
1 2 3 4 5 6 |
[...] THorse .Use(HorseSwagger(Format('%s/swagger/doc/html', ['v1']),Format('%s/swagger/doc/json', ['v1']))); [...] |
No exemplo acima especifiquei que a documentação será acessível no link http://localhost:9000/v1/swagger/doc/html, ou seja, podemos personalizar o endereço como desejarmos. Vamos manter esse padrão com o “v1” para simular uma versão de nossa API. O código completo do DPR desse exemplo simples ficou assim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
program MinhaAPI; {$APPTYPE CONSOLE} {$R *.res} uses Horse, Horse.Jhonson, Horse.GBSwagger, System.SysUtils, API.Controller.Clientes in 'controllers\API.Controller.Clientes.pas', API.Model.Clientes in 'model\API.Model.Clientes.pas'; begin THorse .Use(Jhonson); THorse .Use(HorseSwagger(Format('%s/swagger/doc/html', ['v1']),Format('%s/swagger/doc/json', ['v1']))); THorse .Group .Prefix('v1') .get('ping', procedure(Req: THorseRequest; Res: THorseResponse) begin Res.Send('pong') end ); THorse .Listen(9000); end. |
Uma vez feita a primeira configuração do Swagger, se executarmos nossa api e abrirmos o browser com o endereço dessa documentação, veremos algo semelhante a figura abaixo:
Entendendo a Documentação
Já temos aqui algumas informações. Com o middleware GBSwagger configurado, você pode começar a documentar sua API. O primeiro passo é definir o cabeçalho da documentação, que inclui informações básicas como o título da API, uma breve descrição, a versão e os termos de serviço. Eu gosto de deixar meus projetos bem separados em units, cada uma com sua responsabilidade. Por isso, crie uma nova unit no Delphi, chame-a de API.Swagger.Doc e escreva o seu código como abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
unit API.Swagger.Doc; interface uses Horse.GBSwagger; procedure StartDocumentation; implementation procedure StartDocumentation; begin Swagger .Info .Title('API de Clientes - Adriano Santos Blog') .Description('API de exemplo para artigo') .Contact .Name('Adriano Santos') .Email('adriano@adrianosantostreina.com.br') .URL('https://adrianosantostreina.com.br') .&End .&end; Swagger .Config .ClassPrefixes('TModel') .&End; end; initialization StartDocumentation; end. |
O que fizemos aqui é relativamente simples. Criamos uma procedure que inicializará o cabeçalho da documentação, implementamos nossas personalizações e chamamos essa procedure pelo Initialization da unit. Bem fácil. Se você abrir agora a documentação ela se parecerá com a figura a seguir:
Já mudou bastante e por ai vai. Podemos personalizar muitas coisas. Eu recomendo que dê um Ctrl + Barra de Espaço nas seções e veja o que pode ser personalizado.
Incluindo os Endpoints e Verbos
Bom, até o momento temos apenas o cabeçalho da documentação. Vamos exemplificar a criação dos endepoints para GET, POST, PUT e DELETE da nossa api. Mas antes, deixa eu fazer um parênteses e explicar outras vantagens do middleware.
Registro das Rotas
Se você já está acostumado a criar API em Horse, vai lembrar que para registrar uma rota podemos simplesmente chamar a classe THorse e passar o verbo com a procedure anônima associada às implementações do endpoint, assim:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[...] THorse .Get('clientes', procedure (Req: THorseRequest;Res: THorseResponse) begin //Seu código aqui end ) [...] |
Do mesmo modo podemos implementar os demais verbos. O GBSwagger possui classes e métodos que já fazem o registro da rota automaticamente e fica muito mais fácil trabalhar, assim, podemos criar a api e já documentar em uma “paulada” só. Para isso, precisamos tomar apenas uma única providência: transformar nossos Controllers em classes. Eu gosto de usar TControllerNOME_DO_RECURSO, exemplo: TControllerClientes, TControllerFornecedores, e assim sucessivamente. Mas fica a seu critério.
Criando o primeiro controller com o GBSwagger
Perfeito, então vamos criar uma controller para a tabela de Clientes. Crie uma unit nova no Delphi, salve como API.Controller.Clientes e escreva o código dela como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
unit API.Controller.Clientes; interface uses Horse, System.StrUtils, System.SysUtils, System.Classes, Horse.GBSwagger.Register, Horse.GBSwagger.Controller, GBSwagger.Path.Attributes, API.Model.Clientes; type [SwagPath('v1', 'clientes')] TControllerClientes = class(THorseGBSwagger) private public [SwagGet('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] procedure DoListClientes; end; implementation { TControllerClientes } procedure TControllerClientes.DoListClientes; begin FResponse.Send('JSON da Lista de Clientes'); end; initialization THorseGBSwaggerRegister.RegisterPath(TControllerClientes); end. |
Vou te explicar o que fizemos aqui. Declaramos todas as units necessárias do Horse e GBSwagger e criamos uma classe chamada TControllerClientes. Perceba que antes da classe temos uma anotação. Essa anotação criará na documentação uma TAG chamada Clientes com o prefixo ‘v1’, isso significa que nosso endpoint de clientes ficará http://localhost:9000/v1/clientes. Também informamos que a classe será herdada de THorseGBSwagger, essa classe do GBSwagger já tem algumas coisas que precisamos pra registrar a rota.
Em seguida, criamos o método que responderá quando a rota for requisitada. Veja que temos outra anotação acima do método DoListClientes.
1 2 3 4 |
[SwagGet('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] |
Na verdade temos duas anotações, SwagGet e SwagResponse. A primeira determina que o verbo desse endpoint será do tipo Get e a segunda informa que temos um retorno do tipo 200 que será o retorno default. Podemos incluir outros retornos, quantos quisermos.
Outro detalhe, você notará que em SwagResponse temos um segundo parâmetro apontando para a classe TModelClientes. Esse parâmetro representa o Schema do endpoint, ou seja, o que será retornado. Você pode simplesmente colocar uma string ou criar uma classe que represente os campos da tabela que serão retornados, essa parte é a que mais gosto.
Antes de continuarmos, crie uma nova unit, salve como API.Model.Clientes e digite o código abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
unit API.Model.Clientes; interface uses GBSwagger.Model.Attributes; type TModelClientes = class private FID : Integer; FNome: string; FDataDeInclusao : TDateTime; FCreditoPreAprovado: Double; FDiasPraPagar: Integer; public property ID : Integer read FID write FID; property Nome: string read FNome write FNome; property DataDeInclusao : TDateTime read FDataDeInclusao write FDataDeInclusao; property CreditoPreAprovado: Double read FCreditoPreAprovado write FCreditoPreAprovado; property DiasPraPagar: Integer read FDiasPraPagar write FDiasPraPagar; end; implementation end. |
Não precisa necessariamente ter esse nome de Unit nem de Classe, crie como desejar. Eu prefiro dessa forma, creio que fique mais claro. Perceba que não tem nada demais ai, apenas uma classe com as propriedades que vão representar cada campo. Nós voltaremos nela mais adiante.
Retorne para a unit do Controller e ao final da unit não podemos esquecer de chamar o registro da classe que por sua vez vai registrar a rota.
1 2 3 4 5 |
initialization THorseGBSwaggerRegister.RegisterPath(TControllerClientes); |
Compile, execute a API e atualize o browser. Veja que a documentação já mudou.
Agora temos a representação do verbo GET. Ao clicar na seta do lado, veremos todas as informações desse endpoint. Retorne no código-fonte e altere conforme abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
unit API.Controller.Clientes; interface uses Horse, System.StrUtils, System.SysUtils, System.Classes, Horse.GBSwagger.Register, Horse.GBSwagger.Controller, GBSwagger.Path.Attributes, API.Model.Clientes; type [SwagPath('v1', 'clientes')] TControllerClientes = class(THorseGBSwagger) private public [SwagGet('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] procedure DoListClientes; [SwagPost('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] procedure DoPost; [SwagPut('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] procedure DoPut; [SwagDelete('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] procedure DoDelete; end; implementation { TControllerClientes } procedure TControllerClientes.DoDelete; begin FResponse.Send('Delete'); end; procedure TControllerClientes.DoListClientes; begin FResponse.Send('List'); end; procedure TControllerClientes.DoPost; begin FResponse.Send('Post'); end; procedure TControllerClientes.DoPut; begin FResponse.Send('Put'); end; initialization THorseGBSwaggerRegister.RegisterPath(TControllerClientes); end. |
Apenas adicionamos os novos endpoins com os verbos POST, PUT e DELETE. Agora sim, se quiser compilar e dar um refresh no browser, verá algo como:
O detalhe mais importante aqui. Quando você abrir as configurações do endpoint em qualquer verbo, verá o “Schema” de cada um. Qual JSON é recebido e como podemos enviar os dados.
Muito bom não é mesmo?
Melhorando a Documentação
Show de bola, agora vamos imaginar algumas situações. Digamos que o campo ID seja um autonumérico no servidor, logo no verbo POST não precisamos informá-lo, ou seja, podemos ocultar esse campo. Além disso, precisamos muitas vezes implementar parâmetros no path, body ou query param. Como fazer isso?
Bom, os parâmetros são inclusos no Controller e os atributos de cada campo no Model, por isso separamos tudo pra ficar mais fácil. Vamos fazer uma alteração simples para entendermos melhor.
Digamos que precisamos informar qual o ID do registro que precisa ser Excluído no servidor, normalmente usamos um path param. Nossa url ficaria algo como:
1 2 3 |
http://localhost:9000/v1/clientes/123 |
Onde 123 é o registro que será exluído. Pra indicar a criação de um parâmetro na documentação e também possibilitar fazer testes ao vivo no swagger, teremos que fazer uma anotação nova. Nesse caso no verbo Delete. Volte no código e faça a seguinte alteração.
1 2 3 4 5 6 |
[SwagDelete('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] [SwagParamPath('id', 'ID do registro', True)] procedure DoDelete; |
Adicionamos o SwagParamPath indicando que ele é um número. Execute a API agora e veja documentação. Abra o verbo get e note a inclusão do parâmetro.
Desse modo, quem estiver lendo nossa documentação saberá que podemos passar o ID do registro para ser deletado no lado servidor. Faça mais uma mudança. Vamos criar um novo recurso para o verbo Get, vamos permitir ter um query param, ou seja, filtros. Insira as anotações dessa forma:
1 2 3 4 5 6 7 |
[SwagGet('clientes', 'Descrição do Endpoint')] [SwagResponse(200, TModelClientes, 'Retorno com sucesso')] [SwagParamQuery('estado', 'Estado do cliente', False, False)] [SwagParamQuery('cidade', 'Cidade do cliente', False, False)] procedure DoListClientes; |
Estamos criando dois parâmetros do tipo Query Param não obrigatório, estado e cidade. Imagine que possamos talvez filtrar do lado servidor todos os clientes do estado de São Paulo e Cidade igual a Campinas. Claro que teremos que identificar os parâmetros recebidos do lado servidor e fazer os respectivos selects, mas não vem ao caso agora. Com a criação desses parâmetros, agora temos a possibilidade de enviar a requisição assim:
1 2 3 |
http://localhost:9000/v1/clientes?estado=SP&cidade=Campinas |
E ao rodarmos a API os parâmetros aparecerão na documtenção.
Mudando os atributos do campo
Pra seguimos para o final do nosso artigo, vamos fazer algumas mudanças no Model. Nós podemos determinar o tipo de dado do campo, valor mínimo, máximo, obrigatório, somente leitura, entre outros atributos. Para isso basta fazermos as devidas anotações nos campos no model. Acesse a unit do nosso TModelClientes e faça as modificações como a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
uses GBSwagger.Model.Attributes; type TModelClientes = class private FID : Integer; FNome: string; FDataDeInclusao : TDateTime; FCreditoPreAprovado: Double; FDiasPraPagar: Integer; public [SwagIgnore] property ID : Integer read FID write FID; [SwagString(100, 1)] [SwagRequired] property Nome: string read FNome write FNome; [SwagDate('YYYY-DD-MM hh:mm:ss')] property DataDeInclusao : TDateTime read FDataDeInclusao write FDataDeInclusao; [SwagNumber(1000, 10000)] property CreditoPreAprovado: Double read FCreditoPreAprovado write FCreditoPreAprovado; [SwagNumber] property DiasPraPagar: Integer read FDiasPraPagar write FDiasPraPagar; end; |
Pronto, as anotações foram feitas. Acho que dá pra entender fácil o que cada uma significa né? Bem simples. Podemos determinar se é uma string, número, data. Qual valor mínimo e máximo, obrigatório, etc. Mas atenção, só é possível fazer isso porque declaramos uma unit do Swagger lá no uses, não esqueça. Rode a API agora e veja o resultado.
Note que há uma aba chamada Model e nela a configuração é mostrada. Tudo que configuramos para cada campo foi mostrado, com isso o desenvolvedor que estiver lendo a documentação saberá exatamente como cada campo deve ser enviado ou recebido. Muito bom, né?
Torne-se um especialista em documentação
Evidentemente que não é possível mostrar tudo isso em único artigo. Fiz o possível aqui para que você entendesse os primeiros passos na construção de documentações para suas APIs em Horse com Delphi e você deve estar com muitas dúvidas nesse momento, tais como:
- Como fica minha documentação quando a tabela for um Mestre/Detalhe?
- E sobre HTTPS, como posso prosseguir?
- Como faço para documentar um campo do tipo imagem ou boleano?
Enfim, há bastante coisa a ser aprendida. A boa notícia que te dou é que temos um treinamento completo sobre o middleware GBSwagger que foi ministrado pelo próprio autor do middleware e explica tudo que você precisa saber.
O valor também é muito acessível, são 4h de aulas, 100% pronta pelo valor de 12x de R$ 14,87 ou R$ 149,00 à vista, você assiste ao conteúdo e constrói uma documentação profissional para suas APIs. Basta clicar no botão abaixo e garantir o seu curso com acesso IMEDIATO.
Conclusão
Em suma, a importância de uma documentação de API bem estruturada transcende o simples ato de disponibilizar informações para desenvolvedores terceirizados. É também crucial para os desenvolvedores internos, que se beneficiam enormemente de terem acessos claros e organizados às funcionalidades e aos parâmetros operacionais da API. Documentar uma API não só profissionaliza a entrega como eleva a relevância e a utilidade da API, facilitando sobremaneira o entendimento e a integração em diversos níveis de projetos de software.
O uso do middleware GBSwagger, em específico, integra-se perfeitamente ao ambiente Delphi e ao framework Horse, proporcionando uma maneira eficaz e eficiente de acompanhar a evolução da API sem a necessidade de publicar a documentação em outros locais. Isso não só garante que a documentação esteja sempre atualizada com a última versão da API, mas também que ela esteja sempre acessível e sincronizada com o desenvolvimento do projeto.
Portanto, investir em uma documentação de qualidade através do GBSwagger é mais do que uma prática recomendada; é uma estratégia essencial para qualquer equipe de desenvolvimento que deseja garantir clareza, eficiência e um alto padrão de profissionalismo em suas APIs.
Comunidade no Telegram
🚀Comente no campo abaixo 👇👇👇 o que achou e qual sua dúvida.
Te vejo na próxima
Adriano Santos