committed by
GitHub
1 changed files with 261 additions and 0 deletions
@ -0,0 +1,261 @@ |
|||
# Generate Clients |
|||
|
|||
Como o **FastAPI** é baseado na especificação **OpenAPI**, você obtém compatibilidade automática com muitas ferramentas, incluindo a documentação automática da API (fornecida pelo Swagger UI). |
|||
|
|||
Uma vantagem particular que nem sempre é óbvia é que você pode **gerar clientes** (às vezes chamados de <abbr title="Software Development Kits">**SDKs**</abbr>) para a sua API, para muitas **linguagens de programação** diferentes. |
|||
|
|||
## Geradores de Clientes OpenAPI |
|||
|
|||
Existem muitas ferramentas para gerar clientes a partir do **OpenAPI**. |
|||
|
|||
Uma ferramenta comum é o <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>. |
|||
|
|||
Se voce está construindo um **frontend**, uma alternativa muito interessante é o <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>. |
|||
|
|||
## Geradores de Clientes e SDKs - Patrocinadores |
|||
|
|||
Existem também alguns geradores de clientes e SDKs baseados na OpenAPI (FastAPI) **patrocinados por empresas**, em alguns casos eles podem oferecer **recursos adicionais** além de SDKs/clientes gerados de alta qualidade. |
|||
|
|||
Alguns deles também ✨ [**patrocinam o FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, isso garante o **desenvolvimento** contínuo e saudável do FastAPI e seu **ecossistema**. |
|||
|
|||
E isso mostra o verdadeiro compromisso deles com o FastAPI e sua **comunidade** (você), pois eles não apenas querem fornecer um **bom serviço**, mas também querem garantir que você tenha um **framework bom e saudável**, o FastAPI. 🙇 |
|||
|
|||
Por exemplo, você pode querer experimentar: |
|||
|
|||
* <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a> |
|||
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a> |
|||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a> |
|||
|
|||
Existem também várias outras empresas que oferecem serviços semelhantes que você pode pesquisar e encontrar online. 🤓 |
|||
|
|||
## Gerar um Cliente Frontend TypeScript |
|||
|
|||
Vamos começar com um aplicativo **FastAPI** simples: |
|||
|
|||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *} |
|||
|
|||
Note que as *operações de rota* definem os modelos que usam para o corpo da requisição e o corpo da resposta, usando os modelos `Item` e `ResponseMessage`. |
|||
|
|||
### Documentação da API |
|||
|
|||
Se você acessar a documentação da API, verá que ela tem os **schemas** para os dados a serem enviados nas requisições e recebidos nas respostas: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image01.png"> |
|||
|
|||
Você pode ver esses schemas porque eles foram declarados com os modelos no app. |
|||
|
|||
Essas informações estão disponíveis no **OpenAPI schema** do app e são mostradas na documentação da API (pelo Swagger UI). |
|||
|
|||
E essas mesmas informações dos modelos que estão incluídas no OpenAPI são o que pode ser usado para **gerar o código do cliente**. |
|||
|
|||
### Gerar um Cliente TypeScript |
|||
|
|||
Agora que temos o app com os modelos, podemos gerar o código do cliente para o frontend. |
|||
|
|||
#### Instalar o `openapi-ts` |
|||
|
|||
Você pode instalar o `openapi-ts` no seu código frontend com: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ npm install @hey-api/openapi-ts --save-dev |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
#### Gerar o Código do Cliente |
|||
|
|||
Para gerar o código do cliente, você pode usar a aplicação de linha de comando `openapi-ts` que agora está instalada. |
|||
|
|||
Como ela está instalada no projeto local, você provavelmente não conseguiria chamar esse comando diretamente, mas você o colocaria no seu arquivo `package.json`. |
|||
|
|||
Poderia ser assim: |
|||
|
|||
```JSON hl_lines="7" |
|||
{ |
|||
"name": "frontend-app", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios" |
|||
}, |
|||
"author": "", |
|||
"license": "", |
|||
"devDependencies": { |
|||
"@hey-api/openapi-ts": "^0.27.38", |
|||
"typescript": "^4.6.2" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Depois de ter esse script NPM `generate-client` lá, você pode executá-lo com: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ npm run generate-client |
|||
|
|||
[email protected] generate-client /home/user/code/frontend-app |
|||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Esse comando gerará o código em `./src/client` e usará o `axios` (a biblioteca HTTP frontend) internamente. |
|||
|
|||
### Experimente o Código do Cliente |
|||
|
|||
Agora você pode importar e usar o código do cliente, ele poderia ser assim, observe que você obtém preenchimento automático para os métodos: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image02.png"> |
|||
|
|||
Você também obterá preenchimento automático para o corpo a ser enviado: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image03.png"> |
|||
|
|||
/// tip | Dica |
|||
|
|||
Observe o preenchimento automático para `name` e `price`, que foi definido no aplicativo FastAPI, no modelo `Item`. |
|||
|
|||
/// |
|||
|
|||
Você terá erros em linha para os dados que você envia: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image04.png"> |
|||
|
|||
O objeto de resposta também terá preenchimento automático: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image05.png"> |
|||
|
|||
## App FastAPI com Tags |
|||
|
|||
Em muitos casos seu app FastAPI será maior, e você provavelmente usará tags para separar diferentes grupos de *operações de rota*. |
|||
|
|||
Por exemplo, você poderia ter uma seção para **items** e outra seção para **users**, e elas poderiam ser separadas por tags: |
|||
|
|||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *} |
|||
|
|||
### Gerar um Cliente TypeScript com Tags |
|||
|
|||
Se você gerar um cliente para um app FastAPI usando tags, normalmente também separará o código do cliente com base nas tags. |
|||
|
|||
Dessa forma, você poderá ter as coisas ordenadas e agrupadas corretamente para o código do cliente: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image06.png"> |
|||
|
|||
Nesse caso você tem: |
|||
|
|||
* `ItemsService` |
|||
* `UsersService` |
|||
|
|||
### Nomes dos Métodos do Cliente |
|||
|
|||
Agora os nomes dos métodos gerados como `createItemItemsPost` não parecem muito "limpos": |
|||
|
|||
```TypeScript |
|||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) |
|||
``` |
|||
|
|||
...isto ocorre porque o gerador de clientes usa o **operation ID** interno do OpenAPI para cada *operação de rota*. |
|||
|
|||
O OpenAPI exige que cada operation ID seja único em todas as *operações de rota*, então o FastAPI usa o **nome da função**, o **caminho** e o **método/operacao HTTP** para gerar esse operation ID, porque dessa forma ele pode garantir que os operation IDs sejam únicos. |
|||
|
|||
Mas eu vou te mostrar como melhorar isso a seguir. 🤓 |
|||
|
|||
### IDs de Operação Personalizados e Melhores Nomes de Método |
|||
|
|||
Você pode **modificar** a maneira como esses IDs de operação são **gerados** para torná-los mais simples e ter **nomes de método mais simples** nos clientes. |
|||
|
|||
Neste caso, você terá que garantir que cada ID de operação seja **único** de alguma outra maneira. |
|||
|
|||
Por exemplo, você poderia garantir que cada *operação de rota* tenha uma tag, e então gerar o ID da operação com base na **tag** e no **nome** da *operação de rota* (o nome da função). |
|||
|
|||
### Função Personalizada para Gerar IDs de Operação Únicos |
|||
|
|||
O FastAPI usa um **ID único** para cada *operação de rota*, ele é usado para o **ID da operação** e também para os nomes de quaisquer modelos personalizados necessários, para requisições ou respostas. |
|||
|
|||
Você pode personalizar essa função. Ela recebe uma `APIRoute` e gera uma string. |
|||
|
|||
Por exemplo, aqui está usando a primeira tag (você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função). |
|||
|
|||
Você pode então passar essa função personalizada para o **FastAPI** como o parâmetro `generate_unique_id_function`: |
|||
|
|||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *} |
|||
|
|||
### Gerar um Cliente TypeScript com IDs de Operação Personalizados |
|||
|
|||
Agora, se você gerar o cliente novamente, verá que ele tem os nomes dos métodos melhorados: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image07.png"> |
|||
|
|||
Como você pode ver, os nomes dos métodos agora têm a tag e, em seguida, o nome da função. Agora eles não incluem informações do caminho da URL e da operação HTTP. |
|||
|
|||
### Pré-processar a Especificação OpenAPI para o Gerador de Clientes |
|||
|
|||
O código gerado ainda tem algumas **informações duplicadas**. |
|||
|
|||
Nós já sabemos que esse método está relacionado aos **items** porque essa palavra está no `ItemsService` (retirada da tag), mas ainda temos o nome da tag prefixado no nome do método também. 😕 |
|||
|
|||
Provavelmente ainda queremos mantê-lo para o OpenAPI em geral, pois isso garantirá que os IDs de operação sejam **únicos**. |
|||
|
|||
Mas para o cliente gerado, poderíamos **modificar** os IDs de operação do OpenAPI logo antes de gerar os clientes, apenas para tornar esses nomes de método mais **simples**. |
|||
|
|||
Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este: |
|||
|
|||
{* ../../docs_src/generate_clients/tutorial004.py *} |
|||
|
|||
//// tab | Node.js |
|||
|
|||
```Javascript |
|||
{!> ../../docs_src/generate_clients/tutorial004.js!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
Com isso, os IDs de operação seriam renomeados de coisas como `items-get_items` para apenas `get_items`, dessa forma o gerador de clientes pode gerar nomes de métodos mais simples. |
|||
|
|||
### Gerar um Cliente TypeScript com o OpenAPI Pré-processado |
|||
|
|||
Agora, como o resultado final está em um arquivo `openapi.json`, você modificaria o `package.json` para usar esse arquivo local, por exemplo: |
|||
|
|||
```JSON hl_lines="7" |
|||
{ |
|||
"name": "frontend-app", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios" |
|||
}, |
|||
"author": "", |
|||
"license": "", |
|||
"devDependencies": { |
|||
"@hey-api/openapi-ts": "^0.27.38", |
|||
"typescript": "^4.6.2" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Depois de gerar o novo cliente, você teria agora **nomes de métodos "limpos"**, com todo o **preenchimento automático**, **erros em linha**, etc: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image08.png"> |
|||
|
|||
## Benefícios |
|||
|
|||
Ao usar os clientes gerados automaticamente, você teria **preenchimento automático** para: |
|||
|
|||
* Métodos. |
|||
* Corpo de requisições, parâmetros da query, etc. |
|||
* Corpo de respostas. |
|||
|
|||
Você também teria **erros em linha** para tudo. |
|||
|
|||
E sempre que você atualizar o código do backend, e **regenerar** o frontend, ele teria quaisquer novas *operações de rota* disponíveis como métodos, as antigas removidas, e qualquer outra alteração seria refletida no código gerado. 🤓 |
|||
|
|||
Isso também significa que se algo mudar, será **refletido** no código do cliente automaticamente. E se você **construir** o cliente, ele dará erro se houver alguma **incompatibilidade** nos dados usados. |
|||
|
|||
Então, você **detectaria vários erros** muito cedo no ciclo de desenvolvimento, em vez de ter que esperar que os erros apareçam para seus usuários finais em produção e então tentar depurar onde está o problema. ✨ |
Loading…
Reference in new issue