Sync fastapi docs from b5ca1324 on 2025-12-07
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled

This commit is contained in:
The Scheduler
2025-12-07 21:35:20 +00:00
commit a42b1ff04e
2600 changed files with 301870 additions and 0 deletions
+503
View File
@@ -0,0 +1,503 @@
# Arquivo de teste de LLM { #llm-test-file }
Este documento testa se o <abbr title="Large Language Model Modelo de Linguagem de Grande Porte">LLM</abbr>, que traduz a documentação, entende o `general_prompt` em `scripts/translate.py` e o prompt específico do idioma em `docs/{language code}/llm-prompt.md`. O prompt específico do idioma é anexado ao `general_prompt`.
Os testes adicionados aqui serão vistos por todos os autores dos prompts específicos de idioma.
Use da seguinte forma:
* Tenha um prompt específico do idioma `docs/{language code}/llm-prompt.md`.
* Faça uma tradução nova deste documento para o seu idioma de destino (veja, por exemplo, o comando `translate-page` do `translate.py`). Isso criará a tradução em `docs/{language code}/docs/_llm-test.md`.
* Verifique se está tudo certo na tradução.
* Se necessário, melhore seu prompt específico do idioma, o prompt geral ou o documento em inglês.
* Em seguida, corrija manualmente os problemas restantes na tradução, para que fique uma boa tradução.
* Retraduzir, tendo a boa tradução no lugar. O resultado ideal seria que o LLM não fizesse mais mudanças na tradução. Isso significa que o prompt geral e o seu prompt específico do idioma estão tão bons quanto possível (às vezes fará algumas mudanças aparentemente aleatórias, a razão é que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLMs não são algoritmos determinísticos</a>).
Os testes:
## Trechos de código { #code-snippets}
//// tab | Teste
Este é um trecho de código: `foo`. E este é outro trecho de código: `bar`. E mais um: `baz quux`.
////
//// tab | Informações
O conteúdo dos trechos de código deve ser deixado como está.
Veja a seção `### Content of code snippets` no prompt geral em `scripts/translate.py`.
////
## Citações { #quotes }
//// tab | Teste
Ontem, meu amigo escreveu: "Se você soletrar incorretamente corretamente, você a soletrou incorretamente". Ao que respondi: "Correto, mas 'incorrectly' está incorretamente não '"incorrectly"'".
/// note | Nota
O LLM provavelmente vai traduzir isso errado. O interessante é apenas se ele mantém a tradução corrigida ao retraduzir.
///
////
//// tab | Informações
O autor do prompt pode escolher se deseja converter aspas neutras em aspas tipográficas. Também é aceitável deixá-las como estão.
Veja, por exemplo, a seção `### Quotes` em `docs/de/llm-prompt.md`.
////
## Citações em trechos de código { #quotes-in-code-snippets}
//// tab | Teste
`pip install "foo[bar]"`
Exemplos de literais de string em trechos de código: `"this"`, `'that'`.
Um exemplo difícil de literais de string em trechos de código: `f"I like {'oranges' if orange else "apples"}"`
Pesado: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Informações
... No entanto, as aspas dentro de trechos de código devem permanecer como estão.
////
## Blocos de código { #code-blocks }
//// tab | Teste
Um exemplo de código Bash...
```bash
# Imprimir uma saudação ao universo
echo "Hello universe"
```
...e um exemplo de código de console...
```console
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
Searching for package file structure
```
...e outro exemplo de código de console...
```console
// Crie um diretório "Code"
$ mkdir code
// Entre nesse diretório
$ cd code
```
...e um exemplo de código Python...
```Python
wont_work() # Isto não vai funcionar 😱
works(foo="bar") # Isto funciona 🎉
```
...e é isso.
////
//// tab | Informações
O código em blocos de código não deve ser modificado, com exceção dos comentários.
Veja a seção `### Content of code blocks` no prompt geral em `scripts/translate.py`.
////
## Abas e caixas coloridas { #tabs-and-colored-boxes }
//// tab | Teste
/// info | Informação
Algum texto
///
/// note | Nota
Algum texto
///
/// note | Detalhes Técnicos
Algum texto
///
/// check | Verifique
Algum texto
///
/// tip | Dica
Algum texto
///
/// warning | Atenção
Algum texto
///
/// danger | Cuidado
Algum texto
///
////
//// tab | Informações
Abas e blocos `Info`/`Note`/`Warning`/etc. devem ter a tradução do seu título adicionada após uma barra vertical (`|`).
Veja as seções `### Special blocks` e `### Tab blocks` no prompt geral em `scripts/translate.py`.
////
## Links da Web e internos { #web-and-internal-links }
//// tab | Teste
O texto do link deve ser traduzido, o endereço do link deve permanecer inalterado:
* [Link para o título acima](#code-snippets)
* [Link interno](index.md#installation){.internal-link target=_blank}
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">Link externo</a>
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">Link para um estilo</a>
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">Link para um script</a>
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">Link para uma imagem</a>
O texto do link deve ser traduzido, o endereço do link deve apontar para a tradução:
* <a href="https://fastapi.tiangolo.com/pt/" class="external-link" target="_blank">Link do FastAPI</a>
////
//// tab | Informações
Os links devem ser traduzidos, mas seus endereços devem permanecer inalterados. Uma exceção são links absolutos para páginas da documentação do FastAPI. Nesse caso, devem apontar para a tradução.
Veja a seção `### Links` no prompt geral em `scripts/translate.py`.
////
## Elementos HTML "abbr" { #html-abbr-elements }
//// tab | Teste
Aqui estão algumas coisas envolvidas em elementos HTML "abbr" (algumas são inventadas):
### O abbr fornece uma frase completa { #the-abbr-gives-a-full-phrase }
* <abbr title="Getting Things Done Fazer as Coisas">GTD</abbr>
* <abbr title="menos que"><code>lt</code></abbr>
* <abbr title="XML Web Token Token Web XML">XWT</abbr>
* <abbr title="Parallel Server Gateway Interface Interface de Gateway de Servidor Paralelo">PSGI</abbr>
### O abbr fornece uma explicação { #the-abbr-gives-an-explanation }
* <abbr title="Um grupo de máquinas configuradas para estarem conectadas e trabalharem juntas de alguma forma.">cluster</abbr>
* <abbr title="Um método de aprendizado de máquina que usa redes neurais artificiais com numerosas camadas ocultas entre as camadas de entrada e saída, desenvolvendo assim uma estrutura interna abrangente">Aprendizado Profundo</abbr>
### O abbr fornece uma frase completa e uma explicação { #the-abbr-gives-a-full-phrase-and-an-explanation }
* <abbr title="Mozilla Developer Network Rede de Desenvolvedores da Mozilla: documentação para desenvolvedores, escrita pelo pessoal do Firefox">MDN</abbr>
* <abbr title="Input/Output Entrada/Saída: leitura ou escrita em disco, comunicações de rede.">I/O</abbr>.
////
//// tab | Informações
Os atributos "title" dos elementos "abbr" são traduzidos seguindo algumas instruções específicas.
As traduções podem adicionar seus próprios elementos "abbr" que o LLM não deve remover. Por exemplo, para explicar palavras em inglês.
Veja a seção `### HTML abbr elements` no prompt geral em `scripts/translate.py`.
////
## Títulos { #headings }
//// tab | Teste
### Desenvolver uma aplicação web - um tutorial { #develop-a-webapp-a-tutorial }
Olá.
### Anotações de tipo e -anotações { #type-hints-and-annotations }
Olá novamente.
### Super- e subclasses { #super-and-subclasses }
Olá novamente.
////
//// tab | Informações
A única regra rígida para títulos é que o LLM deixe a parte do hash dentro de chaves inalterada, o que garante que os links não quebrem.
Veja a seção `### Headings` no prompt geral em `scripts/translate.py`.
Para algumas instruções específicas do idioma, veja, por exemplo, a seção `### Headings` em `docs/de/llm-prompt.md`.
////
## Termos usados na documentação { #terms-used-in-the-docs }
//// tab | Teste
* você
* seu
* por exemplo
* etc.
* `foo` como um `int`
* `bar` como uma `str`
* `baz` como uma `list`
* o Tutorial - Guia do Usuário
* o Guia do Usuário Avançado
* a documentação do SQLModel
* a documentação da API
* a documentação automática
* Ciência de Dados
* Aprendizado Profundo
* Aprendizado de Máquina
* Injeção de Dependências
* autenticação HTTP Basic
* HTTP Digest
* formato ISO
* o padrão JSON Schema
* o JSON schema
* a definição do schema
* Fluxo de Senha
* Mobile
* descontinuado
* projetado
* inválido
* dinamicamente
* padrão
* padrão predefinido
* sensível a maiúsculas e minúsculas
* não sensível a maiúsculas e minúsculas
* servir a aplicação
* servir a página
* o app
* a aplicação
* a requisição
* a resposta
* a resposta de erro
* a operação de rota
* o decorador de operação de rota
* a função de operação de rota
* o corpo
* o corpo da requisição
* o corpo da resposta
* o corpo JSON
* o corpo do formulário
* o corpo do arquivo
* o corpo da função
* o parâmetro
* o parâmetro de corpo
* o parâmetro de path
* o parâmetro de query
* o parâmetro de cookie
* o parâmetro de header
* o parâmetro de formulário
* o parâmetro da função
* o evento
* o evento de inicialização
* a inicialização do servidor
* o evento de encerramento
* o evento de lifespan
* o manipulador
* o manipulador de eventos
* o manipulador de exceções
* tratar
* o modelo
* o modelo Pydantic
* o modelo de dados
* o modelo de banco de dados
* o modelo de formulário
* o objeto de modelo
* a classe
* a classe base
* a classe pai
* a subclasse
* a classe filha
* a classe irmã
* o método de classe
* o cabeçalho
* os cabeçalhos
* o cabeçalho de autorização
* o cabeçalho `Authorization`
* o cabeçalho encaminhado
* o sistema de injeção de dependências
* a dependência
* o dependable
* o dependant
* limitado por I/O
* limitado por CPU
* concorrência
* paralelismo
* multiprocessamento
* a env var
* a variável de ambiente
* o `PATH`
* a variável `PATH`
* a autenticação
* o provedor de autenticação
* a autorização
* o formulário de autorização
* o provedor de autorização
* o usuário se autentica
* o sistema autentica o usuário
* a CLI
* a interface de linha de comando
* o servidor
* o cliente
* o provedor de nuvem
* o serviço de nuvem
* o desenvolvimento
* as etapas de desenvolvimento
* o dict
* o dicionário
* a enumeração
* o enum
* o membro do enum
* o codificador
* o decodificador
* codificar
* decodificar
* a exceção
* lançar
* a expressão
* a instrução
* o frontend
* o backend
* a discussão do GitHub
* a issue do GitHub
* o desempenho
* a otimização de desempenho
* o tipo de retorno
* o valor de retorno
* a segurança
* o esquema de segurança
* a tarefa
* a tarefa em segundo plano
* a função da tarefa
* o template
* o mecanismo de template
* a anotação de tipo
* a anotação de tipo
* o worker de servidor
* o worker do Uvicorn
* o Worker do Gunicorn
* o processo worker
* a classe de worker
* a carga de trabalho
* a implantação
* implantar
* o SDK
* o kit de desenvolvimento de software
* o `APIRouter`
* o `requirements.txt`
* o Bearer Token
* a alteração com quebra de compatibilidade
* o bug
* o botão
* o chamável
* o código
* o commit
* o gerenciador de contexto
* a corrotina
* a sessão do banco de dados
* o disco
* o domínio
* o mecanismo
* o X falso
* o método HTTP GET
* o item
* a biblioteca
* o lifespan
* o bloqueio
* o middleware
* a aplicação mobile
* o módulo
* a montagem
* a rede
* a origem
* a sobrescrita
* a carga útil
* o processador
* a propriedade
* o proxy
* o pull request
* a consulta
* a RAM
* a máquina remota
* o código de status
* a string
* a tag
* o framework web
* o curinga
* retornar
* validar
////
//// tab | Informações
Esta é uma lista não completa e não normativa de termos (principalmente) técnicos vistos na documentação. Pode ser útil para o autor do prompt descobrir para quais termos o LLM precisa de uma ajudinha. Por exemplo, quando ele continua revertendo uma boa tradução para uma tradução subótima. Ou quando tem problemas para conjugar/declinar um termo no seu idioma.
Veja, por exemplo, a seção `### List of English terms and their preferred German translations` em `docs/de/llm-prompt.md`.
////
+3
View File
@@ -0,0 +1,3 @@
# Sobre { #about }
Sobre o FastAPI, seu design, inspiração e mais. 🤓
@@ -0,0 +1,247 @@
# Retornos Adicionais no OpenAPI { #additional-responses-in-openapi }
/// warning | Atenção
Este é um tema bem avançado.
Se você está começando com o **FastAPI**, provavelmente você não precisa disso.
///
Você pode declarar retornos adicionais, com códigos de status adicionais, media types, descrições, etc.
Essas respostas adicionais serão incluídas no esquema do OpenAPI, e também aparecerão na documentação da API.
Porém para as respostas adicionais, você deve garantir que está retornando um `Response` como por exemplo o `JSONResponse` diretamente, junto com o código de status e o conteúdo.
## Retorno Adicional com `model` { #additional-response-with-model }
Você pode fornecer o parâmetro `responses` aos seus *decoradores de caminho*.
Este parâmetro recebe um `dict`, as chaves são os códigos de status para cada retorno, como por exemplo `200`, e os valores são um outro `dict` com a informação de cada um deles.
Cada um desses `dict` de retorno pode ter uma chave `model`, contendo um modelo do Pydantic, assim como o `response_model`.
O **FastAPI** pegará este modelo, gerará o esquema JSON dele e incluirá no local correto do OpenAPI.
Por exemplo, para declarar um outro retorno com o status code `404` e um modelo do Pydantic chamado `Message`, você pode escrever:
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
/// note | Nota
Lembre-se que você deve retornar o `JSONResponse` diretamente.
///
/// info | Informação
A chave `model` não é parte do OpenAPI.
O **FastAPI** pegará o modelo do Pydantic, gerará o `JSON Schema`, e adicionará no local correto.
O local correto é:
* Na chave `content`, que tem como valor um outro objeto JSON (`dict`) que contém:
* Uma chave com o media type, como por exemplo `application/json`, que contém como valor um outro objeto JSON, contendo::
* Uma chave `schema`, que contém como valor o JSON Schema do modelo, sendo este o local correto.
* O **FastAPI** adiciona aqui a referência dos esquemas JSON globais que estão localizados em outro lugar, ao invés de incluí-lo diretamente. Deste modo, outras aplicações e clientes podem utilizar estes esquemas JSON diretamente, fornecer melhores ferramentas de geração de código, etc.
///
O retorno gerado no OpenAPI para esta *operação de rota* será:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Os esquemas são referenciados em outro local dentro do esquema OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Media types adicionais para o retorno principal { #additional-media-types-for-the-main-response }
Você pode utilizar o mesmo parâmetro `responses` para adicionar diferentes media types para o mesmo retorno principal.
Por exemplo, você pode adicionar um media type adicional de `image/png`, declarando que a sua *operação de rota* pode retornar um objeto JSON (com o media type `application/json`) ou uma imagem PNG:
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
/// note | Nota
Note que você deve retornar a imagem utilizando um `FileResponse` diretamente.
///
/// info | Informação
A menos que você especifique um media type diferente explicitamente em seu parâmetro `responses`, o FastAPI assumirá que o retorno possui o mesmo media type contido na classe principal de retorno (padrão `application/json`).
Porém se você especificou uma classe de retorno com o valor `None` como media type, o FastAPI utilizará `application/json` para qualquer retorno adicional que possui um modelo associado.
///
## Combinando informações { #combining-information }
Você também pode combinar informações de diferentes lugares, incluindo os parâmetros `response_model`, `status_code`, e `responses`.
Você pode declarar um `response_model`, utilizando o código de status padrão `200` (ou um customizado caso você precise), e depois adicionar informações adicionais para esse mesmo retorno em `responses`, diretamente no esquema OpenAPI.
O **FastAPI** manterá as informações adicionais do `responses`, e combinará com o esquema JSON do seu modelo.
Por exemplo, você pode declarar um retorno com o código de status `404` que utiliza um modelo do Pydantic que possui um `description` customizado.
E um retorno com o código de status `200` que utiliza o seu `response_model`, porém inclui um `example` customizado:
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API:
<img src="/img/tutorial/additional-responses/image01.png">
## Combinar retornos predefinidos e personalizados { #combine-predefined-responses-and-custom-ones }
Você pode querer possuir alguns retornos predefinidos que são aplicados para diversas *operações de rota*, porém você deseja combinar com retornos personalizados que são necessários para cada *operação de rota*.
Para estes casos, você pode utilizar a técnica do Python de "desempacotamento" de um `dict` utilizando `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Aqui, o `new_dict` terá todos os pares de chave-valor do `old_dict` mais o novo par de chave-valor:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Você pode utilizar essa técnica para reutilizar alguns retornos predefinidos nas suas *operações de rota* e combiná-las com personalizações adicionais.
Por exemplo:
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
## Mais informações sobre retornos OpenAPI { #more-information-about-openapi-responses }
Para verificar exatamente o que você pode incluir nos retornos, você pode conferir estas seções na especificação do OpenAPI:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, inclui o `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro `responses`. Incluindo `description`, `headers`, `content` (dentro dele que você declara diferentes media types e esquemas JSON), e `links`.
@@ -0,0 +1,41 @@
# Códigos de status adicionais { #additional-status-codes }
Por padrão, o **FastAPI** retornará as respostas utilizando o `JSONResponse`, adicionando o conteúdo do retorno da sua *operação de rota* dentro do `JSONResponse`.
Ele usará o código de status padrão ou o que você definir na sua *operação de rota*.
## Códigos de status adicionais { #additional-status-codes_1 }
Caso você queira retornar códigos de status adicionais além do código principal, você pode fazer isso retornando um `Response` diretamente, como por exemplo um `JSONResponse`, e definir os códigos de status adicionais diretamente.
Por exemplo, vamos dizer que você deseja ter uma *operação de rota* que permita atualizar itens, e retornar um código de status HTTP 200 "OK" quando for bem sucedido.
Mas você também deseja aceitar novos itens. E quando os itens não existiam, ele os cria, e retorna o código de status HTTP 201 "Created".
Para conseguir isso, importe `JSONResponse` e retorne o seu conteúdo diretamente, definindo o `status_code` que você deseja:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Atenção
Quando você retorna um `Response` diretamente, como no exemplo acima, ele será retornado diretamente.
Ele não será serializado com um modelo, etc.
Garanta que ele tenha toda informação que você deseja, e que os valores sejam um JSON válido (caso você esteja usando `JSONResponse`).
///
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import JSONResponse`.
O **FastAPI** disponibiliza o `starlette.responses` como `fastapi.responses` apenas por conveniência para você, o programador. Porém a maioria dos retornos disponíveis vem diretamente do Starlette. O mesmo com `status`.
///
## OpenAPI e documentação da API { #openapi-and-api-docs }
Se você retorna códigos de status adicionais e retornos diretamente, eles não serão incluídos no esquema do OpenAPI (a documentação da API), porque o FastAPI não tem como saber de antemão o que será retornado.
Mas você pode documentar isso no seu código, utilizando: [Retornos Adicionais](additional-responses.md){.internal-link target=_blank}.
@@ -0,0 +1,163 @@
# Dependências avançadas { #advanced-dependencies }
## Dependências parametrizadas { #parameterized-dependencies }
Todas as dependências que vimos até agora são funções ou classes fixas.
Mas podem ocorrer casos onde você deseja ser capaz de definir parâmetros na dependência, sem ter a necessidade de declarar diversas funções ou classes.
Vamos imaginar que queremos ter uma dependência que verifica se o parâmetro de consulta `q` possui um valor fixo.
Porém nós queremos poder parametrizar o conteúdo fixo.
## Uma instância "chamável" { #a-callable-instance }
Em Python existe uma maneira de fazer com que uma instância de uma classe seja um "chamável".
Não propriamente a classe (que já é um chamável), mas a instância desta classe.
Para fazer isso, nós declaramos o método `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
Neste caso, o `__call__` é o que o **FastAPI** utilizará para verificar parâmetros adicionais e sub dependências, e isso é o que será chamado para passar o valor ao parâmetro na sua *função de operação de rota* posteriormente.
## Parametrizar a instância { #parameterize-the-instance }
E agora, nós podemos utilizar o `__init__` para declarar os parâmetros da instância que podemos utilizar para "parametrizar" a dependência:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
Neste caso, o **FastAPI** nunca tocará ou se importará com o `__init__`, nós vamos utilizar diretamente em nosso código.
## Crie uma instância { #create-an-instance }
Nós poderíamos criar uma instância desta classe com:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
E deste modo nós podemos "parametrizar" a nossa dependência, que agora possui `"bar"` dentro dele, como o atributo `checker.fixed_content`.
## Utilize a instância como dependência { #use-the-instance-as-a-dependency }
Então, nós podemos utilizar este `checker` em um `Depends(checker)`, no lugar de `Depends(FixedContentQueryChecker)`, porque a dependência é a instância, `checker`, e não a própria classe.
E quando a dependência for resolvida, o **FastAPI** chamará este `checker` como:
```Python
checker(q="somequery")
```
...e passar o que quer que isso retorne como valor da dependência em nossa *função de operação de rota* como o parâmetro `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip | Dica
Tudo isso parece não ser natural. E pode não estar muito claro ou aparentar ser útil ainda.
Estes exemplos são intencionalmente simples, porém mostram como tudo funciona.
Nos capítulos sobre segurança, existem funções utilitárias que são implementadas desta maneira.
Se você entendeu tudo isso, você já sabe como essas funções utilitárias para segurança funcionam por debaixo dos panos.
///
## Dependências com `yield`, `HTTPException`, `except` e Tarefas em Segundo Plano { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Atenção
Muito provavelmente você não precisa desses detalhes técnicos.
Esses detalhes são úteis principalmente se você tinha uma aplicação FastAPI anterior à versão 0.121.0 e está enfrentando problemas com dependências com `yield`.
///
Dependências com `yield` evoluíram ao longo do tempo para contemplar diferentes casos de uso e corrigir alguns problemas, aqui está um resumo do que mudou.
### Dependências com `yield` e `scope` { #dependencies-with-yield-and-scope }
Na versão 0.121.0, o FastAPI adicionou suporte a `Depends(scope="function")` para dependências com `yield`.
Usando `Depends(scope="function")`, o código de saída após o `yield` é executado logo depois que a *função de operação de rota* termina, antes de a response ser enviada de volta ao cliente.
E ao usar `Depends(scope="request")` (o padrão), o código de saída após o `yield` é executado depois que a response é enviada.
Você pode ler mais na documentação em [Dependências com `yield` - Saída antecipada e `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Dependências com `yield` e `StreamingResponse`, Detalhes Técnicos { #dependencies-with-yield-and-streamingresponse-technical-details }
Antes do FastAPI 0.118.0, se você usasse uma dependência com `yield`, o código de saída (após o `yield`) rodaria depois que a *função de operação de rota* retornasse, mas logo antes de enviar a resposta.
A intenção era evitar manter recursos por mais tempo que o necessário, esperando a resposta percorrer a rede.
Essa mudança também significava que, se você retornasse um `StreamingResponse`, o código de saída da dependência com `yield` já teria sido executado.
Por exemplo, se você tivesse uma sessão de banco de dados em uma dependência com `yield`, o `StreamingResponse` não conseguiria usar essa sessão enquanto transmite dados, porque a sessão já teria sido fechada no código de saída após o `yield`.
Esse comportamento foi revertido na versão 0.118.0, para que o código de saída após o `yield` seja executado depois que a resposta for enviada.
/// info | Informação
Como você verá abaixo, isso é muito semelhante ao comportamento antes da versão 0.106.0, mas com várias melhorias e correções de bugs para casos extremos.
///
#### Casos de uso com código de saída antecipado { #use-cases-with-early-exit-code }
Há alguns casos de uso, com condições específicas, que poderiam se beneficiar do comportamento antigo de executar o código de saída das dependências com `yield` antes de enviar a resposta.
Por exemplo, imagine que você tem código que usa uma sessão de banco de dados em uma dependência com `yield` apenas para verificar um usuário, mas a sessão de banco de dados nunca é usada novamente na *função de operação de rota*, somente na dependência, e a resposta demora a ser enviada, como um `StreamingResponse` que envia dados lentamente, mas por algum motivo não usa o banco de dados.
Nesse caso, a sessão de banco de dados seria mantida até que a resposta termine de ser enviada, mas se você não a usa, então não seria necessário mantê-la.
Veja como poderia ser:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
O código de saída, o fechamento automático da `Session` em:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...seria executado depois que a resposta terminar de enviar os dados lentos:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Mas como `generate_stream()` não usa a sessão do banco de dados, não é realmente necessário manter a sessão aberta enquanto envia a resposta.
Se você tiver esse caso específico usando SQLModel (ou SQLAlchemy), você poderia fechar explicitamente a sessão depois que não precisar mais dela:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Dessa forma a sessão liberaria a conexão com o banco de dados, para que outras requisições pudessem usá-la.
Se você tiver um caso diferente que precise sair antecipadamente de uma dependência com `yield`, por favor crie uma <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">Pergunta no GitHub Discussions</a> com o seu caso específico e por que você se beneficiaria de ter o fechamento antecipado para dependências com `yield`.
Se houver casos de uso convincentes para fechamento antecipado em dependências com `yield`, considerarei adicionar uma nova forma de optar por esse fechamento antecipado.
### Dependências com `yield` e `except`, Detalhes Técnicos { #dependencies-with-yield-and-except-technical-details }
Antes do FastAPI 0.110.0, se você usasse uma dependência com `yield`, e então capturasse uma exceção com `except` nessa dependência, e você não relançasse a exceção, a exceção seria automaticamente levantada/encaminhada para quaisquer tratadores de exceção ou para o tratador de erro interno do servidor.
Isso foi alterado na versão 0.110.0 para corrigir consumo de memória não tratado decorrente de exceções encaminhadas sem um tratador (erros internos do servidor), e para torná-lo consistente com o comportamento do código Python regular.
### Tarefas em Segundo Plano e Dependências com `yield`, Detalhes Técnicos { #background-tasks-and-dependencies-with-yield-technical-details }
Antes do FastAPI 0.106.0, lançar exceções após o `yield` não era possível, o código de saída em dependências com `yield` era executado depois que a resposta era enviada, então [Tratadores de Exceções](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} já teriam sido executados.
Isso foi projetado assim principalmente para permitir o uso dos mesmos objetos "yielded" por dependências dentro de tarefas em segundo plano, porque o código de saída seria executado depois que as tarefas em segundo plano fossem concluídas.
Isso foi alterado no FastAPI 0.106.0 com a intenção de não manter recursos enquanto se espera a resposta percorrer a rede.
/// tip | Dica
Além disso, uma tarefa em segundo plano normalmente é um conjunto de lógica independente que deve ser tratado separadamente, com seus próprios recursos (por exemplo, sua própria conexão de banco de dados).
Assim, desta forma você provavelmente terá um código mais limpo.
///
Se você costumava depender desse comportamento, agora você deve criar os recursos para tarefas em segundo plano dentro da própria tarefa em segundo plano, e usar internamente apenas dados que não dependam dos recursos de dependências com `yield`.
Por exemplo, em vez de usar a mesma sessão de banco de dados, você criaria uma nova sessão de banco de dados dentro da tarefa em segundo plano, e obteria os objetos do banco de dados usando essa nova sessão. E então, em vez de passar o objeto do banco de dados como parâmetro para a função da tarefa em segundo plano, você passaria o ID desse objeto e então obteria o objeto novamente dentro da função da tarefa em segundo plano.
+99
View File
@@ -0,0 +1,99 @@
# Testes Assíncronos { #async-tests }
Você já viu como testar as suas aplicações **FastAPI** utilizando o `TestClient` que é fornecido. Até agora, você viu apenas como escrever testes síncronos, sem utilizar funções `async`.
Ser capaz de utilizar funções assíncronas em seus testes pode ser útil, por exemplo, quando você está realizando uma consulta em seu banco de dados de maneira assíncrona. Imagine que você deseja testar realizando requisições para a sua aplicação FastAPI e depois verificar que a sua aplicação inseriu corretamente as informações no banco de dados, ao utilizar uma biblioteca assíncrona para banco de dados.
Vamos ver como nós podemos fazer isso funcionar.
## pytest.mark.anyio { #pytest-mark-anyio }
Se quisermos chamar funções assíncronas em nossos testes, as nossas funções de teste precisam ser assíncronas. O AnyIO oferece um plugin bem legal para isso, que nos permite especificar que algumas das nossas funções de teste precisam ser chamadas de forma assíncrona.
## HTTPX { #httpx }
Mesmo que a sua aplicação **FastAPI** utilize funções normais com `def` no lugar de `async def`, ela ainda é uma aplicação `async` por baixo dos panos.
O `TestClient` faz algumas mágicas para invocar a aplicação FastAPI assíncrona em suas funções `def` normais, utilizando o pytest padrão. Porém a mágica não acontece mais quando nós estamos utilizando dentro de funções assíncronas. Ao executar os nossos testes de forma assíncrona, nós não podemos mais utilizar o `TestClient` dentro das nossas funções de teste.
O `TestClient` é baseado no <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, e felizmente nós podemos utilizá-lo diretamente para testar a API.
## Exemplo { #example }
Para um exemplos simples, vamos considerar uma estrutura de arquivos semelhante ao descrito em [Bigger Applications](../tutorial/bigger-applications.md){.internal-link target=_blank} e [Testing](../tutorial/testing.md){.internal-link target=_blank}:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
O arquivo `main.py` teria:
{* ../../docs_src/async_tests/main.py *}
O arquivo `test_main.py` teria os testes para para o arquivo `main.py`, ele poderia ficar assim:
{* ../../docs_src/async_tests/test_main.py *}
## Executá-lo { #run-it }
Você pode executar os seus testes normalmente via:
<div class="termy">
```console
$ pytest
---> 100%
```
</div>
## Em Detalhes { #in-detail }
O marcador `@pytest.mark.anyio` informa ao pytest que esta função de teste deve ser invocada de maneira assíncrona:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
/// tip | Dica
Note que a função de teste é `async def` agora, no lugar de apenas `def` como quando estávamos utilizando o `TestClient` anteriormente.
///
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
Isso é equivalente a:
```Python
response = client.get('/')
```
...que nós utilizamos para fazer as nossas requisições utilizando o `TestClient`.
/// tip | Dica
Note que nós estamos utilizando async/await com o novo `AsyncClient` - a requisição é assíncrona.
///
/// warning | Atenção
Se a sua aplicação depende de eventos de lifespan, o `AsyncClient` não acionará estes eventos. Para garantir que eles são acionados, utilize o `LifespanManager` do <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.
///
## Outras Chamadas de Funções Assíncronas { #other-asynchronous-function-calls }
Como a função de teste agora é assíncrona, você pode chamar (e `await`) outras funções `async` além de enviar requisições para a sua aplicação FastAPI em seus testes, exatamente como você as chamaria em qualquer outro lugar do seu código.
/// tip | Dica
Se você se deparar com um `RuntimeError: Task attached to a different loop` ao integrar funções assíncronas em seus testes (e.g. ao utilizar o <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient do MongoDB</a>) Lembre-se de instanciar objetos que precisam de um loop de eventos (*event loop*) apenas em funções assíncronas, e.g. um callback `@app.on_event("startup")`.
///
+458
View File
@@ -0,0 +1,458 @@
# Atrás de um Proxy { #behind-a-proxy }
Em muitas situações, você usaria um **proxy** como Traefik ou Nginx na frente da sua aplicação FastAPI.
Esses proxies podem lidar com certificados HTTPS e outras coisas.
## Headers Encaminhados pelo Proxy { #proxy-forwarded-headers }
Um **proxy** na frente da sua aplicação normalmente definiria alguns headers dinamicamente antes de enviar as requisições para o seu **servidor**, para informar ao servidor que a requisição foi **encaminhada** pelo proxy, informando a URL original (pública), incluindo o domínio, que está usando HTTPS, etc.
O programa do **servidor** (por exemplo, **Uvicorn** via **CLI do FastAPI**) é capaz de interpretar esses headers e então repassar essas informações para a sua aplicação.
Mas, por segurança, como o servidor não sabe que está atrás de um proxy confiável, ele não interpretará esses headers.
/// note | Detalhes Técnicos
Os headers do proxy são:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
### Ativar headers encaminhados pelo proxy { #enable-proxy-forwarded-headers }
Você pode iniciar a CLI do FastAPI com a opção de linha de comando `--forwarded-allow-ips` e informar os endereços IP que devem ser confiáveis para ler esses headers encaminhados.
Se você definir como `--forwarded-allow-ips="*"`, ele confiará em todos os IPs de entrada.
Se o seu **servidor** estiver atrás de um **proxy** confiável e somente o proxy falar com ele, isso fará com que ele aceite seja qual for o IP desse **proxy**.
<div class="termy">
```console
$ fastapi run --forwarded-allow-ips="*"
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Redirecionamentos com HTTPS { #redirects-with-https }
Por exemplo, suponha que você defina uma *operação de rota* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
Se o cliente tentar ir para `/items`, por padrão, ele seria redirecionado para `/items/`.
Mas antes de definir a opção de linha de comando `--forwarded-allow-ips`, poderia redirecionar para `http://localhost:8000/items/`.
Mas talvez sua aplicação esteja hospedada em `https://mysuperapp.com`, e o redirecionamento deveria ser para `https://mysuperapp.com/items/`.
Ao definir `--proxy-headers`, agora o FastAPI conseguirá redirecionar para o local correto. 😎
```
https://mysuperapp.com/items/
```
/// tip | Dica
Se você quiser saber mais sobre HTTPS, confira o tutorial [Sobre HTTPS](../deployment/https.md){.internal-link target=_blank}.
///
### Como funcionam os headers encaminhados pelo proxy { #how-proxy-forwarded-headers-work }
Aqui está uma representação visual de como o **proxy** adiciona headers encaminhados entre o cliente e o **servidor da aplicação**:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
O **proxy** intercepta a requisição original do cliente e adiciona os headers especiais de encaminhamento (`X-Forwarded-*`) antes de repassar a requisição para o **servidor da aplicação**.
Esses headers preservam informações sobre a requisição original que, de outra forma, seriam perdidas:
* X-Forwarded-For: o endereço IP original do cliente
* X-Forwarded-Proto: o protocolo original (`https`)
* X-Forwarded-Host: o host original (`mysuperapp.com`)
Quando a **CLI do FastAPI** é configurada com `--forwarded-allow-ips`, ela confia nesses headers e os utiliza, por exemplo, para gerar as URLs corretas em redirecionamentos.
## Proxy com um prefixo de path removido { #proxy-with-a-stripped-path-prefix }
Você pode ter um proxy que adiciona um prefixo de path à sua aplicação.
Nesses casos, você pode usar `root_path` para configurar sua aplicação.
O `root_path` é um mecanismo fornecido pela especificação ASGI (na qual o FastAPI é construído, através do Starlette).
O `root_path` é usado para lidar com esses casos específicos.
E também é usado internamente ao montar sub-aplicações.
Ter um proxy com um prefixo de path removido, nesse caso, significa que você poderia declarar um path em `/app` no seu código, mas então você adiciona uma camada no topo (o proxy) que colocaria sua aplicação **FastAPI** sob um path como `/api/v1`.
Nesse caso, o path original `/app` seria servido em `/api/v1/app`.
Embora todo o seu código esteja escrito assumindo que existe apenas `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
E o proxy estaria **"removendo"** o **prefixo de path** dinamicamente antes de transmitir a solicitação para o servidor da aplicação (provavelmente Uvicorn via CLI do FastAPI), mantendo sua aplicação convencida de que está sendo servida em `/app`, para que você não precise atualizar todo o seu código para incluir o prefixo `/api/v1`.
Até aqui, tudo funcionaria normalmente.
Mas então, quando você abre a interface de documentação integrada (o frontend), ela esperaria obter o OpenAPI schema em `/openapi.json`, em vez de `/api/v1/openapi.json`.
Então, o frontend (que roda no navegador) tentaria acessar `/openapi.json` e não conseguiria obter o OpenAPI schema.
Como temos um proxy com um prefixo de path de `/api/v1` para nossa aplicação, o frontend precisa buscar o OpenAPI schema em `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Dica
O IP `0.0.0.0` é comumente usado para significar que o programa escuta em todos os IPs disponíveis naquela máquina/servidor.
///
A interface de documentação também precisaria do OpenAPI schema para declarar que este `server` da API está localizado em `/api/v1` (atrás do proxy). Por exemplo:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Mais coisas aqui
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Mais coisas aqui
}
}
```
Neste exemplo, o "Proxy" poderia ser algo como **Traefik**. E o servidor seria algo como a CLI do FastAPI com **Uvicorn**, executando sua aplicação FastAPI.
### Fornecendo o `root_path` { #providing-the-root-path }
Para conseguir isso, você pode usar a opção de linha de comando `--root-path` assim:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Se você usar Hypercorn, ele também tem a opção `--root-path`.
/// note | Detalhes Técnicos
A especificação ASGI define um `root_path` para esse caso de uso.
E a opção de linha de comando `--root-path` fornece esse `root_path`.
///
### Verificando o `root_path` atual { #checking-the-current-root-path }
Você pode obter o `root_path` atual usado pela sua aplicação para cada solicitação, ele faz parte do dicionário `scope` (que faz parte da especificação ASGI).
Aqui estamos incluindo-o na mensagem apenas para fins de demonstração.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
Então, se você iniciar o Uvicorn com:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
A resposta seria algo como:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Configurando o `root_path` na aplicação FastAPI { #setting-the-root-path-in-the-fastapi-app }
Alternativamente, se você não tiver uma maneira de fornecer uma opção de linha de comando como `--root-path` ou equivalente, você pode definir o parâmetro `root_path` ao criar sua aplicação FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
Passar o `root_path` para `FastAPI` seria o equivalente a passar a opção de linha de comando `--root-path` para Uvicorn ou Hypercorn.
### Sobre `root_path` { #about-root-path }
Tenha em mente que o servidor (Uvicorn) não usará esse `root_path` para nada além de passá-lo para a aplicação.
Mas se você acessar com seu navegador <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> você verá a resposta normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Portanto, ele não esperará ser acessado em `http://127.0.0.1:8000/api/v1/app`.
O Uvicorn esperará que o proxy acesse o Uvicorn em `http://127.0.0.1:8000/app`, e então seria responsabilidade do proxy adicionar o prefixo extra `/api/v1` no topo.
## Sobre proxies com um prefixo de path removido { #about-proxies-with-a-stripped-path-prefix }
Tenha em mente que um proxy com prefixo de path removido é apenas uma das maneiras de configurá-lo.
Provavelmente, em muitos casos, o padrão será que o proxy não tenha um prefixo de path removido.
Em um caso como esse (sem um prefixo de path removido), o proxy escutaria em algo como `https://myawesomeapp.com`, e então, se o navegador acessar `https://myawesomeapp.com/api/v1/app` e seu servidor (por exemplo, Uvicorn) escutar em `http://127.0.0.1:8000`, o proxy (sem um prefixo de path removido) acessaria o Uvicorn no mesmo path: `http://127.0.0.1:8000/api/v1/app`.
## Testando localmente com Traefik { #testing-locally-with-traefik }
Você pode facilmente executar o experimento localmente com um prefixo de path removido usando <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Faça o download do Traefik</a>, ele é um único binário, você pode extrair o arquivo compactado e executá-lo diretamente do terminal.
Então, crie um arquivo `traefik.toml` com:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Isso diz ao Traefik para escutar na porta 9999 e usar outro arquivo `routes.toml`.
/// tip | Dica
Estamos usando a porta 9999 em vez da porta padrão HTTP 80 para que você não precise executá-lo com privilégios de administrador (`sudo`).
///
Agora crie esse outro arquivo `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Esse arquivo configura o Traefik para usar o prefixo de path `/api/v1`.
E então o Traefik redirecionará suas solicitações para seu Uvicorn rodando em `http://127.0.0.1:8000`.
Agora inicie o Traefik:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
```
</div>
E agora inicie sua aplicação, usando a opção `--root-path`:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Verifique as respostas { #check-the-responses }
Agora, se você for ao URL com a porta para o Uvicorn: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, você verá a resposta normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Dica
Perceba que, mesmo acessando em `http://127.0.0.1:8000/app`, ele mostra o `root_path` de `/api/v1`, retirado da opção `--root-path`.
///
E agora abra o URL com a porta para o Traefik, incluindo o prefixo de path: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
Obtemos a mesma resposta:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
mas desta vez no URL com o prefixo de path fornecido pelo proxy: `/api/v1`.
Claro, a ideia aqui é que todos acessariam a aplicação através do proxy, então a versão com o prefixo de path `/api/v1` é a "correta".
E a versão sem o prefixo de path (`http://127.0.0.1:8000/app`), fornecida diretamente pelo Uvicorn, seria exclusivamente para o _proxy_ (Traefik) acessá-la.
Isso demonstra como o Proxy (Traefik) usa o prefixo de path e como o servidor (Uvicorn) usa o `root_path` da opção `--root-path`.
### Verifique a interface de documentação { #check-the-docs-ui }
Mas aqui está a parte divertida. ✨
A maneira "oficial" de acessar a aplicação seria através do proxy com o prefixo de path que definimos. Então, como esperaríamos, se você tentar a interface de documentação servida diretamente pelo Uvicorn, sem o prefixo de path no URL, ela não funcionará, porque espera ser acessada através do proxy.
Você pode verificar em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image01.png">
Mas se acessarmos a interface de documentação no URL "oficial" usando o proxy com a porta `9999`, em `/api/v1/docs`, ela funciona corretamente! 🎉
Você pode verificar em <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image02.png">
Exatamente como queríamos. ✔️
Isso porque o FastAPI usa esse `root_path` para criar o `server` padrão no OpenAPI com o URL fornecido pelo `root_path`.
## Servidores adicionais { #additional-servers }
/// warning | Atenção
Este é um caso de uso mais avançado. Sinta-se à vontade para pular.
///
Por padrão, o **FastAPI** criará um `server` no OpenAPI schema com o URL para o `root_path`.
Mas você também pode fornecer outros `servers` alternativos, por exemplo, se quiser que a mesma interface de documentação interaja com ambientes de staging e produção.
Se você passar uma lista personalizada de `servers` e houver um `root_path` (porque sua API está atrás de um proxy), o **FastAPI** inserirá um "server" com esse `root_path` no início da lista.
Por exemplo:
{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
Gerará um OpenAPI schema como:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Mais coisas aqui
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Mais coisas aqui
}
}
```
/// tip | Dica
Perceba o servidor gerado automaticamente com um valor `url` de `/api/v1`, retirado do `root_path`.
///
Na interface de documentação em <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> parecerá:
<img src="/img/tutorial/behind-a-proxy/image03.png">
/// tip | Dica
A interface de documentação interagirá com o servidor que você selecionar.
///
### Desabilitar servidor automático de `root_path` { #disable-automatic-server-from-root-path }
Se você não quiser que o **FastAPI** inclua um servidor automático usando o `root_path`, você pode usar o parâmetro `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
e então ele não será incluído no OpenAPI schema.
## Montando uma sub-aplicação { #mounting-a-sub-application }
Se você precisar montar uma sub-aplicação (como descrito em [Sub-aplicações - Montagens](sub-applications.md){.internal-link target=_blank}) enquanto também usa um proxy com `root_path`, você pode fazer isso normalmente, como esperaria.
O FastAPI usará internamente o `root_path` de forma inteligente, então tudo funcionará. ✨
+312
View File
@@ -0,0 +1,312 @@
# Resposta Personalizada - HTML, Stream, File e outras { #custom-response-html-stream-file-others }
Por padrão, o **FastAPI** irá retornar respostas utilizando `JSONResponse`.
Mas você pode sobrescrever esse comportamento utilizando `Response` diretamente, como visto em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}.
Mas se você retornar uma `Response` diretamente (ou qualquer subclasse, como `JSONResponse`), os dados não serão convertidos automaticamente (mesmo que você declare um `response_model`), e a documentação não será gerada automaticamente (por exemplo, incluindo o "media type", no cabeçalho HTTP `Content-Type` como parte do esquema OpenAPI gerado).
Mas você também pode declarar a `Response` que você deseja utilizar (e.g. qualquer subclasse de `Response`), em um *decorador de operação de rota* utilizando o parâmetro `response_class`.
Os conteúdos que você retorna em sua *função de operação de rota* serão colocados dentro dessa `Response`.
E se a `Response` tiver um media type JSON (`application/json`), como é o caso com `JSONResponse` e `UJSONResponse`, os dados que você retornar serão automaticamente convertidos (e filtrados) com qualquer `response_model` do Pydantic que for declarado no decorador de operação de rota.
/// note | Nota
Se você utilizar uma classe de Resposta sem media type, o FastAPI esperará que sua resposta não tenha conteúdo, então ele não irá documentar o formato da resposta na documentação OpenAPI gerada.
///
## Utilizando `ORJSONResponse` { #use-orjsonresponse }
Por exemplo, se você precisa bastante de performance, você pode instalar e utilizar o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> e definir a resposta para ser uma `ORJSONResponse`.
Importe a classe, ou subclasse, de `Response` que você deseja utilizar e declare ela no *decorador de operação de rota*.
Para respostas grandes, retornar uma `Response` diretamente é muito mais rápido que retornar um dicionário.
Isso ocorre por que, por padrão, o FastAPI irá verificar cada item dentro do dicionário e garantir que ele seja serializável para JSON, utilizando o mesmo[Codificador Compatível com JSON](../tutorial/encoder.md){.internal-link target=_blank} explicado no tutorial. Isso permite que você retorne **objetos abstratos**, como modelos do banco de dados, por exemplo.
Mas se você tem certeza que o conteúdo que você está retornando é **serializável com JSON**, você pode passá-lo diretamente para a classe de resposta e evitar o trabalho extra que o FastAPI teria ao passar o conteúdo pelo `jsonable_encoder` antes de passar para a classe de resposta.
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
/// info | Informação
O parâmetro `response_class` também será usado para definir o "media type" da resposta.
Neste caso, o cabeçalho HTTP `Content-Type` irá ser definido como `application/json`.
E será documentado como tal no OpenAPI.
///
/// tip | Dica
A `ORJSONResponse` está disponível apenas no FastAPI, e não no Starlette.
///
## Resposta HTML { #html-response }
Para retornar uma resposta com HTML diretamente do **FastAPI**, utilize `HTMLResponse`.
* Importe `HTMLResponse`
* Passe `HTMLResponse` como o parâmetro de `response_class` do seu *decorador de operação de rota*.
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
/// info | Informação
O parâmetro `response_class` também será usado para definir o "media type" da resposta.
Neste caso, o cabeçalho HTTP `Content-Type` será definido como `text/html`.
E será documentado como tal no OpenAPI.
///
### Retornando uma `Response` { #return-a-response }
Como visto em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}, você também pode sobrescrever a resposta diretamente na sua *operação de rota*, ao retornar ela.
O mesmo exemplo de antes, retornando uma `HTMLResponse`, poderia parecer com:
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
/// warning | Atenção
Uma `Response` retornada diretamente em sua *função de operação de rota* não será documentada no OpenAPI (por exemplo, o `Content-Type` não será documentado) e não será visível na documentação interativa automática.
///
/// info | Informação
Obviamente, o cabeçalho `Content-Type`, o código de status, etc, virão do objeto `Response` que você retornou.
///
### Documentar no OpenAPI e sobrescrever `Response` { #document-in-openapi-and-override-response }
Se você deseja sobrescrever a resposta dentro de uma função, mas ao mesmo tempo documentar o "media type" no OpenAPI, você pode utilizar o parâmetro `response_class` E retornar um objeto `Response`.
A `response_class` será usada apenas para documentar o OpenAPI da *operação de rota*, mas sua `Response` será usada como foi definida.
#### Retornando uma `HTMLResponse` diretamente { #return-an-htmlresponse-directly }
Por exemplo, poderia ser algo como:
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
Neste exemplo, a função `generate_html_response()` já cria e retorna uma `Response` em vez de retornar o HTML em uma `str`.
Ao retornar o resultado chamando `generate_html_response()`, você já está retornando uma `Response` que irá sobrescrever o comportamento padrão do **FastAPI**.
Mas se você passasse uma `HTMLResponse` em `response_class` também, o **FastAPI** saberia como documentar isso no OpenAPI e na documentação interativa como um HTML com `text/html`:
<img src="/img/tutorial/custom-response/image01.png">
## Respostas disponíveis { #available-responses }
Aqui estão algumas dos tipos de resposta disponíveis.
Lembre-se que você pode utilizar `Response` para retornar qualquer outra coisa, ou até mesmo criar uma subclasse personalizada.
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import HTMLResponse`.
O **FastAPI** provê a mesma `starlette.responses` como `fastapi.responses` apenas como uma facilidade para você, desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette.
///
### `Response` { #response }
A classe principal de respostas, todas as outras respostas herdam dela.
Você pode retorná-la diretamente.
Ela aceita os seguintes parâmetros:
* `content` - Uma sequência de caracteres (`str`) ou `bytes`.
* `status_code` - Um código de status HTTP do tipo `int`.
* `headers` - Um dicionário `dict` de strings.
* `media_type` - Uma `str` informando o media type. E.g. `"text/html"`.
O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você leu acima.
### `PlainTextResponse` { #plaintextresponse }
Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Pega alguns dados e retorna uma resposta com codificação `application/json`.
É a resposta padrão utilizada no **FastAPI**, como você leu acima.
### `ORJSONResponse` { #orjsonresponse }
Uma alternativa mais rápida de resposta JSON utilizando o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, como você leu acima.
/// info | Informação
Essa resposta requer a instalação do pacote `orjson`, com o comando `pip install orjson`, por exemplo.
///
### `UJSONResponse` { #ujsonresponse }
Uma alternativa de resposta JSON utilizando a biblioteca <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
/// info | Informação
Essa resposta requer a instalação do pacote `ujson`, com o comando `pip install ujson`, por exemplo.
///
/// warning | Atenção
`ujson` é menos cauteloso que a implementação nativa do Python na forma que os casos especiais são tratados
///
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
/// tip | Dica
É possível que `ORJSONResponse` seja uma alternativa mais rápida.
///
### `RedirectResponse` { #redirectresponse }
Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecionamento Temporário) por padrão.
Você pode retornar uma `RedirectResponse` diretamente:
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
---
Ou você pode utilizá-la no parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*
Neste caso, o `status_code` utilizada será o padrão de `RedirectResponse`, que é `307`.
---
Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Recebe um gerador assíncrono ou um gerador/iterador comum e retorna o corpo da resposta de forma contínua (stream).
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
#### Utilizando `StreamingResponse` com objetos semelhantes a arquivos { #using-streamingresponse-with-file-like-objects }
Se você tiver um objeto <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">semelhante a um arquivo</a> (e.g. o objeto retornado por `open()`), você pode criar uma função geradora para iterar sobre esse objeto.
Dessa forma, você não precisa ler todo o arquivo na memória primeiro, e você pode passar essa função geradora para `StreamingResponse` e retorná-la.
Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
1. Essa é a função geradora. É definida como "função geradora" porque contém declarações `yield` nela.
2. Ao utilizar o bloco `with`, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enviada.
3. Essa declaração `yield from` informa a função para iterar sobre essa coisa nomeada de `file_like`. E então, para cada parte iterada, fornece essa parte como se viesse dessa função geradora (`iterfile`).
Então, é uma função geradora que transfere o trabalho de "geração" para alguma outra coisa interna.
Fazendo dessa forma, podemos colocá-la em um bloco `with`, e assim garantir que o objeto semelhante a um arquivo é fechado quando a função termina.
/// tip | Dica
Perceba que aqui estamos utilizando o `open()` da biblioteca padrão que não suporta `async` e `await`, e declaramos a operação de rota com o `def` básico.
///
### `FileResponse` { #fileresponse }
Envia um arquivo de forma assíncrona e contínua (stream).
Recebe um conjunto de argumentos do construtor diferente dos outros tipos de resposta:
* `path` - O caminho do arquivo que será transmitido
* `headers` - quaisquer cabeçalhos que serão incluídos, como um dicionário.
* `media_type` - Uma string com o media type. Se não for definida, o media type é inferido a partir do nome ou caminho do arquivo.
* `filename` - Se for definido, é incluído no cabeçalho `Content-Disposition`.
Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos `Content-Length`, `Last-Modified` e `ETag`, respectivamente.
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
Você também pode usar o parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
Nesse caso, você pode retornar o caminho do arquivo diretamente da sua *função de operação de rota*.
## Classe de resposta personalizada { #custom-response-class }
Você pode criar sua própria classe de resposta, herdando de `Response` e usando essa nova classe.
Por exemplo, vamos supor que você queira utilizar o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, mas com algumas configurações personalizadas que não estão incluídas na classe `ORJSONResponse`.
Vamos supor também que você queira retornar um JSON indentado e formatado, então você quer utilizar a opção `orjson.OPT_INDENT_2` do orjson.
Você poderia criar uma classe `CustomORJSONResponse`. A principal coisa a ser feita é sobrecarregar o método render da classe Response, `Response.render(content)`, que retorna o conteúdo em bytes:
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
Agora em vez de retornar:
```json
{"message": "Hello World"}
```
...essa resposta retornará:
```json
{
"message": "Hello World"
}
```
Obviamente, você provavelmente vai encontrar maneiras muito melhores de se aproveitar disso do que a formatação de JSON. 😉
## Classe de resposta padrão { #default-response-class }
Quando você criar uma instância da classe **FastAPI** ou um `APIRouter` você pode especificar qual classe de resposta utilizar por padrão.
O padrão que define isso é o `default_response_class`.
No exemplo abaixo, o **FastAPI** irá utilizar `ORJSONResponse` por padrão, em todas as *operações de rota*, em vez de `JSONResponse`.
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
/// tip | Dica
Você ainda pode substituir `response_class` em *operações de rota* como antes.
///
## Documentação adicional { #additional-documentation }
Você também pode declarar o media type e muitos outros detalhes no OpenAPI utilizando `responses`: [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
+95
View File
@@ -0,0 +1,95 @@
# Usando Dataclasses { #using-dataclasses }
FastAPI é construído em cima do **Pydantic**, e eu tenho mostrado como usar modelos Pydantic para declarar requisições e respostas.
Mas o FastAPI também suporta o uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> da mesma forma:
{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
Isso ainda é suportado graças ao **Pydantic**, pois ele tem <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">suporte interno para `dataclasses`</a>.
Então, mesmo com o código acima que não usa Pydantic explicitamente, o FastAPI está usando Pydantic para converter essas dataclasses padrão para a versão do Pydantic.
E claro, ele suporta o mesmo:
* validação de dados
* serialização de dados
* documentação de dados, etc.
Isso funciona da mesma forma que com os modelos Pydantic. E na verdade é alcançado da mesma maneira por baixo dos panos, usando Pydantic.
/// info | Informação
Lembre-se de que dataclasses não podem fazer tudo o que os modelos Pydantic podem fazer.
Então, você ainda pode precisar usar modelos Pydantic.
Mas se você tem um monte de dataclasses por aí, este é um truque legal para usá-las para alimentar uma API web usando FastAPI. 🤓
///
## Dataclasses em `response_model` { #dataclasses-in-response-model }
Você também pode usar `dataclasses` no parâmetro `response_model`:
{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
A dataclass será automaticamente convertida para uma dataclass Pydantic.
Dessa forma, seu esquema aparecerá na interface de documentação da API:
<img src="/img/tutorial/dataclasses/image01.png">
## Dataclasses em Estruturas de Dados Aninhadas { #dataclasses-in-nested-data-structures }
Você também pode combinar `dataclasses` com outras anotações de tipo para criar estruturas de dados aninhadas.
Em alguns casos, você ainda pode ter que usar a versão do Pydantic das `dataclasses`. Por exemplo, se você tiver erros com a documentação da API gerada automaticamente.
Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto:
{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
1. Ainda importamos `field` das `dataclasses` padrão.
2. `pydantic.dataclasses` é um substituto direto para `dataclasses`.
3. A dataclass `Author` inclui uma lista de dataclasses `Item`.
4. A dataclass `Author` é usada como o parâmetro `response_model`.
5. Você pode usar outras anotações de tipo padrão com dataclasses como o corpo da requisição.
Neste caso, é uma lista de dataclasses `Item`.
6. Aqui estamos retornando um dicionário que contém `items`, que é uma lista de dataclasses.
O FastAPI ainda é capaz de <abbr title="converter os dados para um formato que pode ser transmitido">serializar</abbr> os dados para JSON.
7. Aqui o `response_model` está usando uma anotação de tipo de uma lista de dataclasses `Author`.
Novamente, você pode combinar `dataclasses` com anotações de tipo padrão.
8. Note que esta *função de operação de rota* usa `def` regular em vez de `async def`.
Como sempre, no FastAPI você pode combinar `def` e `async def` conforme necessário.
Se você precisar de uma atualização sobre quando usar qual, confira a seção _"Com pressa?"_ na documentação sobre [`async` e `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
9. Esta *função de operação de rota* não está retornando dataclasses (embora pudesse), mas uma lista de dicionários com dados internos.
O FastAPI usará o parâmetro `response_model` (que inclui dataclasses) para converter a resposta.
Você pode combinar `dataclasses` com outras anotações de tipo em muitas combinações diferentes para formar estruturas de dados complexas.
Confira as dicas de anotação no código acima para ver mais detalhes específicos.
## Saiba Mais { #learn-more }
Você também pode combinar `dataclasses` com outros modelos Pydantic, herdar deles, incluí-los em seus próprios modelos, etc.
Para saber mais, confira a <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">documentação do Pydantic sobre dataclasses</a>.
## Versão { #version }
Isso está disponível desde a versão `0.67.0` do FastAPI. 🔖
+165
View File
@@ -0,0 +1,165 @@
# Eventos de lifespan { #lifespan-events }
Você pode definir a lógica (código) que deve ser executada antes da aplicação **inicializar**. Isso significa que esse código será executado **uma vez**, **antes** de a aplicação **começar a receber requisições**.
Da mesma forma, você pode definir a lógica (código) que deve ser executada quando a aplicação estiver **encerrando**. Nesse caso, esse código será executado **uma vez**, **depois** de possivelmente ter tratado **várias requisições**.
Como esse código é executado antes de a aplicação **começar** a receber requisições e logo depois que ela **termina** de lidar com as requisições, ele cobre todo o **lifespan** da aplicação (a palavra "lifespan" será importante em um segundo 😉).
Isso pode ser muito útil para configurar **recursos** que você precisa usar por toda a aplicação, e que são **compartilhados** entre as requisições e/ou que você precisa **limpar** depois. Por exemplo, um pool de conexões com o banco de dados ou o carregamento de um modelo de machine learning compartilhado.
## Caso de uso { #use-case }
Vamos começar com um exemplo de **caso de uso** e então ver como resolvê-lo com isso.
Vamos imaginar que você tem alguns **modelos de machine learning** que deseja usar para lidar com as requisições. 🤖
Os mesmos modelos são compartilhados entre as requisições, então não é um modelo por requisição, ou um por usuário, ou algo parecido.
Vamos imaginar que o carregamento do modelo pode **demorar bastante tempo**, porque ele precisa ler muitos **dados do disco**. Então você não quer fazer isso a cada requisição.
Você poderia carregá-lo no nível mais alto do módulo/arquivo, mas isso também significaria **carregar o modelo** mesmo se você estivesse executando um teste automatizado simples; então esse teste poderia ser **lento** porque teria que esperar o carregamento do modelo antes de conseguir executar uma parte independente do código.
É isso que vamos resolver: vamos carregar o modelo antes de as requisições serem tratadas, mas apenas um pouco antes de a aplicação começar a receber requisições, não enquanto o código estiver sendo carregado.
## Lifespan { #lifespan }
Você pode definir essa lógica de *inicialização* e *encerramento* usando o parâmetro `lifespan` da aplicação `FastAPI`, e um "gerenciador de contexto" (vou mostrar o que é isso em um segundo).
Vamos começar com um exemplo e depois ver em detalhes.
Nós criamos uma função assíncrona `lifespan()` com `yield` assim:
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
Aqui estamos simulando a operação de *inicialização* custosa de carregar o modelo, colocando a (falsa) função do modelo no dicionário com modelos de machine learning antes do `yield`. Esse código será executado **antes** de a aplicação **começar a receber requisições**, durante a *inicialização*.
E então, logo após o `yield`, descarregamos o modelo. Esse código será executado **depois** de a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou uma GPU.
/// tip | Dica
O `shutdown` aconteceria quando você estivesse **encerrando** a aplicação.
Talvez você precise iniciar uma nova versão, ou apenas cansou de executá-la. 🤷
///
### Função lifespan { #lifespan-function }
A primeira coisa a notar é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante a Dependências com `yield`.
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
A primeira parte da função, antes do `yield`, será executada **antes** de a aplicação iniciar.
E a parte posterior ao `yield` será executada **depois** de a aplicação ter terminado.
### Gerenciador de contexto assíncrono { #async-context-manager }
Se você verificar, a função está decorada com um `@asynccontextmanager`.
Isso converte a função em algo chamado "**gerenciador de contexto assíncrono**".
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
```Python
with open("file.txt") as file:
file.read()
```
Em versões mais recentes do Python, há também um **gerenciador de contexto assíncrono**. Você o usaria com `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Quando você cria um gerenciador de contexto ou um gerenciador de contexto assíncrono como acima, o que ele faz é: antes de entrar no bloco `with`, ele executa o código antes do `yield`, e após sair do bloco `with`, ele executa o código depois do `yield`.
No nosso exemplo de código acima, não o usamos diretamente, mas passamos para o FastAPI para que ele o use.
O parâmetro `lifespan` da aplicação `FastAPI` aceita um **gerenciador de contexto assíncrono**, então podemos passar para ele nosso novo gerenciador de contexto assíncrono `lifespan`.
{* ../../docs_src/events/tutorial003.py hl[22] *}
## Eventos alternativos (descontinuados) { #alternative-events-deprecated }
/// warning | Atenção
A forma recomendada de lidar com a *inicialização* e o *encerramento* é usando o parâmetro `lifespan` da aplicação `FastAPI`, como descrito acima. Se você fornecer um parâmetro `lifespan`, os manipuladores de eventos `startup` e `shutdown` não serão mais chamados. É tudo `lifespan` ou tudo por eventos, não ambos.
Você provavelmente pode pular esta parte.
///
Existe uma forma alternativa de definir essa lógica para ser executada durante a *inicialização* e durante o *encerramento*.
Você pode definir manipuladores de eventos (funções) que precisam ser executados antes de a aplicação iniciar ou quando a aplicação estiver encerrando.
Essas funções podem ser declaradas com `async def` ou `def` normal.
### Evento `startup` { #startup-event }
Para adicionar uma função que deve rodar antes de a aplicação iniciar, declare-a com o evento `"startup"`:
{* ../../docs_src/events/tutorial001.py hl[8] *}
Nesse caso, a função de manipulador do evento `startup` inicializará os itens do "banco de dados" (apenas um `dict`) com alguns valores.
Você pode adicionar mais de uma função de manipulador de eventos.
E sua aplicação não começará a receber requisições até que todos os manipuladores de eventos `startup` sejam concluídos.
### Evento `shutdown` { #shutdown-event }
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare-a com o evento `"shutdown"`:
{* ../../docs_src/events/tutorial002.py hl[6] *}
Aqui, a função de manipulador do evento `shutdown` escreverá uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
/// info | Informação
Na função `open()`, o `mode="a"` significa "acrescentar", então a linha será adicionada depois do que já estiver naquele arquivo, sem sobrescrever o conteúdo anterior.
///
/// tip | Dica
Perceba que, nesse caso, estamos usando a função padrão do Python `open()` que interage com um arquivo.
Então, isso envolve I/O (input/output), que requer "esperar" que as coisas sejam escritas em disco.
Mas `open()` não usa `async` e `await`.
Assim, declaramos a função de manipulador de evento com `def` padrão em vez de `async def`.
///
### `startup` e `shutdown` juntos { #startup-and-shutdown-together }
Há uma grande chance de que a lógica para sua *inicialização* e *encerramento* esteja conectada, você pode querer iniciar alguma coisa e então finalizá-la, adquirir um recurso e então liberá-lo, etc.
Fazer isso em funções separadas que não compartilham lógica ou variáveis entre si é mais difícil, pois você precisaria armazenar valores em variáveis globais ou truques semelhantes.
Por causa disso, agora é recomendado usar o `lifespan`, como explicado acima.
## Detalhes técnicos { #technical-details }
Apenas um detalhe técnico para nerds curiosos. 🤓
Por baixo, na especificação técnica do ASGI, isso é parte do <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Protocolo Lifespan</a>, e define eventos chamados `startup` e `shutdown`.
/// info | Informação
Você pode ler mais sobre os manipuladores de `lifespan` do Starlette na <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Documentação do Lifespan do Starlette</a>.
Incluindo como lidar com estado do lifespan que pode ser usado em outras áreas do seu código.
///
## Sub Aplicações { #sub-applications }
🚨 Tenha em mente que esses eventos de lifespan (inicialização e encerramento) serão executados apenas para a aplicação principal, não para [Sub Aplicações - Montagem](sub-applications.md){.internal-link target=_blank}.
+208
View File
@@ -0,0 +1,208 @@
# Gerando SDKs { #generating-sdks }
Como o **FastAPI** é baseado na especificação **OpenAPI**, suas APIs podem ser descritas em um formato padrão que muitas ferramentas entendem.
Isso facilita gerar **documentação** atualizada, bibliotecas clientes (<abbr title="Software Development Kits Kits de Desenvolvimento de Software">**SDKs**</abbr>) em várias linguagens e **testes** ou **fluxos de trabalho de automação** que permanecem em sincronia com o seu código.
Neste guia, você aprenderá como gerar um **SDK em TypeScript** para o seu backend FastAPI.
## Geradores de SDK de código aberto { #open-source-sdk-generators }
Uma opção versátil é o <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, que suporta **muitas linguagens de programação** e pode gerar SDKs a partir da sua especificação OpenAPI.
Para **clientes TypeScript**, o <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> é uma solução feita sob medida, oferecendo uma experiência otimizada para o ecossistema TypeScript.
Você pode descobrir mais geradores de SDK em <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
/// tip | Dica
O FastAPI gera automaticamente especificações **OpenAPI 3.1**, então qualquer ferramenta que você usar deve suportar essa versão.
///
## Geradores de SDK dos patrocinadores do FastAPI { #sdk-generators-from-fastapi-sponsors }
Esta seção destaca soluções **financiadas por investimento** e **com suporte de empresas** que patrocinam o FastAPI. Esses produtos fornecem **funcionalidades adicionais** e **integrações** além de SDKs gerados com alta qualidade.
Ao ✨ [**patrocinar o FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, essas empresas ajudam a garantir que o framework e seu **ecossistema** continuem saudáveis e **sustentáveis**.
O patrocínio também demonstra um forte compromisso com a **comunidade** FastAPI (você), mostrando que elas se importam não apenas em oferecer um **ótimo serviço**, mas também em apoiar um **framework robusto e próspero**, o FastAPI. 🙇
Por exemplo, você pode querer experimentar:
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainless.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>
Algumas dessas soluções também podem ser open source ou oferecer planos gratuitos, para que você possa testá-las sem compromisso financeiro. Outros geradores comerciais de SDK estão disponíveis e podem ser encontrados online. 🤓
## Crie um SDK em TypeScript { #create-a-typescript-sdk }
Vamos começar com uma aplicação 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 { #api-docs }
Se você for para `/docs`, 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 **schema OpenAPI** do app e são mostradas na documentação da API.
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**.
### Hey API { #hey-api }
Depois que tivermos uma aplicação FastAPI com os modelos, podemos usar o Hey API para gerar um cliente TypeScript. A forma mais rápida é via npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Isso gerará um SDK TypeScript em `./src/client`.
Você pode aprender como <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">instalar `@hey-api/openapi-ts`</a> e ler sobre o <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">resultado gerado</a> no site deles.
### Usando o SDK { #using-the-sdk }
Agora você pode importar e usar o código do cliente. 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 na aplicação 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">
## Aplicação FastAPI com Tags { #fastapi-app-with-tags }
Em muitos casos, sua aplicação 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] *}
### Gere um cliente TypeScript com Tags { #generate-a-typescript-client-with-tags }
Se você gerar um cliente para uma aplicação 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 { #client-method-names }
Agora os nomes dos métodos gerados como `createItemItemsPost` não parecem muito “limpos”:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...isso ocorre porque o gerador de clientes usa o **ID de operação** interno do OpenAPI para cada *operação de rota*.
O OpenAPI exige que cada ID de operação seja único em todas as *operações de rota*, então o FastAPI usa o **nome da função**, o **path** e o **método/operação HTTP** para gerar esse ID de operação, porque dessa forma ele pode garantir que os IDs de operação sejam únicos.
Mas eu vou te mostrar como melhorar isso a seguir. 🤓
## IDs de operação personalizados e nomes de métodos melhores { #custom-operation-ids-and-better-method-names }
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 de 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 exclusivos { #custom-generate-unique-id-function }
O FastAPI usa um **ID exclusivo** para cada *operação de rota*, ele é usado para o **ID de 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 retorna 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] *}
### Gere um cliente TypeScript com IDs de operação personalizados { #generate-a-typescript-client-with-custom-operation-ids }
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 path da URL e da operação HTTP.
### Pré-processar a especificação OpenAPI para o gerador de clientes { #preprocess-the-openapi-specification-for-the-client-generator }
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 agradáveis e **limpos**.
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.
### Gere um cliente TypeScript com o OpenAPI pré-processado { #generate-a-typescript-client-with-the-preprocessed-openapi }
Como o resultado final está agora em um arquivo `openapi.json`, você precisa atualizar o local de entrada:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Depois de gerar o novo cliente, você terá 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 { #benefits }
Ao usar os clientes gerados automaticamente, você terá **preenchimento automático** para:
* Métodos.
* Corpos de requisições, parâmetros de query, etc.
* Corpos de respostas.
Você também terá **erros em linha** para tudo.
E sempre que você atualizar o código do backend e **regenerar** o frontend, ele terá quaisquer novas *operações de rota* disponíveis como métodos, as antigas removidas, e qualquer outra alteração será refletida no código gerado. 🤓
Isso também significa que, se algo mudou, será **refletido** no código do cliente automaticamente. E se você **construir** o cliente, ele falhará caso haja qualquer **incompatibilidade** nos dados usados.
Assim, você **detectará muitos 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. ✨
+21
View File
@@ -0,0 +1,21 @@
# Guia de Usuário Avançado { #advanced-user-guide }
## Recursos Adicionais { #additional-features }
O [Tutorial - Guia de Usuário](../tutorial/index.md){.internal-link target=_blank} deve ser o suficiente para dar a você um tour por todos os principais recursos do **FastAPI**.
Nas próximas seções você verá outras opções, configurações, e recursos adicionais.
/// tip | Dica
As próximas seções **não são necessáriamente "avançadas"**
E é possível que para seu caso de uso, a solução esteja em uma delas.
///
## Leia o Tutorial primeiro { #read-the-tutorial-first }
Você ainda pode usar a maior parte dos recursos no **FastAPI** com o conhecimento do [Tutorial - Guia de Usuário](../tutorial/index.md){.internal-link target=_blank}.
E as próximas seções assumem que você já leu ele, e que você conhece suas ideias principais.
+97
View File
@@ -0,0 +1,97 @@
# Middleware Avançado { #advanced-middleware }
No tutorial principal você leu como adicionar [Middleware Personalizado](../tutorial/middleware.md){.internal-link target=_blank} à sua aplicação.
E então você também leu como lidar com [CORS com o `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
Nesta seção, veremos como usar outros middlewares.
## Adicionando middlewares ASGI { #adding-asgi-middlewares }
Como o **FastAPI** é baseado no Starlette e implementa a especificação <abbr title="Asynchronous Server Gateway Interface Interface de Gateway de Servidor Assíncrona">ASGI</abbr>, você pode usar qualquer middleware ASGI.
O middleware não precisa ser feito para o FastAPI ou Starlette para funcionar, desde que siga a especificação ASGI.
No geral, os middlewares ASGI são classes que esperam receber um aplicativo ASGI como o primeiro argumento.
Então, na documentação de middlewares ASGI de terceiros, eles provavelmente dirão para você fazer algo como:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Mas, o FastAPI (na verdade, o Starlette) fornece uma maneira mais simples de fazer isso que garante que os middlewares internos lidem com erros do servidor e que os manipuladores de exceções personalizados funcionem corretamente.
Para isso, você usa `app.add_middleware()` (como no exemplo para CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` recebe uma classe de middleware como o primeiro argumento e quaisquer argumentos adicionais a serem passados para o middleware.
## Middlewares Integrados { #integrated-middlewares }
**FastAPI** inclui vários middlewares para casos de uso comuns, veremos a seguir como usá-los.
/// note | Detalhes Técnicos
Para os próximos exemplos, você também poderia usar `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vem diretamente do Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Garante que todas as requisições devem ser `https` ou `wss`.
Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
Os seguintes argumentos são suportados:
* `allowed_hosts` - Uma lista de nomes de domínio que são permitidos como nomes de host. Domínios com coringa, como `*.example.com`, são suportados para corresponder a subdomínios. Para permitir qualquer nome de host, use `allowed_hosts=["*"]` ou omita o middleware.
* `www_redirect` - Se definido como True, as requisições para versões sem www dos hosts permitidos serão redirecionadas para suas versões com www. O padrão é `True`.
Se uma requisição recebida não for validada corretamente, uma resposta `400` será enviada.
## `GZipMiddleware` { #gzipmiddleware }
Gerencia respostas GZip para qualquer requisição que inclua `"gzip"` no cabeçalho `Accept-Encoding`.
O middleware lidará com respostas padrão e de streaming.
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
Os seguintes argumentos são suportados:
* `minimum_size` - Não comprima respostas menores que este tamanho mínimo em bytes. O padrão é `500`.
* `compresslevel` - Usado durante a compressão GZip. É um inteiro variando de 1 a 9. O padrão é `9`. Um valor menor resulta em uma compressão mais rápida, mas em arquivos maiores, enquanto um valor maior resulta em uma compressão mais lenta, mas em arquivos menores.
## Outros middlewares { #other-middlewares }
Há muitos outros middlewares ASGI.
Por exemplo:
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn's `ProxyHeadersMiddleware`</a>
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
Para checar outros middlewares disponíveis, confira <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Documentação de Middlewares do Starlette</a> e a <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">Lista Incrível do ASGI</a>.
+186
View File
@@ -0,0 +1,186 @@
# Callbacks na OpenAPI { #openapi-callbacks }
Você poderia criar uma API com uma *operação de rota* que poderia acionar uma solicitação a uma *API externa* criada por outra pessoa (provavelmente o mesmo desenvolvedor que estaria *usando* sua API).
O processo que acontece quando sua aplicação de API chama a *API externa* é chamado de "callback". Porque o software que o desenvolvedor externo escreveu envia uma solicitação para sua API e então sua API *chama de volta*, enviando uma solicitação para uma *API externa* (que provavelmente foi criada pelo mesmo desenvolvedor).
Nesse caso, você poderia querer documentar como essa API externa *deveria* ser. Que *operação de rota* ela deveria ter, que corpo ela deveria esperar, que resposta ela deveria retornar, etc.
## Um aplicativo com callbacks { #an-app-with-callbacks }
Vamos ver tudo isso com um exemplo.
Imagine que você desenvolve um aplicativo que permite criar faturas.
Essas faturas terão um `id`, `title` (opcional), `customer` e `total`.
O usuário da sua API (um desenvolvedor externo) criará uma fatura na sua API com uma solicitação POST.
Então sua API irá (vamos imaginar):
* Enviar a fatura para algum cliente do desenvolvedor externo.
* Coletar o dinheiro.
* Enviar a notificação de volta para o usuário da API (o desenvolvedor externo).
* Isso será feito enviando uma solicitação POST (de *sua API*) para alguma *API externa* fornecida por esse desenvolvedor externo (este é o "callback").
## O aplicativo **FastAPI** normal { #the-normal-fastapi-app }
Vamos primeiro ver como o aplicativo da API normal se pareceria antes de adicionar o callback.
Ele terá uma *operação de rota* que receberá um corpo `Invoice`, e um parâmetro de consulta `callback_url` que conterá a URL para o callback.
Essa parte é bastante normal, a maior parte do código provavelmente já é familiar para você:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
/// tip | Dica
O parâmetro de consulta `callback_url` usa um tipo Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>.
///
A única novidade é o `callbacks=invoices_callback_router.routes` como argumento do decorador da *operação de rota*. Veremos o que é isso a seguir.
## Documentando o callback { #documenting-the-callback }
O código real do callback dependerá muito da sua própria aplicação de API.
E provavelmente variará muito de um aplicativo para o outro.
Poderia ser apenas uma ou duas linhas de código, como:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Mas possivelmente a parte mais importante do callback é garantir que o usuário da sua API (o desenvolvedor externo) implemente a *API externa* corretamente, de acordo com os dados que *sua API* vai enviar no corpo da solicitação do callback, etc.
Então, o que faremos a seguir é adicionar o código para documentar como essa *API externa* deve ser para receber o callback de *sua API*.
A documentação aparecerá na Swagger UI em `/docs` na sua API, e permitirá que os desenvolvedores externos saibam como construir a *API externa*.
Esse exemplo não implementa o callback em si (que poderia ser apenas uma linha de código), apenas a parte da documentação.
/// tip | Dica
O callback real é apenas uma solicitação HTTP.
Ao implementar o callback por conta própria, você pode usar algo como <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> ou <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
///
## Escreva o código de documentação do callback { #write-the-callback-documentation-code }
Esse código não será executado em seu aplicativo, nós só precisamos dele para *documentar* como essa *API externa* deveria ser.
Mas, você já sabe como criar facilmente documentação automática para uma API com o **FastAPI**.
Então vamos usar esse mesmo conhecimento para documentar como a *API externa* deveria ser... criando as *operações de rota* que a *API externa* deveria implementar (as que sua API irá chamar).
/// tip | Dica
Ao escrever o código para documentar um callback, pode ser útil imaginar que você é aquele *desenvolvedor externo*. E que você está atualmente implementando a *API externa*, não *sua API*.
Adotar temporariamente esse ponto de vista (do *desenvolvedor externo*) pode ajudar a perceber mais facilmente onde colocar os parâmetros, o modelo Pydantic para o corpo, para a resposta, etc. para essa *API externa*.
///
### Crie um `APIRouter` de callback { #create-a-callback-apirouter }
Primeiro crie um novo `APIRouter` que conterá um ou mais callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
### Crie a *operação de rota* do callback { #create-the-callback-path-operation }
Para criar a *operação de rota* do callback, use o mesmo `APIRouter` que você criou acima.
Ela deve parecer exatamente como uma *operação de rota* normal do FastAPI:
* Ela provavelmente deveria ter uma declaração do corpo que deveria receber, por exemplo, `body: InvoiceEvent`.
* E também poderia ter uma declaração da resposta que deveria retornar, por exemplo, `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
Há 2 diferenças principais de uma *operação de rota* normal:
* Ela não necessita ter nenhum código real, porque seu aplicativo nunca chamará esse código. Ele é usado apenas para documentar a *API externa*. Então, a função poderia ter apenas `pass`.
* O *path* pode conter uma <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expressão OpenAPI 3</a> (veja mais abaixo) em que pode usar variáveis com parâmetros e partes da solicitação original enviada para *sua API*.
### A expressão do path do callback { #the-callback-path-expression }
O *path* do callback pode ter uma <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expressão OpenAPI 3</a> que pode conter partes da solicitação original enviada para *sua API*.
Nesse caso, é a `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Então, se o usuário da sua API (o desenvolvedor externo) enviar uma solicitação para *sua API* para:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
com um corpo JSON de:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
então *sua API* processará a fatura e, em algum momento posterior, enviará uma solicitação de callback para o `callback_url` (a *API externa*):
```
https://www.external.org/events/invoices/2expen51ve
```
com um corpo JSON contendo algo como:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
e esperaria uma resposta daquela *API externa* com um corpo JSON como:
```JSON
{
"ok": true
}
```
/// tip | Dica
Perceba como a URL de callback usada contém a URL recebida como um parâmetro de consulta em `callback_url` (`https://www.external.org/events`) e também o `id` da fatura de dentro do corpo JSON (`2expen51ve`).
///
### Adicione o roteador de callback { #add-the-callback-router }
Nesse ponto você tem a(s) *operação(ões) de rota de callback* necessária(s) (a(s) que o *desenvolvedor externo* deveria implementar na *API externa*) no roteador de callback que você criou acima.
Agora use o parâmetro `callbacks` no decorador da *operação de rota da sua API* para passar o atributo `.routes` (que é na verdade apenas uma `list` de rotas/*operações de path*) do roteador de callback:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
/// tip | Dica
Perceba que você não está passando o roteador em si (`invoices_callback_router`) para `callback=`, mas o atributo `.routes`, como em `invoices_callback_router.routes`.
///
### Verifique a documentação { #check-the-docs }
Agora você pode iniciar seu aplicativo e ir para <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá sua documentação incluindo uma seção "Callbacks" para sua *operação de rota* que mostra como a *API externa* deveria ser:
<img src="/img/tutorial/openapi-callbacks/image01.png">
+55
View File
@@ -0,0 +1,55 @@
# Webhooks OpenAPI { #openapi-webhooks }
Existem situações onde você deseja informar os **usuários** da sua API que a sua aplicação pode chamar a aplicação *deles* (enviando uma requisição) com alguns dados, normalmente para **notificar** algum tipo de **evento**.
Isso significa que no lugar do processo normal de seus usuários enviarem requisições para a sua API, é a **sua API** (ou sua aplicação) que poderia **enviar requisições para o sistema deles** (para a API deles, a aplicação deles).
Isso normalmente é chamado de **webhook**.
## Etapas dos webhooks { #webhooks-steps }
Normalmente, o processo é que **você define** em seu código qual é a mensagem que você irá mandar, o **corpo da sua requisição**.
Você também define de alguma maneira em quais **momentos** a sua aplicação mandará essas requisições ou eventos.
E os **seus usuários** definem de alguma forma (em algum painel por exemplo) a **URL** que a sua aplicação deve enviar essas requisições.
Toda a **lógica** sobre como cadastrar as URLs para os webhooks e o código para enviar de fato as requisições cabe a você definir. Você escreve da maneira que você desejar no **seu próprio código**.
## Documentando webhooks com o FastAPI e OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
Com o **FastAPI**, utilizando o OpenAPI, você pode definir os nomes destes webhooks, os tipos das operações HTTP que a sua aplicação pode enviar (e.g. `POST`, `PUT`, etc.) e os **corpos** da requisição que a sua aplicação enviaria.
Isto pode facilitar bastante para os seus usuários **implementarem as APIs deles** para receber as requisições dos seus **webhooks**, eles podem inclusive ser capazes de gerar parte do código da API deles.
/// info | Informação
Webhooks estão disponíveis a partir do OpenAPI 3.1.0, e possui suporte do FastAPI a partir da versão `0.99.0`.
///
## Uma aplicação com webhooks { #an-app-with-webhooks }
Quando você cria uma aplicação com o **FastAPI**, existe um atributo chamado `webhooks`, que você utilizar para defini-los da mesma maneira que você definiria as suas **operações de rotas**, utilizando por exemplo `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Os webhooks que você define aparecerão no esquema do **OpenAPI** e na **página de documentação** gerada automaticamente.
/// info | Informação
O objeto `app.webhooks` é na verdade apenas um `APIRouter`, o mesmo tipo que você utilizaria ao estruturar a sua aplicação com diversos arquivos.
///
Note que utilizando webhooks você não está de fato declarando um *path* (como `/items/`), o texto que informa é apenas um **identificador** do webhook (o nome do evento), por exemplo em `@app.webhooks.post("new-subscription")`, o nome do webhook é `new-subscription`.
Isto porque espera-se que os **seus usuários** definam o verdadeiro **URL path** onde eles desejam receber a requisição do webhook de algum outra maneira. (e.g. um painel).
### Confira a documentação { #check-the-docs }
Agora você pode iniciar a sua aplicação e ir até <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá que a sua documentação possui as *operações de rota* normais e agora também possui alguns **webhooks**:
<img src="/img/tutorial/openapi-webhooks/image01.png">
@@ -0,0 +1,204 @@
# Configuração Avançada da Operação de Rota { #path-operation-advanced-configuration }
## operationId do OpenAPI { #openapi-operationid }
/// warning | Atenção
Se você não é um "especialista" no OpenAPI, você provavelmente não precisa disso.
///
Você pode definir o `operationId` do OpenAPI que será utilizado na sua *operação de rota* com o parâmetro `operation_id`.
Você precisa ter certeza que ele é único para cada operação.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### Utilizando o nome da *função de operação de rota* como o operationId { #using-the-path-operation-function-name-as-the-operationid }
Se você quiser utilizar o nome das funções da sua API como `operationId`s, você pode iterar sobre todos esses nomes e sobrescrever o `operationId` em cada *operação de rota* utilizando o `APIRoute.name` dela.
Você deve fazer isso depois de adicionar todas as suas *operações de rota*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
/// tip | Dica
Se você chamar `app.openapi()` manualmente, os `operationId`s devem ser atualizados antes dessa chamada.
///
/// warning | Atenção
Se você fizer isso, você tem que ter certeza de que cada uma das suas *funções de operação de rota* tem um nome único.
Mesmo que elas estejam em módulos (arquivos Python) diferentes.
///
## Excluir do OpenAPI { #exclude-from-openapi }
Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequência, dos sistemas de documentação automáticos), utilize o parâmetro `include_in_schema` e defina ele como `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## Descrição avançada a partir de docstring { #advanced-description-from-docstring }
Você pode limitar as linhas utilizadas a partir de uma docstring de uma *função de operação de rota* para o OpenAPI.
Adicionar um `\f` (um caractere de escape para alimentação de formulário) faz com que o **FastAPI** restrinja a saída utilizada pelo OpenAPI até esse ponto.
Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto do texto.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
## Respostas Adicionais { #additional-responses }
Você provavelmente já viu como declarar o `response_model` e `status_code` para uma *operação de rota*.
Isso define os metadados sobre a resposta principal da *operação de rota*.
Você também pode declarar respostas adicionais, com seus modelos, códigos de status, etc.
Existe um capítulo inteiro da nossa documentação sobre isso, você pode ler em [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
## Extras do OpenAPI { #openapi-extra }
Quando você declara uma *operação de rota* na sua aplicação, o **FastAPI** irá gerar os metadados relevantes da *operação de rota* automaticamente para serem incluídos no esquema do OpenAPI.
/// note | Detalhes Técnicos
Na especificação do OpenAPI, isso é chamado de um <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objeto de Operação</a>.
///
Ele possui toda a informação sobre a *operação de rota* e é usado para gerar a documentação automaticamente.
Ele inclui os atributos `tags`, `parameters`, `requestBody`, `responses`, etc.
Esse esquema específico para uma *operação de rota* normalmente é gerado automaticamente pelo **FastAPI**, mas você também pode estender ele.
/// tip | Dica
Esse é um ponto de extensão de baixo nível.
Caso você só precise declarar respostas adicionais, uma forma conveniente de fazer isso é com [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
///
Você pode estender o esquema do OpenAPI para uma *operação de rota* utilizando o parâmetro `openapi_extra`.
### Extensões do OpenAPI { #openapi-extensions }
Esse parâmetro `openapi_extra` pode ser útil, por exemplo, para declarar [Extensões do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
Se você abrir os documentos criados automaticamente para a API, sua extensão aparecerá no final da *operação de rota* específica.
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
E se você olhar o esquema OpenAPI resultante (na rota `/openapi.json` da sua API), você verá que a sua extensão também faz parte da *operação de rota* específica:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Esquema de *operação de rota* do OpenAPI personalizado { #custom-openapi-path-operation-schema }
O dicionário em `openapi_extra` vai ter todos os seus níveis mesclados dentro do esquema OpenAPI gerado automaticamente para a *operação de rota*.
Então, você pode adicionar dados extras para o esquema gerado automaticamente.
Por exemplo, você poderia optar por ler e validar a requisição com seu próprio código, sem utilizar funcionalidades automatizadas do FastAPI com o Pydantic, mas você ainda pode quere definir a requisição no esquema OpenAPI.
Você pode fazer isso com `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo <abbr title="convertido de um formato plano, como bytes, para objetos Python">analisado</abbr> como JSON, ele é lido diretamente como `bytes` e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma.
De toda forma, nós podemos declarar o esquema esperado para o corpo da requisição.
### Tipo de conteúdo do OpenAPI personalizado { #custom-openapi-content-type }
Utilizando esse mesmo truque, você pode utilizar um modelo Pydantic para definir o JSON Schema que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*.
E você pode fazer isso até mesmo quando os dados da requisição não seguem o formato JSON.
Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o JSON Schema dos modelos Pydantic nem a validação automática do JSON. Na verdade, estamos declarando o tipo do conteúdo da requisição como YAML, em vez de JSON:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
////
/// info | Informação
Na versão 1 do Pydantic, o método para obter o JSON Schema de um modelo é `Item.schema()`, na versão 2 do Pydantic, o método é `Item.model_json_schema()`.
///
Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um JSON Schema manualmente para os dados que queremos receber no formato YAML.
Então utilizamos a requisição diretamente, e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o corpo da requisição como JSON.
E então no nosso código, nós analisamos o conteúdo YAML diretamente, e estamos utilizando o mesmo modelo Pydantic para validar o conteúdo YAML:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
////
/// info | Informação
Na versão 1 do Pydantic, o método para analisar e validar um objeto era `Item.parse_obj()`, na versão 2 do Pydantic, o método é chamado de `Item.model_validate()`.
///
/// tip | Dica
Aqui reutilizamos o mesmo modelo do Pydantic.
Mas da mesma forma, nós poderíamos ter validado de alguma outra forma.
///
@@ -0,0 +1,31 @@
# Retorno - Altere o Código de Status { #response-change-status-code }
Você provavelmente leu anteriormente que você pode definir um [Código de Status do Retorno](../tutorial/response-status-code.md){.internal-link target=_blank} padrão.
Porém em alguns casos você precisa retornar um código de status diferente do padrão.
## Caso de uso { #use-case }
Por exemplo, imagine que você deseja retornar um código de status HTTP de "OK" `200` por padrão.
Mas se o dado não existir, você quer criá-lo e retornar um código de status HTTP de "CREATED" `201`.
Mas você ainda quer ser capaz de filtrar e converter o dado que você retornará com um `response_model`.
Para estes casos, você pode utilizar um parâmetro `Response`.
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` em sua *função de operação de rota* (assim como você pode fazer para cookies e headers).
E então você pode definir o `status_code` neste objeto de retorno temporal.
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
E então você pode retornar qualquer objeto que você precise, como você faria normalmente (um `dict`, um modelo de banco de dados, etc.).
E se você declarar um `response_model`, ele ainda será utilizado para filtrar e converter o objeto que você retornou.
O **FastAPI** utilizará este retorno *temporal* para extrair o código de status (e também cookies e headers), e irá colocá-los no retorno final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` nas dependências, e definir o código de status nelas. Mas lembre-se que o último que for definido é o que prevalecerá.
+51
View File
@@ -0,0 +1,51 @@
# Cookies de Resposta { #response-cookies }
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota*.
E então você pode definir cookies nesse objeto de resposta *temporário*.
{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
Em seguida, você pode retornar qualquer objeto que precise, como normalmente faria (um `dict`, um modelo de banco de dados, etc).
E se você declarou um `response_model`, ele ainda será usado para filtrar e converter o objeto que você retornou.
**FastAPI** usará essa resposta *temporária* para extrair os cookies (também os cabeçalhos e código de status) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` em dependências e definir cookies (e cabeçalhos) nelas.
## Retorne uma `Response` diretamente { #return-a-response-directly }
Você também pode criar cookies ao retornar uma `Response` diretamente no seu código.
Para fazer isso, você pode criar uma resposta como descrito em [Retorne uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}.
Então, defina os cookies nela e a retorne:
{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
/// tip | Dica
Lembre-se de que se você retornar uma resposta diretamente em vez de usar o parâmetro `Response`, FastAPI a retornará diretamente.
Portanto, você terá que garantir que seus dados sejam do tipo correto. E.g. será compatível com JSON se você estiver retornando um `JSONResponse`.
E também que você não esteja enviando nenhum dado que deveria ter sido filtrado por um `response_model`.
///
### Mais informações { #more-info }
/// note | Detalhes Técnicos
Você também poderia usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fornece as mesmas `starlette.responses` em `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
E como o `Response` pode ser usado frequentemente para definir cabeçalhos e cookies, o **FastAPI** também o fornece em `fastapi.Response`.
///
Para ver todos os parâmetros e opções disponíveis, verifique a <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">documentação no Starlette</a>.
@@ -0,0 +1,65 @@
# Retornando uma Resposta Diretamente { #return-a-response-directly }
Quando você cria uma *operação de rota* no **FastAPI** você pode retornar qualquer dado nela: um dicionário (`dict`), uma lista (`list`), um modelo do Pydantic ou do seu banco de dados, etc.
Por padrão, o **FastAPI** irá converter automaticamente o valor do retorno para JSON utilizando o `jsonable_encoder` explicado em [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
Então, por baixo dos panos, ele incluiria esses dados compatíveis com JSON (e.g. um `dict`) dentro de uma `JSONResponse` que é utilizada para enviar uma resposta para o cliente.
Mas você pode retornar a `JSONResponse` diretamente nas suas *operações de rota*.
Pode ser útil para retornar cabeçalhos e cookies personalizados, por exemplo.
## Retornando uma `Response` { #return-a-response }
Na verdade, você pode retornar qualquer `Response` ou subclasse dela.
/// tip | Dica
A própria `JSONResponse` é uma subclasse de `Response`.
///
E quando você retorna uma `Response`, o **FastAPI** vai repassá-la diretamente.
Ele não vai fazer conversões de dados com modelos do Pydantic, não irá converter a tipagem de nenhum conteúdo, etc.
Isso te dá bastante flexibilidade. Você pode retornar qualquer tipo de dado, sobrescrever qualquer declaração e validação nos dados, etc.
## Utilizando o `jsonable_encoder` em uma `Response` { #using-the-jsonable-encoder-in-a-response }
Como o **FastAPI** não realiza nenhuma mudança na `Response` que você retorna, você precisa garantir que o conteúdo dela está pronto para uso.
Por exemplo, você não pode colocar um modelo do Pydantic em uma `JSONResponse` sem antes convertê-lo em um `dict` com todos os tipos de dados (como `datetime`, `UUID`, etc) convertidos para tipos compatíveis com JSON.
Para esses casos, você pode usar o `jsonable_encoder` para converter seus dados antes de repassá-los para a resposta:
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import JSONResponse`.
**FastAPI** utiliza a mesma `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas maior parte das respostas disponíveis vem diretamente do Starlette.
///
## Retornando uma `Response` personalizada { #returning-a-custom-response }
O exemplo acima mostra todas as partes que você precisa, mas ainda não é muito útil, já que você poderia ter retornado o `item` diretamente, e o **FastAPI** colocaria em uma `JSONResponse` para você, convertendo em um `dict`, etc. Tudo isso por padrão.
Agora, vamos ver como você pode usar isso para retornar uma resposta personalizada.
Vamos dizer que você quer retornar uma resposta <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>.
Você pode colocar o seu conteúdo XML em uma string, colocar em uma `Response`, e retorná-lo:
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
## Notas { #notes }
Quando você retorna uma `Response` diretamente os dados não são validados, convertidos (serializados) ou documentados automaticamente.
Mas você ainda pode documentar como descrito em [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
Você pode ver nas próximas seções como usar/declarar essas `Responses` customizadas enquanto mantém a conversão e documentação automática dos dados.
+41
View File
@@ -0,0 +1,41 @@
# Cabeçalhos de resposta { #response-headers }
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota* (assim como você pode fazer para cookies).
Então você pode definir os cabeçalhos nesse objeto de resposta *temporário*.
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
Em seguida você pode retornar qualquer objeto que precisar, da maneira que faria normalmente (um `dict`, um modelo de banco de dados, etc.).
Se você declarou um `response_model`, ele ainda será utilizado para filtrar e converter o objeto que você retornou.
**FastAPI** usará essa resposta *temporária* para extrair os cabeçalhos (cookies e código de status também) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` em dependências e definir cabeçalhos (e cookies) nelas.
## Retorne uma `Response` diretamente { #return-a-response-directly }
Você também pode adicionar cabeçalhos quando retornar uma `Response` diretamente.
Crie uma resposta conforme descrito em [Retornar uma resposta diretamente](response-directly.md){.internal-link target=_blank} e passe os cabeçalhos como um parâmetro adicional:
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
/// note | Detalhes Técnicos
Você também pode usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fornece as mesmas `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
E como a `Response` pode ser usada frequentemente para definir cabeçalhos e cookies, **FastAPI** também a fornece em `fastapi.Response`.
///
## Cabeçalhos personalizados { #custom-headers }
Tenha em mente que cabeçalhos personalizados proprietários podem ser adicionados <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando o prefixo `X-`</a>.
Porém, se voce tiver cabeçalhos personalizados que deseja que um cliente no navegador possa ver, você precisa adicioná-los às suas configurações de CORS (saiba mais em [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando o parâmetro `expose_headers` descrito na <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">documentação de CORS do Starlette</a>.
@@ -0,0 +1,107 @@
# HTTP Basic Auth { #http-basic-auth }
Para os casos mais simples, você pode utilizar o HTTP Basic Auth.
No HTTP Basic Auth, a aplicação espera um cabeçalho que contém um usuário e uma senha.
Caso ela não receba, ela retorna um erro HTTP 401 "Unauthorized".
E retorna um cabeçalho `WWW-Authenticate` com o valor `Basic`, e um parâmetro opcional `realm`.
Isso sinaliza ao navegador para mostrar o prompt integrado para um usuário e senha.
Então, quando você digitar o usuário e senha, o navegador os envia automaticamente no cabeçalho.
## HTTP Basic Auth Simples { #simple-http-basic-auth }
* Importe `HTTPBasic` e `HTTPBasicCredentials`.
* Crie um "esquema `security`" utilizando `HTTPBasic`.
* Utilize o `security` com uma dependência em sua *operação de rota*.
* Isso retorna um objeto do tipo `HTTPBasicCredentials`:
* Isto contém o `username` e o `password` enviado.
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
Quando você tentar abrir a URL pela primeira vez (ou clicar no botão "Executar" na documentação) o navegador vai pedir pelo seu usuário e senha:
<img src="/img/tutorial/security/image12.png">
## Verifique o usuário { #check-the-username }
Aqui está um exemplo mais completo.
Utilize uma dependência para verificar se o usuário e a senha estão corretos.
Para isso, utilize o módulo padrão do Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> para verificar o usuário e senha.
O `secrets.compare_digest()` necessita receber `bytes` ou `str` que possuem apenas caracteres ASCII (os em inglês). Isso significa que não funcionaria com caracteres como o `á`, como em `Sebastián`.
Para lidar com isso, primeiramente nós convertemos o `username` e o `password` para `bytes`, codificando-os com UTF-8.
Então nós podemos utilizar o `secrets.compare_digest()` para garantir que o `credentials.username` é `"stanleyjobson"`, e que o `credentials.password` é `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
Isso seria parecido com:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Return some error
...
```
Porém, ao utilizar o `secrets.compare_digest()`, isso estará seguro contra um tipo de ataque chamado "timing attacks" (ataques de temporização).
### Ataques de Temporização { #timing-attacks }
Mas o que é um "timing attack" (ataque de temporização)?
Vamos imaginar que alguns invasores estão tentando adivinhar o usuário e a senha.
E eles enviam uma requisição com um usuário `johndoe` e uma senha `love123`.
Então o código Python em sua aplicação seria equivalente a algo como:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Mas no exato momento que o Python compara o primeiro `j` em `johndoe` contra o primeiro `s` em `stanleyjobson`, ele retornará `False`, porque ele já sabe que aquelas duas strings não são a mesma, pensando que "não existe a necessidade de desperdiçar mais poder computacional comparando o resto das letras". E a sua aplicação dirá "Usuário ou senha incorretos".
Então os invasores vão tentar com o usuário `stanleyjobsox` e a senha `love123`.
E a sua aplicação faz algo como:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
O Python terá que comparar todo o `stanleyjobso` tanto em `stanleyjobsox` como em `stanleyjobson` antes de perceber que as strings não são a mesma. Então isso levará alguns microssegundos a mais para retornar "Usuário ou senha incorretos".
#### O tempo para responder ajuda os invasores { #the-time-to-answer-helps-the-attackers }
Neste ponto, ao perceber que o servidor demorou alguns microssegundos a mais para enviar o retorno "Usuário ou senha incorretos", os invasores irão saber que eles acertaram _alguma coisa_, algumas das letras iniciais estavam certas.
E eles podem tentar de novo sabendo que provavelmente é algo mais parecido com `stanleyjobsox` do que com `johndoe`.
#### Um ataque "profissional" { #a-professional-attack }
Claro, os invasores não tentariam tudo isso de forma manual, eles escreveriam um programa para fazer isso, possivelmente com milhares ou milhões de testes por segundo. E obteriam apenas uma letra a mais por vez.
Mas fazendo isso, em alguns minutos ou horas os invasores teriam adivinhado o usuário e senha corretos, com a "ajuda" da nossa aplicação, apenas usando o tempo levado para responder.
#### Corrija com o `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Mas em nosso código já estamos utilizando o `secrets.compare_digest()`.
Resumindo, levará o mesmo tempo para comparar `stanleyjobsox` com `stanleyjobson` do que comparar `johndoe` com `stanleyjobson`. E o mesmo para a senha.
Deste modo, ao utilizar `secrets.compare_digest()` no código de sua aplicação, ela estará a salvo contra toda essa gama de ataques de segurança.
### Retorne o erro { #return-the-error }
Após detectar que as credenciais estão incorretas, retorne um `HTTPException` com o status 401 (o mesmo retornado quando nenhuma credencial foi informada) e adicione o cabeçalho `WWW-Authenticate` para fazer com que o navegador mostre o prompt de login novamente:
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
+19
View File
@@ -0,0 +1,19 @@
# Segurança Avançada { #advanced-security }
## Funcionalidades Adicionais { #additional-features }
Existem algumas funcionalidades adicionais para lidar com segurança além das cobertas em [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
/// tip | Dica
As próximas seções **não são necessariamente "avançadas"**.
E é possível que para o seu caso de uso, a solução está em uma delas.
///
## Leia o Tutorial primeiro { #read-the-tutorial-first }
As próximas seções pressupõem que você já leu o principal [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
Todas elas são baseadas nos mesmos conceitos, mas permitem algumas funcionalidades extras.
@@ -0,0 +1,274 @@
# Escopos OAuth2 { #oauth2-scopes }
Você pode utilizar escopos do OAuth2 diretamente com o **FastAPI**, eles são integrados para funcionar perfeitamente.
Isso permitiria que você tivesse um sistema de permissionamento mais refinado, seguindo o padrão do OAuth2 integrado na sua aplicação OpenAPI (e as documentações da API).
OAuth2 com escopos é o mecanismo utilizado por muitos provedores de autenticação, como o Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Eles utilizam isso para prover permissões específicas para os usuários e aplicações.
Toda vez que você "se autentica com" Facebook, Google, GitHub, Microsoft, X (Twitter), aquela aplicação está utilizando o OAuth2 com escopos.
Nesta seção, você verá como gerenciar a autenticação e autorização com os mesmos escopos do OAuth2 em sua aplicação **FastAPI**.
/// warning | Atenção
Isso é uma seção mais ou menos avançada. Se você está apenas começando, você pode pular.
Você não necessariamente precisa de escopos do OAuth2, e você pode lidar com autenticação e autorização da maneira que você achar melhor.
Mas o OAuth2 com escopos pode ser integrado de maneira fácil em sua API (com OpenAPI) e a sua documentação de API.
No entanto, você ainda aplica estes escopos, ou qualquer outro requisito de segurança/autorização, conforme necessário, em seu código.
Em muitos casos, OAuth2 com escopos pode ser um exagero.
Mas se você sabe que precisa, ou está curioso, continue lendo.
///
## Escopos OAuth2 e OpenAPI { #oauth2-scopes-and-openapi }
A especificação OAuth2 define "escopos" como uma lista de strings separadas por espaços.
O conteúdo de cada uma dessas strings pode ter qualquer formato, mas não devem possuir espaços.
Estes escopos representam "permissões".
No OpenAPI (e.g. os documentos da API), você pode definir "esquemas de segurança".
Quando um desses esquemas de segurança utiliza OAuth2, você pode também declarar e utilizar escopos.
Cada "escopo" é apenas uma string (sem espaços).
Eles são normalmente utilizados para declarar permissões de segurança específicas, como por exemplo:
* `users:read` or `users:write` são exemplos comuns.
* `instagram_basic` é utilizado pelo Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` é utilizado pelo Google.
/// info | Informação
No OAuth2, um "escopo" é apenas uma string que declara uma permissão específica necessária.
Não importa se ela contém outros caracteres como `:` ou se ela é uma URL.
Estes detalhes são específicos da implementação.
Para o OAuth2, eles são apenas strings.
///
## Visão global { #global-view }
Primeiro, vamos olhar rapidamente as partes que mudam dos exemplos do **Tutorial - Guia de Usuário** para [OAuth2 com Senha (e hash), Bearer com tokens JWT](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Agora utilizando escopos OAuth2:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Agora vamos revisar essas mudanças passo a passo.
## Esquema de segurança OAuth2 { #oauth2-security-scheme }
A primeira mudança é que agora nós estamos declarando o esquema de segurança OAuth2 com dois escopos disponíveis, `me` e `items`.
O parâmetro `scopes` recebe um `dict` contendo cada escopo como chave e a descrição como valor:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Pelo motivo de estarmos declarando estes escopos, eles aparecerão nos documentos da API quando você se autenticar/autorizar.
E você poderá selecionar quais escopos você deseja dar acesso: `me` e `items`.
Este é o mesmo mecanismo utilizado quando você adiciona permissões enquanto se autentica com o Facebook, Google, GitHub, etc:
<img src="/img/tutorial/security/image11.png">
## Token JWT com escopos { #jwt-token-with-scopes }
Agora, modifique a *operação de rota* do token para retornar os escopos solicitados.
Nós ainda estamos utilizando o mesmo `OAuth2PasswordRequestForm`. Ele inclui a propriedade `scopes` com uma `list` de `str`, com cada escopo que ele recebeu na requisição.
E nós retornamos os escopos como parte do token JWT.
/// danger | Cuidado
Para manter as coisas simples, aqui nós estamos apenas adicionando os escopos recebidos diretamente ao token.
Porém em sua aplicação, por segurança, você deve garantir que você apenas adiciona os escopos que o usuário possui permissão de fato, ou aqueles que você predefiniu.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Declare escopos em *operações de rota* e dependências { #declare-scopes-in-path-operations-and-dependencies }
Agora nós declaramos que a *operação de rota* para `/users/me/items/` exige o escopo `items`.
Para isso, nós importamos e utilizamos `Security` de `fastapi`.
Você pode utilizar `Security` para declarar dependências (assim como `Depends`), porém o `Security` também recebe o parâmetro `scopes` com uma lista de escopos (strings).
Neste caso, nós passamos a função `get_current_active_user` como dependência para `Security` (da mesma forma que nós faríamos com `Depends`).
Mas nós também passamos uma `list` de escopos, neste caso com apenas um escopo: `items` (poderia ter mais).
E a função de dependência `get_current_active_user` também pode declarar subdependências, não apenas com `Depends`, mas também com `Security`. Ao declarar sua própria função de subdependência (`get_current_user`), e mais requisitos de escopo.
Neste caso, ele requer o escopo `me` (poderia requerer mais de um escopo).
/// note | Nota
Você não necessariamente precisa adicionar diferentes escopos em diferentes lugares.
Nós estamos fazendo isso aqui para demonstrar como o **FastAPI** lida com escopos declarados em diferentes níveis.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Detalhes Técnicos
`Security` é na verdade uma subclasse de `Depends`, e ele possui apenas um parâmetro extra que veremos depois.
Porém ao utilizar `Security` no lugar de `Depends`, o **FastAPI** saberá que ele pode declarar escopos de segurança, utilizá-los internamente, e documentar a API com o OpenAPI.
Mas quando você importa `Query`, `Path`, `Depends`, `Security` entre outros de `fastapi`, eles são na verdade funções que retornam classes especiais.
///
## Utilize `SecurityScopes` { #use-securityscopes }
Agora atualize a dependência `get_current_user`.
Este é o usado pelas dependências acima.
Aqui é onde estamos utilizando o mesmo esquema OAuth2 que nós declaramos antes, declarando-o como uma dependência: `oauth2_scheme`.
Porque esta função de dependência não possui nenhum requerimento de escopo, nós podemos utilizar `Depends` com o `oauth2_scheme`. Nós não precisamos utilizar `Security` quando nós não precisamos especificar escopos de segurança.
Nós também declaramos um parâmetro especial do tipo `SecurityScopes`, importado de `fastapi.security`.
A classe `SecurityScopes` é semelhante à classe `Request` (`Request` foi utilizada para obter o objeto da requisição diretamente).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Utilize os `scopes` { #use-the-scopes }
O parâmetro `security_scopes` será do tipo `SecurityScopes`.
Ele terá a propriedade `scopes` com uma lista contendo todos os escopos requeridos por ele e todas as dependências que utilizam ele como uma subdependência. Isso significa, todos os "dependentes"... pode soar meio confuso, e isso será explicado novamente mais adiante.
O objeto `security_scopes` (da classe `SecurityScopes`) também oferece um atributo `scope_str` com uma única string, contendo os escopos separados por espaços (nós vamos utilizar isso).
Nós criamos uma `HTTPException` que nós podemos reutilizar (`raise`) mais tarde em diversos lugares.
Nesta exceção, nós incluímos os escopos necessários (se houver algum) como uma string separada por espaços (utilizando `scope_str`). Nós colocamos esta string contendo os escopos no cabeçalho `WWW-Authenticate` (isso é parte da especificação).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Verifique o `username` e o formato dos dados { #verify-the-username-and-data-shape }
Nós verificamos que nós obtemos um `username`, e extraímos os escopos.
E depois nós validamos esse dado com o modelo Pydantic (capturando a exceção `ValidationError`), e se nós obtemos um erro ao ler o token JWT ou validando os dados com o Pydantic, nós levantamos a exceção `HTTPException` que criamos anteriormente.
Para isso, nós atualizamos o modelo Pydantic `TokenData` com a nova propriedade `scopes`.
Ao validar os dados com o Pydantic nós podemos garantir que temos, por exemplo, exatamente uma `list` de `str` com os escopos e uma `str` com o `username`.
No lugar de, por exemplo, um `dict`, ou alguma outra coisa, que poderia quebrar a aplicação em algum lugar mais tarde, tornando isso um risco de segurança.
Nós também verificamos que nós temos um usuário com o "*username*", e caso contrário, nós levantamos a mesma exceção que criamos anteriormente.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Verifique os `scopes` { #verify-the-scopes }
Nós verificamos agora que todos os escopos necessários, por essa dependência e todos os dependentes (incluindo as *operações de rota*) estão incluídas nos escopos fornecidos pelo token recebido, caso contrário, levantamos uma `HTTPException`.
Para isso, nós utilizamos `security_scopes.scopes`, que contém uma `list` com todos esses escopos como uma `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Árvore de dependência e escopos { #dependency-tree-and-scopes }
Vamos rever novamente essa árvore de dependência e os escopos.
Como a dependência `get_current_active_user` possui uma subdependência em `get_current_user`, o escopo `"me"` declarado em `get_current_active_user` será incluído na lista de escopos necessários em `security_scopes.scopes` passado para `get_current_user`.
A própria *operação de rota* também declara o escopo, `"items"`, então ele também estará na lista de `security_scopes.scopes` passado para o `get_current_user`.
Aqui está como a hierarquia de dependências e escopos parecem:
* A *operação de rota* `read_own_items` possui:
* Escopos necessários `["items"]` com a dependência:
* `get_current_active_user`:
* A função de dependência `get_current_active_user` possui:
* Escopos necessários `["me"]` com a dependência:
* `get_current_user`:
* A função de dependência `get_current_user` possui:
* Nenhum escopo necessário.
* Uma dependência utilizando `oauth2_scheme`.
* Um parâmetro `security_scopes` do tipo `SecurityScopes`:
* Este parâmetro `security_scopes` possui uma propriedade `scopes` com uma `list` contendo todos estes escopos declarados acima, então:
* `security_scopes.scopes` terá `["me", "items"]` para a *operação de rota* `read_own_items`.
* `security_scopes.scopes` terá `["me"]` para a *operação de rota* `read_users_me`, porque ela declarou na dependência `get_current_active_user`.
* `security_scopes.scopes` terá `[]` (nada) para a *operação de rota* `read_system_status`, porque ele não declarou nenhum `Security` com `scopes`, e sua dependência, `get_current_user`, não declara nenhum `scopes` também.
/// tip | Dica
A coisa importante e "mágica" aqui é que `get_current_user` terá diferentes listas de `scopes` para validar para cada *operação de rota*.
Tudo depende dos `scopes` declarados em cada *operação de rota* e cada dependência da árvore de dependências para aquela *operação de rota* específica.
///
## Mais detalhes sobre `SecurityScopes` { #more-details-about-securityscopes }
Você pode utilizar `SecurityScopes` em qualquer lugar, e em diversos lugares. Ele não precisa estar na dependência "raiz".
Ele sempre terá os escopos de segurança declarados nas dependências atuais de `Security` e todos os dependentes para **aquela** *operação de rota* **específica** e **aquela** árvore de dependência **específica**.
Porque o `SecurityScopes` terá todos os escopos declarados por dependentes, você pode utilizá-lo para verificar se o token possui os escopos necessários em uma função de dependência central, e depois declarar diferentes requisitos de escopo em diferentes *operações de rota*.
Todos eles serão validados independentemente para cada *operação de rota*.
## Verifique { #check-it }
Se você abrir os documentos da API, você pode autenticar e especificar quais escopos você quer autorizar.
<img src="/img/tutorial/security/image11.png">
Se você não selecionar nenhum escopo, você terá "autenticado", mas quando você tentar acessar `/users/me/` ou `/users/me/items/`, você vai obter um erro dizendo que você não possui as permissões necessárias. Você ainda poderá acessar `/status/`.
E se você selecionar o escopo `me`, mas não o escopo `items`, você poderá acessar `/users/me/`, mas não `/users/me/items/`.
Isso é o que aconteceria se uma aplicação terceira que tentou acessar uma dessas *operações de rota* com um token fornecido por um usuário, dependendo de quantas permissões o usuário forneceu para a aplicação.
## Sobre integrações de terceiros { #about-third-party-integrations }
Neste exemplo nós estamos utilizando o fluxo de senha do OAuth2.
Isso é apropriado quando nós estamos autenticando em nossa própria aplicação, provavelmente com o nosso próprio "*frontend*".
Porque nós podemos confiar nele para receber o `username` e o `password`, pois nós controlamos isso.
Mas se nós estamos construindo uma aplicação OAuth2 que outros poderiam conectar (i.e., se você está construindo um provedor de autenticação equivalente ao Facebook, Google, GitHub, etc.) você deveria utilizar um dos outros fluxos.
O mais comum é o fluxo implícito.
O mais seguro é o fluxo de código, mas ele é mais complexo para implementar, pois ele necessita mais passos. Como ele é mais complexo, muitos provedores terminam sugerindo o fluxo implícito.
/// note | Nota
É comum que cada provedor de autenticação nomeie os seus fluxos de forma diferente, para torná-lo parte de sua marca.
Mas no final, eles estão implementando o mesmo padrão OAuth2.
///
O **FastAPI** inclui utilitários para todos esses fluxos de autenticação OAuth2 em `fastapi.security.oauth2`.
## `Security` em decoradores de `dependencies` { #security-in-decorator-dependencies }
Da mesma forma que você pode definir uma `list` de `Depends` no parâmetro `dependencies` do decorador (como explicado em [Dependências em decoradores de operações de rota](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), você também pode utilizar `Security` com escopos lá.
+346
View File
@@ -0,0 +1,346 @@
# Configurações e Variáveis de Ambiente { #settings-and-environment-variables }
Em muitos casos, sua aplicação pode precisar de configurações externas, por exemplo chaves secretas, credenciais de banco de dados, credenciais para serviços de e-mail, etc.
A maioria dessas configurações é variável (pode mudar), como URLs de banco de dados. E muitas podem ser sensíveis, como segredos.
Por esse motivo, é comum fornecê-las em variáveis de ambiente lidas pela aplicação.
/// tip | Dica
Para entender variáveis de ambiente, você pode ler [Variáveis de Ambiente](../environment-variables.md){.internal-link target=_blank}.
///
## Tipagem e validação { #types-and-validation }
Essas variáveis de ambiente só conseguem lidar com strings de texto, pois são externas ao Python e precisam ser compatíveis com outros programas e com o resto do sistema (e até com diferentes sistemas operacionais, como Linux, Windows, macOS).
Isso significa que qualquer valor lido em Python a partir de uma variável de ambiente será uma `str`, e qualquer conversão para um tipo diferente ou validação precisa ser feita em código.
## Pydantic `Settings` { #pydantic-settings }
Felizmente, o Pydantic fornece uma ótima utilidade para lidar com essas configurações vindas de variáveis de ambiente com <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings management</a>.
### Instalar `pydantic-settings` { #install-pydantic-settings }
Primeiro, certifique-se de criar seu [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então instalar o pacote `pydantic-settings`:
<div class="termy">
```console
$ pip install pydantic-settings
---> 100%
```
</div>
Ele também vem incluído quando você instala os extras `all` com:
<div class="termy">
```console
$ pip install "fastapi[all]"
---> 100%
```
</div>
/// info | Informação
No Pydantic v1 ele vinha incluído no pacote principal. Agora é distribuído como um pacote independente para que você possa optar por instalá-lo ou não, caso não precise dessa funcionalidade.
///
### Criar o objeto `Settings` { #create-the-settings-object }
Importe `BaseSettings` do Pydantic e crie uma subclasse, muito parecido com um modelo do Pydantic.
Da mesma forma que com modelos do Pydantic, você declara atributos de classe com anotações de tipo e, possivelmente, valores padrão.
Você pode usar as mesmas funcionalidades e ferramentas de validação que usa em modelos do Pydantic, como diferentes tipos de dados e validações adicionais com `Field()`.
//// tab | Pydantic v2
{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
////
//// tab | Pydantic v1
/// info | Informação
No Pydantic v1 você importaria `BaseSettings` diretamente de `pydantic` em vez de `pydantic_settings`.
///
{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
////
/// tip | Dica
Se você quer algo rápido para copiar e colar, não use este exemplo, use o último abaixo.
///
Então, quando você cria uma instância dessa classe `Settings` (neste caso, no objeto `settings`), o Pydantic vai ler as variáveis de ambiente sem diferenciar maiúsculas de minúsculas; assim, uma variável em maiúsculas `APP_NAME` ainda será lida para o atributo `app_name`.
Em seguida, ele converterá e validará os dados. Assim, quando você usar esse objeto `settings`, terá dados dos tipos que declarou (por exemplo, `items_per_user` será um `int`).
### Usar o `settings` { #use-the-settings }
Depois você pode usar o novo objeto `settings` na sua aplicação:
{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
### Executar o servidor { #run-the-server }
Em seguida, você executaria o servidor passando as configurações como variáveis de ambiente, por exemplo, você poderia definir `ADMIN_EMAIL` e `APP_NAME` com:
<div class="termy">
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
/// tip | Dica
Para definir várias variáveis de ambiente para um único comando, basta separá-las com espaço e colocá-las todas antes do comando.
///
Então a configuração `admin_email` seria definida como `"deadpool@example.com"`.
O `app_name` seria `"ChimichangApp"`.
E `items_per_user` manteria seu valor padrão de `50`.
## Configurações em outro módulo { #settings-in-another-module }
Você pode colocar essas configurações em outro arquivo de módulo como visto em [Aplicações Maiores - Múltiplos Arquivos](../tutorial/bigger-applications.md){.internal-link target=_blank}.
Por exemplo, você poderia ter um arquivo `config.py` com:
{* ../../docs_src/settings/app01/config.py *}
E então usá-lo em um arquivo `main.py`:
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
/// tip | Dica
Você também precisaria de um arquivo `__init__.py` como visto em [Aplicações Maiores - Múltiplos Arquivos](../tutorial/bigger-applications.md){.internal-link target=_blank}.
///
## Configurações em uma dependência { #settings-in-a-dependency }
Em algumas ocasiões, pode ser útil fornecer as configurações a partir de uma dependência, em vez de ter um objeto global `settings` usado em todos os lugares.
Isso pode ser especialmente útil durante os testes, pois é muito fácil sobrescrever uma dependência com suas próprias configurações personalizadas.
### O arquivo de configuração { #the-config-file }
Vindo do exemplo anterior, seu arquivo `config.py` poderia ser assim:
{* ../../docs_src/settings/app02/config.py hl[10] *}
Perceba que agora não criamos uma instância padrão `settings = Settings()`.
### O arquivo principal da aplicação { #the-main-app-file }
Agora criamos uma dependência que retorna um novo `config.Settings()`.
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
/// tip | Dica
Vamos discutir o `@lru_cache` em breve.
Por enquanto, você pode assumir que `get_settings()` é uma função normal.
///
E então podemos exigi-la na *função de operação de rota* como dependência e usá-la onde for necessário.
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
### Configurações e testes { #settings-and-testing }
Então seria muito fácil fornecer um objeto de configurações diferente durante os testes criando uma sobrescrita de dependência para `get_settings`:
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
Na sobrescrita da dependência definimos um novo valor para `admin_email` ao criar o novo objeto `Settings`, e então retornamos esse novo objeto.
Depois podemos testar que ele é usado.
## Lendo um arquivo `.env` { #reading-a-env-file }
Se você tiver muitas configurações que possivelmente mudam bastante, talvez em diferentes ambientes, pode ser útil colocá-las em um arquivo e então lê-las como se fossem variáveis de ambiente.
Essa prática é tão comum que tem um nome: essas variáveis de ambiente são comumente colocadas em um arquivo `.env`, e o arquivo é chamado de "dotenv".
/// tip | Dica
Um arquivo começando com um ponto (`.`) é um arquivo oculto em sistemas tipo Unix, como Linux e macOS.
Mas um arquivo dotenv não precisa ter exatamente esse nome de arquivo.
///
O Pydantic tem suporte para leitura desses tipos de arquivos usando uma biblioteca externa. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>.
/// tip | Dica
Para isso funcionar, você precisa executar `pip install python-dotenv`.
///
### O arquivo `.env` { #the-env-file }
Você poderia ter um arquivo `.env` com:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Ler configurações do `.env` { #read-settings-from-env }
E então atualizar seu `config.py` com:
//// tab | Pydantic v2
{* ../../docs_src/settings/app03_an/config.py hl[9] *}
/// tip | Dica
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic: Concepts: Configuration</a>.
///
////
//// tab | Pydantic v1
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
/// tip | Dica
A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
///
////
/// info | Informação
Na versão 1 do Pydantic a configuração era feita em uma classe interna `Config`, na versão 2 do Pydantic é feita em um atributo `model_config`. Esse atributo recebe um `dict`, e para ter autocompletar e erros inline você pode importar e usar `SettingsConfigDict` para definir esse `dict`.
///
Aqui definimos a configuração `env_file` dentro da sua classe `Settings` do Pydantic e definimos o valor como o nome do arquivo dotenv que queremos usar.
### Criando o `Settings` apenas uma vez com `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Ler um arquivo do disco normalmente é uma operação custosa (lenta), então você provavelmente vai querer fazer isso apenas uma vez e depois reutilizar o mesmo objeto de configurações, em vez de lê-lo a cada requisição.
Mas toda vez que fizermos:
```Python
Settings()
```
um novo objeto `Settings` seria criado e, na criação, ele leria o arquivo `.env` novamente.
Se a função de dependência fosse assim:
```Python
def get_settings():
return Settings()
```
criaríamos esse objeto para cada requisição e leríamos o arquivo `.env` para cada requisição. ⚠️
Mas como estamos usando o decorador `@lru_cache` por cima, o objeto `Settings` será criado apenas uma vez, na primeira vez em que for chamado. ✔️
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
Em qualquer chamada subsequente de `get_settings()` nas dependências das próximas requisições, em vez de executar o código interno de `get_settings()` e criar um novo objeto `Settings`, ele retornará o mesmo objeto que foi retornado na primeira chamada, repetidamente.
#### Detalhes Técnicos do `lru_cache` { #lru-cache-technical-details }
`@lru_cache` modifica a função que decora para retornar o mesmo valor que foi retornado na primeira vez, em vez de calculá-lo novamente executando o código da função todas as vezes.
Assim, a função abaixo dele será executada uma vez para cada combinação de argumentos. E então os valores retornados para cada uma dessas combinações de argumentos serão usados repetidamente sempre que a função for chamada com exatamente a mesma combinação de argumentos.
Por exemplo, se você tiver uma função:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
seu programa poderia executar assim:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
No caso da nossa dependência `get_settings()`, a função nem recebe argumentos, então ela sempre retorna o mesmo valor.
Dessa forma, ela se comporta quase como se fosse apenas uma variável global. Mas como usa uma função de dependência, podemos sobrescrevê-la facilmente para testes.
`@lru_cache` faz parte de `functools`, que faz parte da biblioteca padrão do Python; você pode ler mais sobre isso na <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">documentação do Python para `@lru_cache`</a>.
## Recapitulando { #recap }
Você pode usar Pydantic Settings para lidar com as configurações da sua aplicação, com todo o poder dos modelos Pydantic.
* Usando uma dependência você pode simplificar os testes.
* Você pode usar arquivos `.env` com ele.
* Usar `@lru_cache` permite evitar ler o arquivo dotenv repetidamente a cada requisição, enquanto permite sobrescrevê-lo durante os testes.
+67
View File
@@ -0,0 +1,67 @@
# Sub Aplicações - Montagens { #sub-applications-mounts }
Se você precisar ter duas aplicações FastAPI independentes, cada uma com seu próprio OpenAPI e suas próprias interfaces de documentação, você pode ter um aplicativo principal e "montar" uma (ou mais) sub-aplicações.
## Montando uma aplicação **FastAPI** { #mounting-a-fastapi-application }
"Montar" significa adicionar uma aplicação completamente "independente" em um path específico, que então se encarrega de lidar com tudo sob esse path, com as _operações de rota_ declaradas nessa sub-aplicação.
### Aplicação de nível superior { #top-level-application }
Primeiro, crie a aplicação principal, de nível superior, **FastAPI**, e suas *operações de rota*:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
### Sub-aplicação { #sub-application }
Em seguida, crie sua sub-aplicação e suas *operações de rota*.
Essa sub-aplicação é apenas outra aplicação FastAPI padrão, mas esta é a que será "montada":
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
### Monte a sub-aplicação { #mount-the-sub-application }
Na sua aplicação de nível superior, `app`, monte a sub-aplicação, `subapi`.
Neste caso, ela será montada no path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
### Verifique a documentação automática da API { #check-the-automatic-api-docs }
Agora, execute o comando `fastapi` com o seu arquivo:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
E abra a documentação em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá a documentação automática da API para a aplicação principal, incluindo apenas suas próprias _operações de rota_:
<img src="/img/tutorial/sub-applications/image01.png">
E então, abra a documentação para a sub-aplicação, em <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
Você verá a documentação automática da API para a sub-aplicação, incluindo apenas suas próprias _operações de rota_, todas sob o prefixo de sub-path correto `/subapi`:
<img src="/img/tutorial/sub-applications/image02.png">
Se você tentar interagir com qualquer uma das duas interfaces de usuário, elas funcionarão corretamente, porque o navegador será capaz de se comunicar com cada aplicação ou sub-aplicação específica.
### Detalhes Técnicos: `root_path` { #technical-details-root-path }
Quando você monta uma sub-aplicação como descrito acima, o FastAPI se encarrega de comunicar o path de montagem para a sub-aplicação usando um mecanismo da especificação ASGI chamado `root_path`.
Dessa forma, a sub-aplicação saberá usar esse prefixo de path para a interface de documentação.
E a sub-aplicação também poderia ter suas próprias sub-aplicações montadas e tudo funcionaria corretamente, porque o FastAPI lida com todos esses `root_path`s automaticamente.
Você aprenderá mais sobre o `root_path` e como usá-lo explicitamente na seção sobre [Atrás de um Proxy](behind-a-proxy.md){.internal-link target=_blank}.
+126
View File
@@ -0,0 +1,126 @@
# Templates { #templates }
Você pode usar qualquer template engine com o **FastAPI**.
Uma escolha comum é o Jinja2, o mesmo usado pelo Flask e outras ferramentas.
Existem utilitários para configurá-lo facilmente que você pode usar diretamente em sua aplicação **FastAPI** (fornecidos pelo Starlette).
## Instalar dependências { #install-dependencies }
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e instalar `jinja2`:
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## Usando `Jinja2Templates` { #using-jinja2templates }
* Importe `Jinja2Templates`.
* Crie um objeto `templates` que você possa reutilizar posteriormente.
* Declare um parâmetro `Request` no *path operation* que retornará um template.
* Use o `templates` que você criou para renderizar e retornar uma `TemplateResponse`, passe o nome do template, o objeto `request` e um dicionário "context" com pares chave-valor a serem usados dentro do template do Jinja2.
{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
/// note | Nota
Antes do FastAPI 0.108.0, Starlette 0.29.0, `name` era o primeiro parâmetro.
Além disso, em versões anteriores, o objeto `request` era passado como parte dos pares chave-valor no "context" dict para o Jinja2.
///
/// tip | Dica
Ao declarar `response_class=HTMLResponse`, a documentação entenderá que a resposta será HTML.
///
/// note | Detalhes Técnicos
Você também poderia usar `from starlette.templating import Jinja2Templates`.
**FastAPI** fornece o mesmo `starlette.templating` como `fastapi.templating` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette. O mesmo acontece com `Request` e `StaticFiles`.
///
## Escrevendo templates { #writing-templates }
Então você pode escrever um template em `templates/item.html`, por exemplo:
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### Valores de contexto do template { #template-context-values }
No código HTML que contém:
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
...aparecerá o `id` obtido do "context" `dict` que você passou:
```Python
{"id": id}
```
Por exemplo, dado um ID de valor `42`, aparecerá:
```html
Item ID: 42
```
### Argumentos do `url_for` no template { #template-url-for-arguments }
Você também pode usar `url_for()` dentro do template, ele recebe como argumentos os mesmos argumentos que seriam usados pela sua *path operation function*.
Logo, a seção com:
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
...irá gerar um link para a mesma URL que será tratada pela *path operation function* `read_item(id=id)`.
Por exemplo, com um ID de `42`, isso renderizará:
```html
<a href="/items/42">
```
## Templates e arquivos estáticos { #templates-and-static-files }
Você também pode usar `url_for()` dentro do template e usá-lo, por exemplo, com o `StaticFiles` que você montou com o `name="static"`.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
Neste exemplo, ele seria vinculado a um arquivo CSS em `static/styles.css` com:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
E como você está usando `StaticFiles`, este arquivo CSS será automaticamente servido pela sua aplicação **FastAPI** na URL `/static/styles.css`.
## Mais detalhes { #more-details }
Para obter mais detalhes, incluindo como testar templates, consulte a <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">documentação da Starlette sobre templates</a>.
@@ -0,0 +1,53 @@
# Testando Dependências com Sobreposições { #testing-dependencies-with-overrides }
## Sobrepondo dependências durante os testes { #overriding-dependencies-during-testing }
Existem alguns cenários onde você deseje sobrepor uma dependência durante os testes.
Você não quer que a dependência original execute (e nenhuma das subdependências que você possa ter).
Em vez disso, você deseja fornecer uma dependência diferente que será usada somente durante os testes (possivelmente apenas para alguns testes específicos) e fornecerá um valor que pode ser usado onde o valor da dependência original foi usado.
### Casos de uso: serviço externo { #use-cases-external-service }
Um exemplo pode ser que você possua um provedor de autenticação externo que você precisa chamar.
Você envia ao serviço um *token* e ele retorna um usuário autenticado.
Este provedor pode cobrar por requisição, e chamá-lo pode levar mais tempo do que se você tivesse um usuário fixo para os testes.
Você provavelmente quer testar o provedor externo uma vez, mas não necessariamente chamá-lo em todos os testes que executarem.
Neste caso, você pode sobrepor (*override*) a dependência que chama o provedor, e utilizar uma dependência customizada que retorna um *mock* do usuário, apenas para os seus testes.
### Utilize o atributo `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
Para estes casos, a sua aplicação **FastAPI** possui o atributo `app.dependency_overrides`. Ele é um simples `dict`.
Para sobrepor a dependência para os testes, você coloca como chave a dependência original (a função), e como valor, a sua sobreposição da dependência (outra função).
E então o **FastAPI** chamará a sobreposição no lugar da dependência original.
{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
/// tip | Dica
Você pode definir uma sobreposição de dependência para uma dependência que é utilizada em qualquer lugar da sua aplicação **FastAPI**.
A dependência original pode estar sendo utilizada em uma *função de operação de rota*, um *decorador de operação de rota* (quando você não utiliza o valor retornado), uma chamada ao `.include_router()`, etc.
O FastAPI ainda poderá sobrescrevê-lo.
///
E então você pode redefinir as suas sobreposições (removê-las) definindo o `app.dependency_overrides` como um `dict` vazio:
```Python
app.dependency_overrides = {}
```
/// tip | Dica
Se você quer sobrepor uma dependência apenas para alguns testes, você pode definir a sobreposição no início do teste (dentro da função de teste) e reiniciá-la ao final (no final da função de teste).
///
+11
View File
@@ -0,0 +1,11 @@
# Testando eventos: lifespan e inicialização - encerramento { #testing-events-lifespan-and-startup-shutdown }
Quando você precisa que o `lifespan` seja executado em seus testes, você pode utilizar o `TestClient` com a instrução `with`:
{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
Você pode ler mais detalhes sobre o ["Executando lifespan em testes no site oficial da documentação do Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Para os eventos `startup` e `shutdown` descontinuados, você pode usar o `TestClient` da seguinte forma:
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
@@ -0,0 +1,13 @@
# Testando WebSockets { #testing-websockets }
Você pode usar o mesmo `TestClient` para testar WebSockets.
Para isso, você utiliza o `TestClient` dentro de uma instrução `with`, conectando com o WebSocket:
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
/// note | Nota
Para mais detalhes, confira a documentação do Starlette para <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">testar WebSockets</a>.
///
@@ -0,0 +1,56 @@
# Utilizando o Request diretamente { #using-the-request-directly }
Até agora você declarou as partes da requisição que você precisa utilizando os seus tipos.
Obtendo dados de:
* O path como parâmetros.
* Cabeçalhos (*Headers*).
* Cookies.
* etc.
E ao fazer isso, o **FastAPI** está validando as informações, convertendo-as e gerando documentação para a sua API automaticamente.
Porém há situações em que você possa precisar acessar o objeto `Request` diretamente.
## Detalhes sobre o objeto `Request` { #details-about-the-request-object }
Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> do Starlette diretamente quando precisar.
Isso significaria também que se você obtiver informações do objeto `Request` diretamente (ler o corpo da requisição por exemplo), as informações não serão validadas, convertidas ou documentadas (com o OpenAPI, para a interface de usuário automática da API) pelo FastAPI.
Embora qualquer outro parâmetro declarado normalmente (o corpo da requisição com um modelo Pydantic, por exemplo) ainda seria validado, convertido, anotado, etc.
Mas há situações específicas onde é útil utilizar o objeto `Request`.
## Utilize o objeto `Request` diretamente { #use-the-request-object-directly }
Vamos imaginar que você deseja obter o endereço de IP/host do cliente dentro da sua *função de operação de rota*.
Para isso você precisa acessar a requisição diretamente.
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
Ao declarar o parâmetro com o tipo sendo um `Request` em sua *função de operação de rota*, o **FastAPI** saberá como passar o `Request` neste parâmetro.
/// tip | Dica
Note que neste caso, nós estamos declarando o parâmetro de path ao lado do parâmetro da requisição.
Assim, o parâmetro de path será extraído, validado, convertido para o tipo especificado e anotado com OpenAPI.
Do mesmo jeito, você pode declarar qualquer outro parâmetro normalmente, e além disso, obter o `Request` também.
///
## Documentação do `Request` { #request-documentation }
Você pode ler mais sobre os detalhes do objeto <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request` no site da documentação oficial do Starlette.</a>.
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.requests import Request`.
O **FastAPI** fornece isso diretamente apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente do Starlette.
///
+186
View File
@@ -0,0 +1,186 @@
# WebSockets { #websockets }
Você pode usar <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> com **FastAPI**.
## Instale `websockets` { #install-websockets }
Garanta que você criou um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, o ativou e instalou o `websockets` (uma biblioteca Python que facilita o uso do protocolo "WebSocket"):
<div class="termy">
```console
$ pip install websockets
---> 100%
```
</div>
## Cliente WebSockets { #websockets-client }
### Em produção { #in-production }
Em seu sistema de produção, você provavelmente tem um frontend criado com um framework moderno como React, Vue.js ou Angular.
E para comunicar usando WebSockets com seu backend, você provavelmente usaria as utilidades do seu frontend.
Ou você pode ter um aplicativo móvel nativo que se comunica diretamente com seu backend WebSocket, em código nativo.
Ou você pode ter qualquer outra forma de comunicar com o endpoint WebSocket.
---
Mas para este exemplo, usaremos um documento HTML muito simples com algum JavaScript, tudo dentro de uma string longa.
Esse, é claro, não é o ideal e você não o usaria para produção.
Na produção, você teria uma das opções acima.
Mas é a maneira mais simples de focar no lado do servidor de WebSockets e ter um exemplo funcional:
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
## Crie um `websocket` { #create-a-websocket }
Em sua aplicação **FastAPI**, crie um `websocket`:
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
/// note | Detalhes Técnicos
Você também poderia usar `from starlette.websockets import WebSocket`.
A **FastAPI** fornece o mesmo `WebSocket` diretamente apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente do Starlette.
///
## Aguarde mensagens e envie mensagens { #await-for-messages-and-send-messages }
Em sua rota WebSocket você pode esperar (`await`) por mensagens e enviar mensagens.
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
Você pode receber e enviar dados binários, de texto e JSON.
## Tente { #try-it }
Se seu arquivo for nomeado `main.py`, execute sua aplicação com:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Abra seu navegador em: <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Você verá uma página simples como:
<img src="/img/tutorial/websockets/image01.png">
Você pode digitar mensagens na caixa de entrada e enviá-las:
<img src="/img/tutorial/websockets/image02.png">
E sua aplicação **FastAPI** com WebSockets responderá de volta:
<img src="/img/tutorial/websockets/image03.png">
Você pode enviar (e receber) muitas mensagens:
<img src="/img/tutorial/websockets/image04.png">
E todas elas usarão a mesma conexão WebSocket.
## Usando `Depends` e outros { #using-depends-and-others }
Nos endpoints WebSocket você pode importar do `fastapi` e usar:
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
Eles funcionam da mesma forma que para outros endpoints FastAPI/*operações de rota*:
{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
/// info | Informação
Como isso é um WebSocket, não faz muito sentido levantar uma `HTTPException`, em vez disso levantamos uma `WebSocketException`.
Você pode usar um código de fechamento dos <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">códigos válidos definidos na especificação</a>.
///
### Tente os WebSockets com dependências { #try-the-websockets-with-dependencies }
Se seu arquivo for nomeado `main.py`, execute sua aplicação com:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Abra seu navegador em: <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Lá você pode definir:
* O "Item ID", usado no path.
* O "Token" usado como um parâmetro de consulta.
/// tip | Dica
Perceba que a consulta `token` será manipulada por uma dependência.
///
Com isso você pode conectar o WebSocket e então enviar e receber mensagens:
<img src="/img/tutorial/websockets/image05.png">
## Lidando com desconexões e múltiplos clientes { #handling-disconnections-and-multiple-clients }
Quando uma conexão WebSocket é fechada, o `await websocket.receive_text()` levantará uma exceção `WebSocketDisconnect`, que você pode então capturar e lidar como neste exemplo.
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
Para testar:
* Abra o aplicativo com várias abas do navegador.
* Escreva mensagens a partir delas.
* Então feche uma das abas.
Isso levantará a exceção `WebSocketDisconnect`, e todos os outros clientes receberão uma mensagem como:
```
Client #1596980209979 left the chat
```
/// tip | Dica
O app acima é um exemplo mínimo e simples para demonstrar como lidar e transmitir mensagens para várias conexões WebSocket.
Mas tenha em mente que, como tudo é manipulado na memória, em uma única list, ele só funcionará enquanto o processo estiver em execução e só funcionará com um único processo.
Se você precisa de algo fácil de integrar com o FastAPI, mas que seja mais robusto, suportado por Redis, PostgreSQL ou outros, verifique o <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>.
///
## Mais informações { #more-info }
Para aprender mais sobre as opções, verifique a documentação do Starlette para:
* <a href="https://www.starlette.dev/websockets/" class="external-link" target="_blank">A classe `WebSocket`</a>.
* <a href="https://www.starlette.dev/endpoints/#websocketendpoint" class="external-link" target="_blank">Manipulação de WebSockets baseada em classes</a>.
+35
View File
@@ -0,0 +1,35 @@
# Adicionando WSGI - Flask, Django, entre outros { #including-wsgi-flask-django-others }
Como você viu em [Subaplicações - Montagens](sub-applications.md){.internal-link target=_blank} e [Atrás de um Proxy](behind-a-proxy.md){.internal-link target=_blank}, você pode montar aplicações WSGI.
Para isso, você pode utilizar o `WSGIMiddleware` para encapsular a sua aplicação WSGI, como por exemplo Flask, Django, etc.
## Usando `WSGIMiddleware` { #using-wsgimiddleware }
Você precisa importar o `WSGIMiddleware`.
Em seguida, encapsule a aplicação WSGI (e.g. Flask) com o middleware.
E então monte isso sob um path.
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
## Confira { #check-it }
Agora, todas as requisições sob o path `/v1/` serão manipuladas pela aplicação Flask.
E o resto será manipulado pelo **FastAPI**.
Se você rodar a aplicação e ir até <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>, você verá o retorno do Flask:
```txt
Hello, World from Flask!
```
E se você for até <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a>, você verá o retorno do FastAPI:
```JSON
{
"message": "Hello World"
}
```
+485
View File
@@ -0,0 +1,485 @@
# Alternativas, Inspiração e Comparações { #alternatives-inspiration-and-comparisons }
O que inspirou o **FastAPI**, como ele se compara às alternativas e o que ele aprendeu com elas.
## Introdução { #intro }
**FastAPI** não existiria se não fosse pelo trabalho anterior de outras pessoas.
Houve muitas ferramentas criadas antes que ajudaram a inspirar sua criação.
Tenho evitado criar um novo framework por vários anos. Primeiro tentei resolver todas as funcionalidades cobertas pelo **FastAPI** utilizando muitos frameworks, plug-ins e ferramentas diferentes.
Mas em algum momento, não havia outra opção senão criar algo que fornecesse todos esses recursos, pegando as melhores ideias de ferramentas anteriores e combinando-as da melhor maneira possível, usando funcionalidades da linguagem que nem sequer estavam disponíveis antes (anotações de tipo no Python 3.6+).
## Ferramentas anteriores { #previous-tools }
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
É o framework Python mais popular e amplamente confiável. É utilizado para construir sistemas como o Instagram.
É relativamente bem acoplado com bancos de dados relacionais (como MySQL ou PostgreSQL), então, ter um banco de dados NoSQL (como Couchbase, MongoDB, Cassandra, etc.) como mecanismo principal de armazenamento não é muito fácil.
Foi criado para gerar o HTML no backend, não para criar APIs usadas por um frontend moderno (como React, Vue.js e Angular) ou por outros sistemas (como dispositivos <abbr title="Internet of Things Internet das Coisas">IoT</abbr>) comunicando com ele.
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
Django REST framework foi criado para ser uma caixa de ferramentas flexível para construção de APIs Web utilizando Django por baixo, para melhorar suas capacidades de API.
Ele é utilizado por muitas empresas incluindo Mozilla, Red Hat e Eventbrite.
Foi um dos primeiros exemplos de **documentação automática de API**, e essa foi especificamente uma das primeiras ideias que inspirou "a busca por" **FastAPI**.
/// note | Nota
Django REST Framework foi criado por Tom Christie. O mesmo criador de Starlette e Uvicorn, nos quais **FastAPI** é baseado.
///
/// check | **FastAPI** inspirado para
Ter uma interface web de documentação automática da API.
///
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
Flask é um "microframework", não inclui integrações com banco de dados nem muitas das coisas que vêm por padrão no Django.
Essa simplicidade e flexibilidade permitem fazer coisas como utilizar bancos de dados NoSQL como o principal sistema de armazenamento de dados.
Por ser muito simples, é relativamente intuitivo de aprender, embora a documentação se torne um pouco técnica em alguns pontos.
Ele também é comumente utilizado por outras aplicações que não necessariamente precisam de banco de dados, gerenciamento de usuários, ou qualquer uma das muitas funcionalidades que já vêm prontas no Django. Embora muitas dessas funcionalidades possam ser adicionadas com plug-ins.
Esse desacoplamento de partes, e ser um "microframework" que pode ser estendido para cobrir exatamente o que é necessário era uma funcionalidade chave que eu queria manter.
Dada a simplicidade do Flask, ele parecia uma boa opção para construção de APIs. A próxima coisa a encontrar era um "Django REST Framework" para Flask.
/// check | **FastAPI** inspirado para
Ser um microframework. Tornar fácil misturar e combinar as ferramentas e partes necessárias.
Ter um sistema de roteamento simples e fácil de usar.
///
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
**FastAPI** na verdade não é uma alternativa ao **Requests**. O escopo deles é muito diferente.
Na verdade, é comum utilizar Requests dentro de uma aplicação FastAPI.
Ainda assim, o FastAPI tirou bastante inspiração do Requests.
**Requests** é uma biblioteca para interagir com APIs (como um cliente), enquanto **FastAPI** é uma biblioteca para construir APIs (como um servidor).
Eles estão, mais ou menos, em pontas opostas, complementando-se.
Requests tem um design muito simples e intuitivo, é muito fácil de usar, com padrões sensatos. Mas ao mesmo tempo, é muito poderoso e personalizável.
É por isso que, como dito no site oficial:
> Requests é um dos pacotes Python mais baixados de todos os tempos
O jeito de usar é muito simples. Por exemplo, para fazer uma requisição `GET`, você escreveria:
```Python
response = requests.get("http://example.com/some/url")
```
A contra-parte na aplicação FastAPI, a operação de rota, poderia ficar assim:
```Python hl_lines="1"
@app.get("/some/url")
def read_url():
return {"message": "Hello World"}
```
Veja as similaridades em `requests.get(...)` e `@app.get(...)`.
/// check | **FastAPI** inspirado para
* Ter uma API simples e intuitiva.
* Utilizar nomes de métodos HTTP (operações) diretamente, de um jeito direto e intuitivo.
* Ter padrões sensatos, mas customizações poderosas.
///
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
A principal funcionalidade que eu queria do Django REST Framework era a documentação automática da API.
Então descobri que existia um padrão para documentar APIs, utilizando JSON (ou YAML, uma extensão do JSON) chamado Swagger.
E havia uma interface web para APIs Swagger já criada. Então, ser capaz de gerar documentação Swagger para uma API permitiria usar essa interface web automaticamente.
Em algum ponto, Swagger foi doado para a Fundação Linux, para ser renomeado OpenAPI.
É por isso que ao falar sobre a versão 2.0 é comum dizer "Swagger", e para a versão 3+ "OpenAPI".
/// check | **FastAPI** inspirado para
Adotar e usar um padrão aberto para especificações de API, em vez de um schema personalizado.
E integrar ferramentas de interface para usuários baseadas nos padrões:
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
Essas duas foram escolhidas por serem bem populares e estáveis, mas fazendo uma pesquisa rápida, você pode encontrar dúzias de interfaces alternativas adicionais para OpenAPI (que você pode utilizar com **FastAPI**).
///
### Flask REST frameworks { #flask-rest-frameworks }
Existem vários Flask REST frameworks, mas depois de investir tempo e trabalho investigando-os, descobri que muitos estão descontinuados ou abandonados, com diversas questões em aberto que os tornaram inadequados.
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
Uma das principais funcionalidades necessárias em sistemas de API é a "<abbr title="também chamado marshalling, conversão">serialização</abbr>" de dados, que é pegar dados do código (Python) e convertê-los em algo que possa ser enviado pela rede. Por exemplo, converter um objeto contendo dados de um banco de dados em um objeto JSON. Converter objetos `datetime` em strings, etc.
Outra grande funcionalidade necessária pelas APIs é a validação de dados, garantindo que os dados são válidos, dados certos parâmetros. Por exemplo, que algum campo seja `int`, e não alguma string aleatória. Isso é especialmente útil para dados de entrada.
Sem um sistema de validação de dados, você teria que realizar todas as verificações manualmente, no código.
Essas funcionalidades são o que o Marshmallow foi construído para fornecer. É uma ótima biblioteca, e eu a utilizei bastante antes.
Mas ele foi criado antes de existirem as anotações de tipo do Python. Então, para definir cada <abbr title="a definição de como os dados devem ser formados">schema</abbr> você precisa utilizar utilitários e classes específicos fornecidos pelo Marshmallow.
/// check | **FastAPI** inspirado para
Usar código para definir "schemas" que forneçam, automaticamente, tipos de dados e validação.
///
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
Outra grande funcionalidade requerida pelas APIs é o <abbr title="ler e converter para dados Python">parsing</abbr> de dados vindos de requisições de entrada.
Webargs é uma ferramenta feita para fornecer isso no topo de vários frameworks, inclusive Flask.
Ele utiliza Marshmallow por baixo para a validação de dados. E foi criado pelos mesmos desenvolvedores.
É uma grande ferramenta e eu também a utilizei bastante, antes de ter o **FastAPI**.
/// info | Informação
Webargs foi criado pelos mesmos desenvolvedores do Marshmallow.
///
/// check | **FastAPI** inspirado para
Ter validação automática dos dados de requisições de entrada.
///
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
Marshmallow e Webargs fornecem validação, parsing e serialização como plug-ins.
Mas a documentação ainda estava faltando. Então APISpec foi criado.
É um plug-in para muitos frameworks (e há um plug-in para Starlette também).
O jeito como ele funciona é que você escreve a definição do schema usando formato YAML dentro da docstring de cada função que lida com uma rota.
E ele gera schemas OpenAPI.
É assim como funciona no Flask, Starlette, Responder, etc.
Mas então, temos novamente o problema de ter uma micro-sintaxe, dentro de uma string Python (um grande YAML).
O editor não pode ajudar muito com isso. E se modificarmos parâmetros ou schemas do Marshmallow e esquecermos de também modificar aquela docstring em YAML, o schema gerado ficaria obsoleto.
/// info | Informação
APISpec foi criado pelos mesmos desenvolvedores do Marshmallow.
///
/// check | **FastAPI** inspirado para
Dar suporte ao padrão aberto para APIs, OpenAPI.
///
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
É um plug-in Flask, que amarra juntos Webargs, Marshmallow e APISpec.
Ele utiliza a informação do Webargs e Marshmallow para gerar automaticamente schemas OpenAPI, usando APISpec.
É uma grande ferramenta, muito subestimada. Deveria ser bem mais popular do que muitos plug-ins Flask por aí. Pode ser devido à sua documentação ser concisa e abstrata demais.
Isso resolveu ter que escrever YAML (outra sintaxe) dentro das docstrings do Python.
Essa combinação de Flask, Flask-apispec com Marshmallow e Webargs foi a minha stack de backend favorita até construir o **FastAPI**.
Usá-la levou à criação de vários geradores Flask full-stack. Estas são as principais stacks que eu (e várias equipes externas) tenho utilizado até agora:
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
E esses mesmos geradores full-stack foram a base dos [Geradores de Projetos **FastAPI**](project-generation.md){.internal-link target=_blank}.
/// info | Informação
Flask-apispec foi criado pelos mesmos desenvolvedores do Marshmallow.
///
/// check | **FastAPI** inspirado para
Gerar o schema OpenAPI automaticamente, a partir do mesmo código que define serialização e validação.
///
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (e <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
Isso nem é Python, NestJS é um framework NodeJS em JavaScript (TypeScript) inspirado pelo Angular.
Ele alcança algo um tanto similar ao que pode ser feito com Flask-apispec.
Ele tem um sistema de injeção de dependência integrado, inspirado pelo Angular 2. É necessário fazer o pré-registro dos "injetáveis" (como todos os sistemas de injeção de dependência que conheço), então, adiciona verbosidade e repetição de código.
Como os parâmetros são descritos com tipos do TypeScript (similares às anotações de tipo do Python), o suporte do editor é muito bom.
Mas como os dados do TypeScript não são preservados após a compilação para JavaScript, ele não pode depender dos tipos para definir validação, serialização e documentação ao mesmo tempo. Devido a isso e a algumas decisões de projeto, para obter validação, serialização e geração automática de schema, é necessário adicionar decorators em muitos lugares. Então, ele se torna bastante verboso.
Ele não consegue lidar muito bem com modelos aninhados. Então, se o corpo JSON na requisição for um objeto JSON que contém campos internos que por sua vez são objetos JSON aninhados, ele não consegue ser documentado e validado apropriadamente.
/// check | **FastAPI** inspirado para
Usar tipos do Python para ter um ótimo suporte do editor.
Ter um sistema de injeção de dependência poderoso. Encontrar um jeito de minimizar repetição de código.
///
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
Ele foi um dos primeiros frameworks Python extremamente rápidos baseados em `asyncio`. Ele foi feito para ser muito similar ao Flask.
/// note | Detalhes Técnicos
Ele utilizava <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> em vez do loop `asyncio` padrão do Python. É isso que o deixava tão rápido.
Ele claramente inspirou Uvicorn e Starlette, que atualmente são mais rápidos que o Sanic em benchmarks abertos.
///
/// check | **FastAPI** inspirado para
Encontrar um jeito de ter uma performance insana.
É por isso que o **FastAPI** é baseado em Starlette, pois ela é o framework mais rápido disponível (testado por benchmarks de terceiros).
///
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
Falcon é outro framework Python de alta performance, projetado para ser minimalista, e servir como base para outros frameworks como Hug.
Ele é projetado para ter funções que recebem dois parâmetros, uma "request" e uma "response". Então você "lê" partes da requisição, e "escreve" partes para a resposta. Por causa desse design, não é possível declarar parâmetros de requisição e corpos com as anotações de tipo padrão do Python como parâmetros de função.
Então, validação de dados, serialização e documentação têm que ser feitos no código, não automaticamente. Ou eles têm que ser implementados como um framework acima do Falcon, como o Hug. Essa mesma distinção acontece em outros frameworks inspirados pelo design do Falcon, de ter um objeto de request e um objeto de response como parâmetros.
/// check | **FastAPI** inspirado para
Encontrar maneiras de obter uma ótima performance.
Juntamente com Hug (como Hug é baseado no Falcon) inspirou **FastAPI** a declarar um parâmetro de `response` nas funções.
Embora no FastAPI seja opcional, é utilizado principalmente para configurar cabeçalhos, cookies e códigos de status alternativos.
///
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
Eu descobri Molten nos primeiros estágios da construção do **FastAPI**. E ele tem ideias bastante similares:
* Baseado nas anotações de tipo do Python.
* Validação e documentação a partir desses tipos.
* Sistema de Injeção de Dependência.
Ele não utiliza uma biblioteca de terceiros para validação de dados, serialização e documentação como o Pydantic, ele tem a sua própria. Então, essas definições de tipos de dados não seriam reutilizáveis tão facilmente.
Ele exige configurações um pouco mais verbosas. E como é baseado em WSGI (em vez de ASGI), ele não é projetado para tirar vantagem da alta performance fornecida por ferramentas como Uvicorn, Starlette e Sanic.
O sistema de injeção de dependência exige pré-registro das dependências e elas são resolvidas com base nos tipos declarados. Então, não é possível declarar mais de um "componente" que forneça um certo tipo.
As rotas são declaradas em um único lugar, usando funções declaradas em outros lugares (em vez de usar decorators que possam ser colocados diretamente acima da função que lida com o endpoint). Isso é mais próximo de como o Django faz do que de como o Flask (e o Starlette) fazem. Separa no código coisas que são relativamente bem acopladas.
/// check | **FastAPI** inspirado para
Definir validações extras para tipos de dados usando o valor "padrão" de atributos dos modelos. Isso melhora o suporte do editor, e não estava disponível no Pydantic antes.
Isso na verdade inspirou a atualização de partes do Pydantic, para dar suporte ao mesmo estilo de declaração da validação (toda essa funcionalidade já está disponível no Pydantic).
///
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
Hug foi um dos primeiros frameworks a implementar a declaração de tipos de parâmetros de API usando anotações de tipo do Python. Isso foi uma ótima ideia que inspirou outras ferramentas a fazer o mesmo.
Ele usou tipos personalizados em suas declarações em vez dos tipos padrão do Python, mas mesmo assim foi um grande passo adiante.
Ele também foi um dos primeiros frameworks a gerar um schema personalizado declarando a API inteira em JSON.
Ele não era baseado em um padrão como OpenAPI e JSON Schema. Então não seria simples integrá-lo com outras ferramentas, como Swagger UI. Mas novamente, era uma ideia muito inovadora.
Ele tem um recurso interessante e incomum: usando o mesmo framework, é possível criar APIs e também CLIs.
Como é baseado no padrão anterior para frameworks web Python síncronos (WSGI), ele não consegue lidar com Websockets e outras coisas, embora ainda tenha alta performance também.
/// info | Informação
Hug foi criado por Timothy Crosley, o mesmo criador do <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>, uma ótima ferramenta para ordenar automaticamente imports em arquivos Python.
///
/// check | Ideias que inspiraram o **FastAPI**
Hug inspirou partes do APIStar, e foi uma das ferramentas que achei mais promissoras, ao lado do APIStar.
Hug ajudou a inspirar o **FastAPI** a usar anotações de tipo do Python para declarar parâmetros e para gerar um schema definindo a API automaticamente.
Hug inspirou **FastAPI** a declarar um parâmetro de `response` em funções para definir cabeçalhos e cookies.
///
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
Pouco antes de decidir construir o **FastAPI** eu encontrei o servidor **APIStar**. Ele tinha quase tudo o que eu estava procurando e tinha um ótimo design.
Foi uma das primeiras implementações de um framework usando anotações de tipo do Python para declarar parâmetros e requisições que eu já vi (antes do NestJS e Molten). Eu o encontrei mais ou menos na mesma época que o Hug. Mas o APIStar utilizava o padrão OpenAPI.
Ele tinha validação de dados automática, serialização de dados e geração de schema OpenAPI baseadas nas mesmas anotações de tipo em vários locais.
As definições de schema de corpo não utilizavam as mesmas anotações de tipo do Python como o Pydantic, eram um pouco mais similares ao Marshmallow, então o suporte do editor não seria tão bom, ainda assim, APIStar era a melhor opção disponível.
Ele obteve os melhores benchmarks de performance na época (somente ultrapassado por Starlette).
A princípio, ele não tinha uma interface web com documentação automática da API, mas eu sabia que poderia adicionar o Swagger UI a ele.
Ele tinha um sistema de injeção de dependência. Exigia pré-registro dos componentes, como outras ferramentas já discutidas acima. Mas ainda assim era um grande recurso.
Eu nunca fui capaz de usá-lo em um projeto completo, pois ele não tinha integração de segurança, então, eu não pude substituir todos os recursos que eu tinha com os geradores full-stack baseados no Flask-apispec. Eu tinha no meu backlog de projetos criar um pull request adicionando essa funcionalidade.
Mas então, o foco do projeto mudou.
Ele não era mais um framework web de API, pois o criador precisava focar no Starlette.
Agora APIStar é um conjunto de ferramentas para validar especificações OpenAPI, não um framework web.
/// info | Informação
APIStar foi criado por Tom Christie. O mesmo cara que criou:
* Django REST Framework
* Starlette (no qual **FastAPI** é baseado)
* Uvicorn (usado por Starlette e **FastAPI**)
///
/// check | **FastAPI** inspirado para
Existir.
A ideia de declarar múltiplas coisas (validação de dados, serialização e documentação) com os mesmos tipos do Python, que ao mesmo tempo fornecessem grande suporte ao editor, era algo que eu considerava uma ideia brilhante.
E após procurar por muito tempo por um framework similar e testar muitas alternativas diferentes, APIStar foi a melhor opção disponível.
Então APIStar deixou de existir como servidor e o Starlette foi criado, sendo uma nova e melhor fundação para tal sistema. Essa foi a inspiração final para construir o **FastAPI**.
Eu considero o **FastAPI** um "sucessor espiritual" do APIStar, enquanto aprimora e amplia as funcionalidades, o sistema de tipagem e outras partes, baseado nos aprendizados de todas essas ferramentas anteriores.
///
## Usados por **FastAPI** { #used-by-fastapi }
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
Pydantic é uma biblioteca para definir validação de dados, serialização e documentação (usando JSON Schema) com base nas anotações de tipo do Python.
Isso o torna extremamente intuitivo.
Ele é comparável ao Marshmallow. Embora seja mais rápido que o Marshmallow em benchmarks. E como é baseado nas mesmas anotações de tipo do Python, o suporte do editor é ótimo.
/// check | **FastAPI** usa isso para
Controlar toda a validação de dados, serialização de dados e documentação automática de modelos (baseada no JSON Schema).
**FastAPI** então pega esses dados do JSON Schema e os coloca no OpenAPI, além de todas as outras coisas que faz.
///
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
Starlette é um framework/caixa de ferramentas <abbr title="O novo padrão para construir aplicações web Python assíncronas">ASGI</abbr> leve, o que é ideal para construir serviços asyncio de alta performance.
Ele é muito simples e intuitivo. É projetado para ser facilmente extensível, e ter componentes modulares.
Ele tem:
* Performance seriamente impressionante.
* Suporte a WebSocket.
* Tarefas em segundo plano dentro do processo.
* Eventos de inicialização e encerramento.
* Cliente de testes construído com HTTPX.
* CORS, GZip, Arquivos Estáticos, respostas Streaming.
* Suporte para Sessão e Cookie.
* 100% coberto por testes.
* Código base 100% anotado com tipagem.
* Poucas dependências obrigatórias.
Starlette é atualmente o framework Python mais rápido testado. Somente ultrapassado pelo Uvicorn, que não é um framework, mas um servidor.
Starlette fornece toda a funcionalidade básica de um microframework web.
Mas ele não fornece validação de dados automática, serialização ou documentação.
Essa é uma das principais coisas que o **FastAPI** adiciona por cima, tudo baseado nas anotações de tipo do Python (usando Pydantic). Isso, mais o sistema de injeção de dependência, utilidades de segurança, geração de schema OpenAPI, etc.
/// note | Detalhes Técnicos
ASGI é um novo "padrão" sendo desenvolvido por membros do time central do Django. Ele ainda não é um "padrão Python" (uma PEP), embora eles estejam no processo de fazer isso.
No entanto, ele já está sendo utilizado como "padrão" por diversas ferramentas. Isso melhora enormemente a interoperabilidade, pois você poderia trocar Uvicorn por qualquer outro servidor ASGI (como Daphne ou Hypercorn), ou você poderia adicionar ferramentas compatíveis com ASGI, como `python-socketio`.
///
/// check | **FastAPI** usa isso para
Controlar todas as partes web centrais. Adiciona funcionalidades por cima.
A classe `FastAPI` em si herda diretamente da classe `Starlette`.
Então, qualquer coisa que você pode fazer com Starlette, você pode fazer diretamente com o **FastAPI**, pois ele é basicamente um Starlette com esteróides.
///
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
Uvicorn é um servidor ASGI extremamente rápido, construído com uvloop e httptools.
Ele não é um framework web, mas sim um servidor. Por exemplo, ele não fornece ferramentas para roteamento por paths. Isso é algo que um framework como Starlette (ou **FastAPI**) forneceria por cima.
Ele é o servidor recomendado para Starlette e **FastAPI**.
/// check | **FastAPI** o recomenda como
O principal servidor web para rodar aplicações **FastAPI**.
Você também pode usar a opção de linha de comando `--workers` para ter um servidor assíncrono multi-processos.
Verifique mais detalhes na seção [Deployment](deployment/index.md){.internal-link target=_blank}.
///
## Benchmarks e velocidade { #benchmarks-and-speed }
Para entender, comparar e ver a diferença entre Uvicorn, Starlette e FastAPI, verifique a seção sobre [Benchmarks](benchmarks.md){.internal-link target=_blank}.
+444
View File
@@ -0,0 +1,444 @@
# Concorrência e async / await { #concurrency-and-async-await }
Detalhes sobre a sintaxe `async def` para *funções de operação de rota* e alguns conceitos de código assíncrono, concorrência e paralelismo.
## Com pressa? { #in-a-hurry }
<abbr title="too long; didn't read muito longo; não li"><strong>TL;DR:</strong></abbr>
Se você estiver utilizando bibliotecas de terceiros que dizem para você chamar as funções com `await`, como:
```Python
results = await some_library()
```
Então, declare suas *funções de operação de rota* com `async def` como:
```Python hl_lines="2"
@app.get('/')
async def read_results():
results = await some_library()
return results
```
/// note | Nota
Você só pode usar `await` dentro de funções criadas com `async def`.
///
---
Se você está usando uma biblioteca de terceiros que se comunica com alguma coisa (um banco de dados, uma API, o sistema de arquivos etc.) e não tem suporte para utilizar `await` (esse é atualmente o caso para a maioria das bibliotecas de banco de dados), então declare suas *funções de operação de rota* normalmente, com apenas `def`, como:
```Python hl_lines="2"
@app.get('/')
def results():
results = some_library()
return results
```
---
Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e esperar que o respondam, use `async def`, mesmo que você não precise usar `await` dentro dela.
---
Se você simplesmente não sabe, use apenas `def`.
---
**Note**: Você pode misturar `def` e `async def` nas suas *funções de operação de rota* tanto quanto necessário e definir cada função usando a melhor opção para você. FastAPI irá fazer a coisa certa com elas.
De qualquer forma, em ambos os casos acima, FastAPI irá trabalhar assincronamente e ser extremamente rápido.
Mas, seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance.
## Detalhes Técnicos { #technical-details }
Versões modernas de Python têm suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
Vamos ver aquela frase por partes nas seções abaixo:
* **Código assíncrono**
* **`async` e `await`**
* **Corrotinas**
## Código assíncrono { #asynchronous-code }
Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto do código, ele 🤖 terá que esperar *algo* finalizar em outro lugar. Vamos dizer que esse *algo* seja chamado "arquivo lento" 📝.
Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termina.
Então o computador / programa 🤖 irá voltar sempre que tiver uma chance, seja porque ele está esperando novamente, ou quando ele 🤖 terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram de fazer o que quer que tinham que fazer.
Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que tem que fazer com ela.
Esse "esperar por algo" normalmente se refere a operações <abbr title="Input and Output Entrada e Saída">I/O</abbr> que são relativamente "lentas" (comparadas à velocidade do processador e da memória RAM), como esperar por:
* dados do cliente para serem enviados através da rede
* dados enviados pelo seu programa serem recebidos pelo cliente através da rede
* conteúdo de um arquivo no disco ser lido pelo sistema e entregue ao seu programa
* conteúdo que seu programa deu ao sistema para ser escrito no disco
* uma operação em uma API remota
* uma operação no banco de dados finalizar
* uma solicitação no banco de dados retornar o resultado
* etc.
Quanto o tempo de execução é consumido majoritariamente pela espera de operações <abbr title="Input and Output Entrada e Saída">I/O</abbr>, essas operações são chamadas operações "limitadas por I/O".
Isso é chamado de "assíncrono" porque o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo momento exato em que a tarefa finaliza, enquanto não faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho.
Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar na fila um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo, e então volte para pegar o resultado e continue trabalhando com ele.
Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, em sequência, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar.
### Concorrência e hambúrgueres { #concurrency-and-burgers }
Essa ideia de código **assíncrono** descrita acima é às vezes chamada de **"concorrência"**. Isso é diferente de **"paralelismo"**.
**Concorrência** e **paralelismo** ambos são relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo".
Mas os detalhes entre *concorrência* e *paralelismo* são bem diferentes.
Para ver essa diferença, imagine a seguinte história sobre hambúrgueres:
### Hambúrgueres concorrentes { #concurrent-burgers }
Você vai com seu _crush_ na lanchonete, e fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente. 😍
<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration">
Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_. 🍔🍔
<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration">
O caixa diz alguma coisa para o cozinheiro na cozinha para que eles saibam que têm que preparar seus hambúrgueres (mesmo que ele esteja atualmente preparando os lanches dos outros clientes).
<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration">
Você paga. 💸
O caixa te entrega seu número de chamada.
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration">
Enquanto você espera, você vai com seu _crush_ e pega uma mesa, senta e conversa com seu _crush_ por um bom tempo (já que seus hambúrgueres são muito saborosos, e leva um tempo para serem preparados).
Já que você está sentado na mesa com seu _crush_, esperando os hambúrgueres, você pode passar esse tempo admirando o quão lindo, maravilhoso e esperto é seu _crush_ ✨😍✨.
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration">
Enquanto espera e conversa com seu _crush_, de tempos em tempos, você verifica o número da chamada exibido no balcão para ver se já é sua vez.
Então em algum momento, é finalmente sua vez. Você vai ao balcão, pega seus hambúrgueres e volta para a mesa.
<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration">
Você e seu _crush_ comem os hambúrgueres e aproveitam o tempo. ✨
<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration">
/// info | Informação
Belas ilustrações de <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
Imagine que você seja o computador / programa 🤖 nessa história.
Enquanto você está na fila, você está somente ocioso 😴, esperando por sua vez, sem fazer nada muito "produtivo". Mas a fila é rápida porque o caixa só está pegando os pedidos (não os preparando), então está tudo bem.
Então, quando é sua vez, você faz trabalho realmente "produtivo", você processa o menu, decide o que quer, pega a escolha de seu _crush_, paga, verifica se entregou o cartão ou a cédula correta, verifica se foi cobrado corretamente, verifica se seu pedido está correto etc.
Mas então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado" ⏸, porque você tem que esperar 🕙 seus hambúrgueres ficarem prontos.
Contudo, à medida que você se afasta do balcão e senta na mesa, com um número para sua chamada, você pode trocar 🔀 sua atenção para seu _crush_, e "trabalhar" ⏯ 🤓 nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ 😍.
Então o caixa 💁 diz que "seus hambúrgueres estão prontos" colocando seu número no balcão, mas você não corre que nem um maluco imediatamente quando o número exibido é o seu. Você sabe que ninguém irá roubar seus hambúrgueres porque você tem o seu número da chamada, e os outros têm os deles.
Então você espera seu _crush_ terminar a história que estava contando (terminar o trabalho atual ⏯ / tarefa sendo processada 🤓), sorri gentilmente e diz que você está indo buscar os hambúrgueres ⏸.
Então você vai ao balcão 🔀, para a tarefa inicial que agora está finalizada ⏯, pega os hambúrgueres, agradece, e leva-os para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão ⏹. Isso, por sua vez, cria uma nova tarefa, a de "comer hambúrgueres" 🔀 ⏯, mas a tarefa anterior de "pegar os hambúrgueres" já está finalizada ⏹.
### Hambúrgueres paralelos { #parallel-burgers }
Agora vamos imaginar que esses não são "Hambúrgueres Concorrentes", e sim "Hambúrgueres Paralelos".
Você vai com seu _crush_ na lanchonete paralela.
Você fica na fila enquanto vários (vamos dizer 8) caixas que também são cozinheiros pegam os pedidos das pessoas na sua frente.
Todo mundo na sua frente está esperando seus hambúrgueres ficarem prontos antes de deixar o caixa porque cada um dos 8 caixas vai e prepara o hambúrguer logo após receber o pedido, antes de pegar o próximo pedido.
<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration">
Então é finalmente sua vez, você pede 2 hambúrgueres muito saborosos para você e seu _crush_.
Você paga 💸.
<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration">
O caixa vai para a cozinha.
Você espera, na frente do balcão 🕙, para que ninguém pegue seus hambúrgueres antes de você, já que não tem números de chamadas.
<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration">
Como você e seu _crush_ estão ocupados não permitindo que ninguém passe na frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_. 😞
Isso é trabalho "síncrono", você está "sincronizado" com o caixa / cozinheiro 👨‍🍳. Você tem que esperar 🕙 e estar lá no exato momento que o caixa / cozinheiro 👨‍🍳 terminar os hambúrgueres e os der a você, ou então, outro alguém pode pegá-los.
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration">
Então seu caixa / cozinheiro 👨‍🍳 finalmente volta com seus hambúrgueres, depois de um longo tempo esperando 🕙 por eles em frente ao balcão.
<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration">
Você pega seus hambúrgueres e vai para a mesa com seu _crush_.
Vocês comem os hambúrgueres, e o trabalho está terminado. ⏹
<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration">
Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando 🕙 na frente do balcão. 😞
/// info | Informação
Belas ilustrações de <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
Nesse cenário dos hambúrgueres paralelos, você é um computador / programa 🤖 com dois processadores (você e seu _crush_), ambos esperando 🕙 e dedicando sua atenção ⏯ "esperando no balcão" 🕙 por um bom tempo.
A lanchonete paralela tem 8 processadores (caixas / cozinheiros), enquanto a lanchonete dos hambúrgueres concorrentes tinha apenas 2 (um caixa e um cozinheiro).
Ainda assim, a experiência final não foi a melhor. 😞
---
Essa seria o equivalente paralelo à história dos hambúrgueres. 🍔
Para um exemplo "mais real", imagine um banco.
Até recentemente, a maioria dos bancos tinham muitos caixas 👨‍💼👨‍💼👨‍💼👨‍💼 e uma grande fila 🕙🕙🕙🕙🕙🕙🕙🕙.
Todos os caixas fazendo todo o trabalho, um cliente após o outro 👨‍💼⏯.
E você tinha que esperar 🕙 na fila por um longo tempo ou poderia perder a vez.
Você provavelmente não gostaria de levar seu _crush_ 😍 com você para um rolezinho no banco 🏦.
### Conclusão dos hambúrgueres { #burger-conclusion }
Nesse cenário dos "hambúrgueres com seu _crush_", como tem muita espera, faz mais sentido ter um sistema concorrente ⏸🔀⏯.
Esse é o caso da maioria das aplicações web.
Muitos, muitos usuários, mas seu servidor está esperando 🕙 pela sua conexão não tão boa enviar suas requisições.
E então esperando 🕙 novamente as respostas voltarem.
Essa "espera" 🕙 é medida em microssegundos, mas ainda assim, somando tudo, é um monte de espera no final.
Por isso que faz bastante sentido utilizar código assíncrono ⏸🔀⏯ para APIs web.
Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e essa é a força do Go como uma linguagem de programação.
E esse é o mesmo nível de performance que você tem com o **FastAPI**.
E como você pode ter paralelismo e assincronicidade ao mesmo tempo, você tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que é uma linguagem compilada, mais próxima ao C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(tudo graças ao Starlette)</a>.
### Concorrência é melhor que paralelismo? { #is-concurrency-better-than-parallelism }
Não! Essa não é a moral da história.
Concorrência é diferente de paralelismo. E é melhor em cenários **específicos** que envolvam um monte de espera. Por isso, geralmente é muito melhor do que paralelismo para desenvolvimento de aplicações web. Mas não para tudo.
Então, para equilibrar tudo, imagine a seguinte historinha:
> Você tem que limpar uma casa grande e suja.
*Sim, essa é toda a história*.
---
Não há espera 🕙 em lugar algum, apenas um monte de trabalho para ser feito, em múltiplos cômodos da casa.
Você poderia ter turnos como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas como você não está esperando por nada, apenas limpando e limpando, as chamadas não afetariam em nada.
Levaria o mesmo tempo para finalizar com ou sem turnos (concorrência) e você teria feito o mesmo tanto de trabalho.
Mas nesse caso, se você trouxesse os 8 ex-caixas / cozinheiros / agora-faxineiros, e cada um deles (mais você) pudessem dividir a casa para limpá-la, vocês fariam toda a limpeza em **paralelo**, com a ajuda extra, e terminariam muito mais cedo.
Nesse cenário, cada um dos faxineiros (incluindo você) poderia ser um processador, fazendo a sua parte do trabalho.
E a maior parte do tempo de execução é tomada por trabalho real (ao invés de ficar esperando), e o trabalho em um computador é feito pela <abbr title="Central Processing Unit Unidade Central de Processamento">CPU</abbr>. Eles chamam esses problemas de "limitados por CPU".
---
Exemplos comuns de operações limitadas por CPU são coisas que exigem processamento matemático complexo.
Por exemplo:
* **Processamento de áudio** ou **imagem**
* **Visão Computacional**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores / cores, processar isso normalmente exige alguma computação em todos esses pixels ao mesmo tempo
* **Aprendizado de Máquina**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande planilha com números e em multiplicar todos eles juntos e ao mesmo tempo.
* **Deep Learning**: Esse é um subcampo do Aprendizado de Máquina, então, o mesmo se aplica. A diferença é que não há apenas uma grande planilha com números para multiplicar, mas um grande conjunto delas, e em muitos casos, você utiliza um processador especial para construir e/ou usar esses modelos.
### Concorrência + Paralelismo: Web + Aprendizado de Máquina { #concurrency-parallelism-web-machine-learning }
Com **FastAPI** você pode levar a vantagem da concorrência que é muito comum para desenvolvimento web (o mesmo atrativo de NodeJS).
Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos **limitados por CPU** como aqueles em sistemas de Aprendizado de Máquina.
Isso, somado ao simples fato que Python é a principal linguagem para **Data Science**, Aprendizado de Máquina e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicações com Data Science / Aprendizado de Máquina (entre muitas outras).
Para ver como alcançar esse paralelismo em produção veja a seção sobre [Implantação](deployment/index.md){.internal-link target=_blank}.
## `async` e `await` { #async-and-await }
Versões modernas do Python têm um modo muito intuitivo para definir código assíncrono. Isso faz parecer do mesmo jeito do código normal "sequencial" e fazer a "espera" para você nos momentos certos.
Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses novos recursos do Python, você pode escrever assim:
```Python
burgers = await get_burgers(2)
```
A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar ⏸ por `get_burgers(2)` finalizar suas coisas 🕙 antes de armazenar os resultados em `burgers`. Com isso, o Python saberá que ele pode ir e fazer outras coisas 🔀 ⏯ nesse meio tempo (como receber outra requisição).
Para o `await` funcionar, tem que estar dentro de uma função que suporte essa assincronicidade. Para fazer isso, apenas declare a função com `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
# Faz alguma coisa assíncrona para criar os hambúrgueres
return burgers
```
...ao invés de `def`:
```Python hl_lines="2"
# Isso não é assíncrono
def get_sequential_burgers(number: int):
# Faz alguma coisa sequencial para criar os hambúrgueres
return burgers
```
Com `async def`, o Python sabe que, dentro dessa função, ele deve estar ciente das expressões `await`, e que isso poderá "pausar" ⏸ a execução dessa função, e ir fazer outra coisa 🔀 antes de voltar.
Quando você quiser chamar uma função `async def`, você tem que "esperar" ela. Então, isso não funcionará:
```Python
# Isso não irá funcionar, porquê get_burgers foi definido com: async def
burgers = get_burgers(2)
```
---
Então, se você está usando uma biblioteca que diz que você pode chamá-la com `await`, você precisa criar as *funções de operação de rota* com `async def`, como em:
```Python hl_lines="2-3"
@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers
```
### Mais detalhes técnicos { #more-technical-details }
Você deve ter observado que `await` pode ser usado somente dentro de funções definidas com `async def`.
Mas ao mesmo tempo, funções definidas com `async def` têm que ser "aguardadas". Então, funções com `async def` podem ser chamadas somente dentro de funções definidas com `async def` também.
Então, sobre o ovo e a galinha, como você chama a primeira função async?
Se você estivar trabalhando com **FastAPI** não terá que se preocupar com isso, porquê essa "primeira" função será a sua *função de operação de rota*, e o FastAPI saberá como fazer a coisa certa.
Mas se você quiser usar `async` / `await` sem FastAPI, você também pode fazê-lo.
### Escreva seu próprio código assíncrono { #write-your-own-async-code }
Starlette (e **FastAPI**) são baseados no <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, o que o torna compatível com ambos o <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> da biblioteca padrão do Python, e o <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
Em particular, você pode usar diretamente o <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> para seus casos de uso avançados de concorrência que requerem padrões mais avançados no seu próprio código.
E até se você não estiver utilizando FastAPI, você também pode escrever suas próprias aplicações assíncronas com o <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> por ser altamente compatível e ganhar seus benefícios (e.g. *concorrência estruturada*).
Eu criei outra biblioteca em cima do AnyIO, como uma fina camada acima, para melhorar um pouco as anotações de tipo e obter melhor **preenchimento automático**, **erros inline**, etc. Ela também possui uma introdução amigável e um tutorial para ajudar você a **entender** e escrever **seu próprio código async**: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. Seria particularmente útil se você precisar **combinar código async com código regular** (bloqueador/síncrono).
### Outras formas de código assíncrono { #other-forms-of-asynchronous-code }
Esse estilo de usar `async` e `await` é relativamente novo na linguagem.
Mas ele faz o trabalho com código assíncrono muito mais fácil.
Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em versões modernas do JavaScript (no navegador e NodeJS).
Mas antes disso, controlar código assíncrono era bem mais complexo e difícil.
Nas versões anteriores do Python, você poderia utilizar threads ou <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Mas o código é bem mais complexo de entender, debugar, e pensar sobre.
Nas versões anteriores do NodeJS / Navegador JavaScript, você utilizaria "callbacks". O que leva ao "inferno do callback".
## Corrotinas { #coroutines }
**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é algo como uma função, que pode começar e que vai terminar em algum ponto, mas que pode ser pausada ⏸ internamente também, sempre que tiver um `await` dentro dela.
Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como usando "corrotinas". É comparável ao principal recurso chave do Go, a "Gorrotina".
## Conclusão { #conclusion }
Vamos ver a mesma frase de cima:
> Versões modernas do Python têm suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
Isso pode fazer mais sentido agora. ✨
Tudo isso é o que empodera o FastAPI (através do Starlette) e que o faz ter uma performance tão impressionante.
## Detalhes muito técnicos { #very-technical-details }
/// warning | Atenção
Você pode provavelmente pular isso.
Esses são detalhes muito técnicos de como **FastAPI** funciona por baixo do capô.
Se você tem certo conhecimento técnico (corrotinas, threads, blocking etc) e está curioso sobre como o FastAPI controla o `async def` vs normal `def`, vá em frente.
///
### Funções de operação de rota { #path-operation-functions }
Quando você declara uma *função de operação de rota* com `def` normal ao invés de `async def`, ela é rodada em uma threadpool externa que é então aguardada, ao invés de ser chamada diretamente (já que ela bloquearia o servidor).
Se você está chegando de outro framework assíncrono que não funciona como descrito acima e você está acostumado a definir *funções de operação de rota* triviais somente de computação com simples `def` para ter um mínimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no **FastAPI** o efeito pode ser bem o oposto. Nesses casos, é melhor usar `async def` a menos que suas *funções de operação de rota* utilizem código que performe bloqueamento <abbr title="Input/Output Entrada e Saída: leitura ou escrita no disco, comunicações de rede.">I/O</abbr>.
Ainda, em ambas as situações, as chances são que o **FastAPI** [ainda será mais rápido](index.md#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seu framework anterior.
### Dependências { #dependencies }
O mesmo se aplica para as [dependências](tutorial/dependencies/index.md){.internal-link target=_blank}. Se uma dependência tem as funções com padrão `def` ao invés de `async def`, ela é rodada no threadpool externo.
### Sub-dependências { #sub-dependencies }
Você pode ter múltiplas dependências e [sub-dependências](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} requisitando uma à outra (como parâmetros de definições de funções), algumas delas podem ser criadas com `async def` e algumas com `def` normal. Isso ainda funcionaria, e aquelas criadas com `def` normal seriam chamadas em uma thread externa (do threadpool) ao invés de serem "aguardadas".
### Outras funções de utilidade { #other-utility-functions }
Qualquer outra função de utilidade que você chame diretamente pode ser criada com `def` normal ou `async def` e o FastAPI não irá afetar o modo como você a chama.
Isso está em contraste às funções que o FastAPI chama para você: *funções de operação de rota* e dependências.
Se sua função de utilidade é uma função normal com `def`, ela será chamada diretamente (como você a escreve no código), não em uma threadpool, se a função é criada com `async def` então você deve esperar por essa função quando você chamá-la no seu código.
---
Novamente, esses são detalhes muito técnicos que provavelmente seriam úteis caso você esteja procurando por eles.
Caso contrário, você deve ficar bem com as dicas da seção acima: <a href="#in-a-hurry">Com pressa?</a>.
+34
View File
@@ -0,0 +1,34 @@
# Benchmarks { #benchmarks }
Benchmarks independentes da TechEmpower mostram as aplicações **FastAPI** rodando com Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">um dos frameworks Python mais rápidos disponíveis</a>, somente atrás dos próprios Starlette e Uvicorn (utilizados internamente pelo FastAPI).
Mas quando se checa _benchmarks_ e comparações você deveria ter o seguinte em mente.
## Benchmarks e velocidade { #benchmarks-and-speed }
Ao verificar os _benchmarks_, é comum observar algumas ferramentas de diferentes tipos comparadas como equivalentes.
Especificamente, observa-se Uvicorn, Starlette e FastAPI comparados juntos (entre muitas outras ferramentas).
Quanto mais simples o problema resolvido pela ferramenta, melhor a performance que ela terá. E a maioria dos _benchmarks_ não testam as características adicionais fornecidas pela ferramenta.
A hierarquia segue assim:
* **Uvicorn**: um servidor ASGI
* **Starlette**: (utiliza Uvicorn) um _microframework web_
* **FastAPI**: (utiliza Starlette) um _microframework_ de API com vários recursos adicionais para construção de APIs, com validação de dados, etc.
* **Uvicorn**:
* Terá a melhor performance, já que ele não tem muito código extra além do servidor em si.
* Você não conseguiria escrever uma aplicação em Uvicorn diretamente. Isso significa que seu código deveria conter, mais ou menos, todo o código fornecido pelo Starlette (ou **FastAPI**). E se você fizesse isso, sua aplicação final poderia ter a mesma sobrecarga que utilizar um _framework_ que minimiza o código e _bugs_ da sua aplicação.
* Se você quer fazer comparações com o Uvicorn, compare com Daphne, Hypercorn, uWSGI, etc. Servidores de Aplicação.
* **Starlette**:
* Terá a melhor performance, depois do Uvicorn. De fato, Starlette utiliza Uvicorn para rodar. Então, ele provavelmente será "mais lento" que Uvicorn por ter que executar mais código.
* Mas ele fornece a você as ferramentas para construir aplicações _web_ simples, com roteamento baseado em caminhos, etc.
* Se você quer fazer comparações com o Starlette, compare com Sanic, Flask, Django, etc. _Frameworks Web_ (ou _microframeworks_).
* **FastAPI**:
* Do mesmo modo que Starlette utiliza Uvicorn e não pode ser mais rápido que ele, **FastAPI** utiliza o Starlette, então não tem como ser mais rápido do que o Starlette.
* FastAPI fornece mais recursos acima do Starlette. Recursos que você quase sempre precisará quando construir APIs, como validação de dados e serialização. E utilizando eles, você terá uma documentação automática de graça (a documentação automática nem sequer adiciona peso para rodar as aplicações, ela é gerada na inicialização).
* Se você nunca utilizou FastAPI mas utilizou diretamente o Starlette (ou outra ferramenta, como Sanic, Flask, Responder, etc) você teria que implementar toda validação de dados e serialização por conta. Então, sua aplicação final poderia ainda ter a mesma sobrecarga como se fosse desenvolvida com FastAPI. Em muitos casos, a validação de dados e serialização é o maior pedaço de código escrito em aplicações.
* Então, ao utilizar o FastAPI você estará economizando tempo de desenvolvimento, evitará _bugs_, linhas de código, e você provavelmente terá a mesma performance (ou melhor) do que não utilizá-lo (já que você teria que implementar tudo isso em seu código).
* Se você quer fazer comparações com o FastAPI, compare com um _framework_ (ou conjunto de ferramentas) para aplicações _web_ que forneça validação de dados, serialização e documentação, como Flask-apispec, NestJS, Molten, etc. _Frameworks_ com validação de dados automática, serialização e documentação integradas.
+16
View File
@@ -0,0 +1,16 @@
# Implantar FastAPI em provedores de nuvem { #deploy-fastapi-on-cloud-providers }
Você pode usar praticamente **qualquer provedor de nuvem** para implantar seu aplicativo FastAPI.
Na maioria dos casos, os principais provedores de nuvem têm tutoriais para implantar o FastAPI com eles.
## Provedores de Nuvem - Patrocinadores { #cloud-providers-sponsors }
Alguns provedores de nuvem ✨ [**patrocinam o FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, o que garante o **desenvolvimento** contínuo e saudável do FastAPI e seu **ecossistema**.
E isso mostra seu verdadeiro comprometimento com o FastAPI e sua **comunidade** (você), pois eles não querem apenas fornecer a você um **bom serviço**, mas também querem ter certeza de que você tenha um **framework bom e saudável**, o FastAPI. 🙇
Talvez você queira experimentar os serviços deles e seguir os tutoriais:
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>
+321
View File
@@ -0,0 +1,321 @@
# Conceitos de Implantações { #deployments-concepts }
Ao implantar um aplicativo **FastAPI**, ou na verdade, qualquer tipo de API da web, há vários conceitos com os quais você provavelmente se importa e, usando-os, você pode encontrar a maneira **mais apropriada** de **implantar seu aplicativo**.
Alguns dos conceitos importantes são:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Etapas anteriores antes de iniciar
Veremos como eles afetariam as **implantações**.
No final, o principal objetivo é ser capaz de **atender seus clientes de API** de uma forma **segura**, **evitar interrupções** e usar os **recursos de computação** (por exemplo, servidores remotos/máquinas virtuais) da forma mais eficiente possível. 🚀
Vou lhe contar um pouco mais sobre esses **conceitos** aqui, e espero que isso lhe dê a **intuição** necessária para decidir como implantar sua API em ambientes muito diferentes, possivelmente até mesmo em **futuros** ambientes que ainda não existem.
Ao considerar esses conceitos, você será capaz de **avaliar e projetar** a melhor maneira de implantar **suas próprias APIs**.
Nos próximos capítulos, darei a você mais **receitas concretas** para implantar aplicativos FastAPI.
Mas por enquanto, vamos verificar essas importantes **ideias conceituais**. Esses conceitos também se aplicam a qualquer outro tipo de API da web. 💡
## Segurança - HTTPS { #security-https }
No [capítulo anterior sobre HTTPS](https.md){.internal-link target=_blank} aprendemos como o HTTPS fornece criptografia para sua API.
Também vimos que o HTTPS normalmente é fornecido por um componente **externo** ao seu servidor de aplicativos, um **Proxy de terminação TLS**.
E tem que haver algo responsável por **renovar os certificados HTTPS**, pode ser o mesmo componente ou pode ser algo diferente.
### Ferramentas de exemplo para HTTPS { #example-tools-for-https }
Algumas das ferramentas que você pode usar como um proxy de terminação TLS são:
* Traefik
* Lida automaticamente com renovações de certificados ✨
* Caddy
* Lida automaticamente com renovações de certificados ✨
* Nginx
* Com um componente externo como o Certbot para renovações de certificados
* HAProxy
* Com um componente externo como o Certbot para renovações de certificados
* Kubernetes com um controlador Ingress como o Nginx
* Com um componente externo como cert-manager para renovações de certificados
* Gerenciado internamente por um provedor de nuvem como parte de seus serviços (leia abaixo 👇)
Outra opção é que você poderia usar um **serviço de nuvem** que faz mais do trabalho, incluindo a configuração de HTTPS. Ele pode ter algumas restrições ou cobrar mais, etc. Mas, nesse caso, você não teria que configurar um Proxy de terminação TLS sozinho.
Mostrarei alguns exemplos concretos nos próximos capítulos.
---
Os próximos conceitos a serem considerados são todos sobre o programa que executa sua API real (por exemplo, Uvicorn).
## Programa e Processo { #program-and-process }
Falaremos muito sobre o "**processo**" em execução, então é útil ter clareza sobre o que ele significa e qual é a diferença com a palavra "**programa**".
### O que é um Programa { #what-is-a-program }
A palavra **programa** é comumente usada para descrever muitas coisas:
* O **código** que você escreve, os **arquivos Python**.
* O **arquivo** que pode ser **executado** pelo sistema operacional, por exemplo: `python`, `python.exe` ou `uvicorn`.
* Um programa específico enquanto está **em execução** no sistema operacional, usando a CPU e armazenando coisas na memória. Isso também é chamado de **processo**.
### O que é um Processo { #what-is-a-process }
A palavra **processo** normalmente é usada de forma mais específica, referindo-se apenas ao que está sendo executado no sistema operacional (como no último ponto acima):
* Um programa específico enquanto está **em execução** no sistema operacional.
* Isso não se refere ao arquivo, nem ao código, refere-se **especificamente** à coisa que está sendo **executada** e gerenciada pelo sistema operacional.
* Qualquer programa, qualquer código, **só pode fazer coisas** quando está sendo **executado**. Então, quando há um **processo em execução**.
* O processo pode ser **terminado** (ou "morto") por você, ou pelo sistema operacional. Nesse ponto, ele para de rodar/ser executado, e ele **não pode mais fazer coisas**.
* Cada aplicativo que você tem em execução no seu computador tem algum processo por trás dele, cada programa em execução, cada janela, etc. E normalmente há muitos processos em execução **ao mesmo tempo** enquanto um computador está ligado.
* Pode haver **vários processos** do **mesmo programa** em execução ao mesmo tempo.
Se você verificar o "gerenciador de tarefas" ou o "monitor do sistema" (ou ferramentas semelhantes) no seu sistema operacional, poderá ver muitos desses processos em execução.
E, por exemplo, você provavelmente verá que há vários processos executando o mesmo programa de navegador (Firefox, Chrome, Edge, etc.). Eles normalmente executam um processo por aba, além de alguns outros processos extras.
<img class="shadow" src="/img/deployment/concepts/image01.png">
---
Agora que sabemos a diferença entre os termos **processo** e **programa**, vamos continuar falando sobre implantações.
## Executando na inicialização { #running-on-startup }
Na maioria dos casos, quando você cria uma API web, você quer que ela esteja **sempre em execução**, ininterrupta, para que seus clientes possam sempre acessá-la. Isso é claro, a menos que você tenha um motivo específico para querer que ela seja executada somente em certas situações, mas na maioria das vezes você quer que ela esteja constantemente em execução e **disponível**.
### Em um servidor remoto { #in-a-remote-server }
Ao configurar um servidor remoto (um servidor em nuvem, uma máquina virtual, etc.), a coisa mais simples que você pode fazer é usar `fastapi run` (que usa Uvicorn) ou algo semelhante, manualmente, da mesma forma que você faz ao desenvolver localmente.
E funcionará e será útil **durante o desenvolvimento**.
Mas se sua conexão com o servidor for perdida, o **processo em execução** provavelmente morrerá.
E se o servidor for reiniciado (por exemplo, após atualizações ou migrações do provedor de nuvem), você provavelmente **não notará**. E por causa disso, você nem saberá que precisa reiniciar o processo manualmente. Então, sua API simplesmente permanecerá inativa. 😱
### Executar automaticamente na inicialização { #run-automatically-on-startup }
Em geral, você provavelmente desejará que o programa do servidor (por exemplo, Uvicorn) seja iniciado automaticamente na inicialização do servidor e, sem precisar de nenhuma **intervenção humana**, tenha um processo sempre em execução com sua API (por exemplo, Uvicorn executando seu aplicativo FastAPI).
### Programa separado { #separate-program }
Para conseguir isso, você normalmente terá um **programa separado** que garantiria que seu aplicativo fosse executado na inicialização. E em muitos casos, ele também garantiria que outros componentes ou aplicativos também fossem executados, por exemplo, um banco de dados.
### Ferramentas de exemplo para executar na inicialização { #example-tools-to-run-at-startup }
Alguns exemplos de ferramentas que podem fazer esse trabalho são:
* Docker
* Kubernetes
* Docker Compose
* Docker em Modo Swarm
* Systemd
* Supervisor
* Gerenciado internamente por um provedor de nuvem como parte de seus serviços
* Outros...
Darei exemplos mais concretos nos próximos capítulos.
## Reinicializações { #restarts }
Semelhante a garantir que seu aplicativo seja executado na inicialização, você provavelmente também deseja garantir que ele seja **reiniciado** após falhas.
### Nós cometemos erros { #we-make-mistakes }
Nós, como humanos, cometemos **erros** o tempo todo. O software quase *sempre* tem **bugs** escondidos em lugares diferentes. 🐛
E nós, como desenvolvedores, continuamos aprimorando o código à medida que encontramos esses bugs e implementamos novos recursos (possivelmente adicionando novos bugs também 😅).
### Pequenos erros são tratados automaticamente { #small-errors-automatically-handled }
Ao criar APIs da web com FastAPI, se houver um erro em nosso código, o FastAPI normalmente o conterá na única solicitação que acionou o erro. 🛡
O cliente receberá um **Erro Interno do Servidor 500** para essa solicitação, mas o aplicativo continuará funcionando para as próximas solicitações em vez de travar completamente.
### Erros maiores - Travamentos { #bigger-errors-crashes }
No entanto, pode haver casos em que escrevemos algum código que **trava todo o aplicativo**, fazendo com que o Uvicorn e o Python travem. 💥
E ainda assim, você provavelmente não gostaria que o aplicativo permanecesse inativo porque houve um erro em um lugar, você provavelmente quer que ele **continue em execução** pelo menos para as *operações de caminho* que não estão quebradas.
### Reiniciar após falha { #restart-after-crash }
Mas nos casos com erros realmente graves que travam o **processo** em execução, você vai querer um componente externo que seja responsável por **reiniciar** o processo, pelo menos algumas vezes...
/// tip | Dica
...Embora se o aplicativo inteiro estiver **travando imediatamente**, provavelmente não faça sentido reiniciá-lo para sempre. Mas nesses casos, você provavelmente notará isso durante o desenvolvimento, ou pelo menos logo após a implantação.
Então, vamos nos concentrar nos casos principais, onde ele pode travar completamente em alguns casos específicos **no futuro**, e ainda faz sentido reiniciá-lo.
///
Você provavelmente gostaria de ter a coisa responsável por reiniciar seu aplicativo como um **componente externo**, porque a essa altura, o mesmo aplicativo com Uvicorn e Python já havia travado, então não há nada no mesmo código do mesmo aplicativo que possa fazer algo a respeito.
### Ferramentas de exemplo para reiniciar automaticamente { #example-tools-to-restart-automatically }
Na maioria dos casos, a mesma ferramenta usada para **executar o programa na inicialização** também é usada para lidar com **reinicializações** automáticas.
Por exemplo, isso poderia ser resolvido por:
* Docker
* Kubernetes
* Docker Compose
* Docker no Modo Swarm
* Systemd
* Supervisor
* Gerenciado internamente por um provedor de nuvem como parte de seus serviços
* Outros...
## Replicação - Processos e Memória { #replication-processes-and-memory }
Com um aplicativo FastAPI, usando um programa de servidor como o comando `fastapi` que executa o Uvicorn, executá-lo uma vez em **um processo** pode atender a vários clientes simultaneamente.
Mas em muitos casos, você desejará executar vários processos de trabalho ao mesmo tempo.
### Processos Múltiplos - Trabalhadores { #multiple-processes-workers }
Se você tiver mais clientes do que um único processo pode manipular (por exemplo, se a máquina virtual não for muito grande) e tiver **vários núcleos** na CPU do servidor, você poderá ter **vários processos** em execução com o mesmo aplicativo ao mesmo tempo e distribuir todas as solicitações entre eles.
Quando você executa **vários processos** do mesmo programa de API, eles são comumente chamados de **trabalhadores**.
### Processos do Trabalhador e Portas { #worker-processes-and-ports }
Lembra da documentação [Sobre HTTPS](https.md){.internal-link target=_blank} que diz que apenas um processo pode escutar em uma combinação de porta e endereço IP em um servidor?
Isso ainda é verdade.
Então, para poder ter **vários processos** ao mesmo tempo, tem que haver um **único processo escutando em uma porta** que então transmite a comunicação para cada processo de trabalho de alguma forma.
### Memória por Processo { #memory-per-process }
Agora, quando o programa carrega coisas na memória, por exemplo, um modelo de aprendizado de máquina em uma variável, ou o conteúdo de um arquivo grande em uma variável, tudo isso **consome um pouco da memória (RAM)** do servidor.
E vários processos normalmente **não compartilham nenhuma memória**. Isso significa que cada processo em execução tem suas próprias coisas, variáveis e memória. E se você estiver consumindo uma grande quantidade de memória em seu código, **cada processo** consumirá uma quantidade equivalente de memória.
### Memória do servidor { #server-memory }
Por exemplo, se seu código carrega um modelo de Machine Learning com **1 GB de tamanho**, quando você executa um processo com sua API, ele consumirá pelo menos 1 GB de RAM. E se você iniciar **4 processos** (4 trabalhadores), cada um consumirá 1 GB de RAM. Então, no total, sua API consumirá **4 GB de RAM**.
E se o seu servidor remoto ou máquina virtual tiver apenas 3 GB de RAM, tentar carregar mais de 4 GB de RAM causará problemas. 🚨
### Processos Múltiplos - Um Exemplo { #multiple-processes-an-example }
Neste exemplo, há um **Processo Gerenciador** que inicia e controla dois **Processos de Trabalhadores**.
Este Processo de Gerenciador provavelmente seria o que escutaria na **porta** no IP. E ele transmitiria toda a comunicação para os processos de trabalho.
Esses processos de trabalho seriam aqueles que executariam seu aplicativo, eles executariam os cálculos principais para receber uma **solicitação** e retornar uma **resposta**, e carregariam qualquer coisa que você colocasse em variáveis na RAM.
<img src="/img/deployment/concepts/process-ram.drawio.svg">
E, claro, a mesma máquina provavelmente teria **outros processos** em execução, além do seu aplicativo.
Um detalhe interessante é que a porcentagem da **CPU usada** por cada processo pode **variar** muito ao longo do tempo, mas a **memória (RAM)** normalmente fica mais ou menos **estável**.
Se você tiver uma API que faz uma quantidade comparável de cálculos todas as vezes e tiver muitos clientes, então a **utilização da CPU** provavelmente *também será estável* (em vez de ficar constantemente subindo e descendo rapidamente).
### Exemplos de ferramentas e estratégias de replicação { #examples-of-replication-tools-and-strategies }
Pode haver várias abordagens para conseguir isso, e falarei mais sobre estratégias específicas nos próximos capítulos, por exemplo, ao falar sobre Docker e contêineres.
A principal restrição a ser considerada é que tem que haver um **único** componente manipulando a **porta** no **IP público**. E então tem que ter uma maneira de **transmitir** a comunicação para os **processos/trabalhadores** replicados.
Aqui estão algumas combinações e estratégias possíveis:
* **Uvicorn** com `--workers`
* Um **gerenciador de processos** Uvicorn escutaria no **IP** e na **porta** e iniciaria **vários processos de trabalho Uvicorn**.
* **Kubernetes** e outros **sistemas de contêineres** distribuídos
* Algo na camada **Kubernetes** escutaria no **IP** e na **porta**. A replicação seria por ter **vários contêineres**, cada um com **um processo Uvicorn** em execução.
* **Serviços de nuvem** que cuidam disso para você
* O serviço de nuvem provavelmente **cuidará da replicação para você**. Ele possivelmente deixaria você definir **um processo para executar**, ou uma **imagem de contêiner** para usar, em qualquer caso, provavelmente seria **um único processo Uvicorn**, e o serviço de nuvem seria responsável por replicá-lo.
/// tip | Dica
Não se preocupe se alguns desses itens sobre **contêineres**, Docker ou Kubernetes ainda não fizerem muito sentido.
Falarei mais sobre imagens de contêiner, Docker, Kubernetes, etc. em um capítulo futuro: [FastAPI em contêineres - Docker](docker.md){.internal-link target=_blank}.
///
## Etapas anteriores antes de começar { #previous-steps-before-starting }
Há muitos casos em que você deseja executar algumas etapas **antes de iniciar** sua aplicação.
Por exemplo, você pode querer executar **migrações de banco de dados**.
Mas na maioria dos casos, você precisará executar essas etapas apenas **uma vez**.
Portanto, você vai querer ter um **processo único** para executar essas **etapas anteriores** antes de iniciar o aplicativo.
E você terá que se certificar de que é um único processo executando essas etapas anteriores *mesmo* se depois, você iniciar **vários processos** (vários trabalhadores) para o próprio aplicativo. Se essas etapas fossem executadas por **vários processos**, eles **duplicariam** o trabalho executando-o em **paralelo**, e se as etapas fossem algo delicado como uma migração de banco de dados, elas poderiam causar conflitos entre si.
Claro, há alguns casos em que não há problema em executar as etapas anteriores várias vezes; nesse caso, é muito mais fácil de lidar.
/// tip | Dica
Além disso, tenha em mente que, dependendo da sua configuração, em alguns casos você **pode nem precisar de nenhuma etapa anterior** antes de iniciar sua aplicação.
Nesse caso, você não precisaria se preocupar com nada disso. 🤷
///
### Exemplos de estratégias de etapas anteriores { #examples-of-previous-steps-strategies }
Isso **dependerá muito** da maneira como você **implanta seu sistema** e provavelmente estará conectado à maneira como você inicia programas, lida com reinicializações, etc.
Aqui estão algumas ideias possíveis:
* Um "Init Container" no Kubernetes que roda antes do seu app container
* Um script bash que roda os passos anteriores e então inicia seu aplicativo
* Você ainda precisaria de uma maneira de iniciar/reiniciar *aquele* script bash, detectar erros, etc.
/// tip | Dica
Darei exemplos mais concretos de como fazer isso com contêineres em um capítulo futuro: [FastAPI em contêineres - Docker](docker.md){.internal-link target=_blank}.
///
## Utilização de recursos { #resource-utilization }
Seu(s) servidor(es) é(são) um **recurso** que você pode consumir ou **utilizar**, com seus programas, o tempo de computação nas CPUs e a memória RAM disponível.
Quanto dos recursos do sistema você quer consumir/utilizar? Pode ser fácil pensar "não muito", mas, na realidade, você provavelmente vai querer consumir **o máximo possível sem travar**.
Se você está pagando por 3 servidores, mas está usando apenas um pouco de RAM e CPU, você provavelmente está **desperdiçando dinheiro** 💸, e provavelmente **desperdiçando energia elétrica do servidor** 🌎, etc.
Nesse caso, seria melhor ter apenas 2 servidores e usar uma porcentagem maior de seus recursos (CPU, memória, disco, largura de banda de rede, etc).
Por outro lado, se você tem 2 servidores e está usando **100% da CPU e RAM deles**, em algum momento um processo pedirá mais memória, e o servidor terá que usar o disco como "memória" (o que pode ser milhares de vezes mais lento), ou até mesmo **travar**. Ou um processo pode precisar fazer alguma computação e teria que esperar até que a CPU esteja livre novamente.
Nesse caso, seria melhor obter **um servidor extra** e executar alguns processos nele para que todos tenham **RAM e tempo de CPU suficientes**.
Também há a chance de que, por algum motivo, você tenha um **pico** de uso da sua API. Talvez ela tenha se tornado viral, ou talvez alguns outros serviços ou bots comecem a usá-la. E você pode querer ter recursos extras para estar seguro nesses casos.
Você poderia colocar um **número arbitrário** para atingir, por exemplo, algo **entre 50% a 90%** da utilização de recursos. O ponto é que essas são provavelmente as principais coisas que você vai querer medir e usar para ajustar suas implantações.
Você pode usar ferramentas simples como `htop` para ver a CPU e a RAM usadas no seu servidor ou a quantidade usada por cada processo. Ou você pode usar ferramentas de monitoramento mais complexas, que podem ser distribuídas entre servidores, etc.
## Recapitular { #recap }
Você leu aqui alguns dos principais conceitos que provavelmente precisa ter em mente ao decidir como implantar seu aplicativo:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Etapas anteriores antes de iniciar
Entender essas ideias e como aplicá-las deve lhe dar a intuição necessária para tomar qualquer decisão ao configurar e ajustar suas implantações. 🤓
Nas próximas seções, darei exemplos mais concretos de possíveis estratégias que você pode seguir. 🚀
+620
View File
@@ -0,0 +1,620 @@
# FastAPI em contêineres - Docker { #fastapi-in-containers-docker }
Ao fazer o deploy de aplicações FastAPI uma abordagem comum é construir uma **imagem de contêiner Linux**. Isso normalmente é feito usando o <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Você pode a partir disso fazer o deploy dessa imagem de algumas maneiras.
Usando contêineres Linux você tem diversas vantagens incluindo **segurança**, **replicabilidade**, **simplicidade**, entre outras.
/// tip | Dica
Está com pressa e já sabe dessas coisas? Pode ir direto para o [`Dockerfile` abaixo 👇](#build-a-docker-image-for-fastapi).
///
<details>
<summary>Visualização do Dockerfile 👀</summary>
```Dockerfile
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# Se estiver executando atrás de um proxy como Nginx ou Traefik, adicione --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</details>
## O que é um Contêiner { #what-is-a-container }
Contêineres (principalmente contêineres Linux) são um jeito muito **leve** de empacotar aplicações contendo todas as dependências e arquivos necessários enquanto os mantém isolados de outros contêineres (outras aplicações ou componentes) no mesmo sistema.
Contêineres Linux rodam usando o mesmo kernel Linux do hospedeiro (máquina, máquina virtual, servidor na nuvem, etc). Isso simplesmente significa que eles são muito leves (comparados com máquinas virtuais emulando um sistema operacional completo).
Dessa forma, contêineres consomem **poucos recursos**, uma quantidade comparável com rodar os processos diretamente (uma máquina virtual consumiria muito mais).
Contêineres também possuem seus próprios processos em execução (comumente **um único processo**), sistema de arquivos e rede **isolados**, simplificando deploy, segurança, desenvolvimento, etc.
## O que é uma Imagem de Contêiner { #what-is-a-container-image }
Um **contêiner** roda a partir de uma **imagem de contêiner**.
Uma imagem de contêiner é uma versão **estática** de todos os arquivos, variáveis de ambiente e do comando/programa padrão que deve estar presente num contêiner. **Estática** aqui significa que a **imagem** de contêiner não está rodando, não está sendo executada, somente contém os arquivos e metadados empacotados.
Em contraste com a "**imagem de contêiner**" que contém os conteúdos estáticos armazenados, um "**contêiner**" normalmente se refere à instância rodando, a coisa que está sendo **executada**.
Quando o **contêiner** é iniciado e está rodando (iniciado a partir de uma **imagem de contêiner**), ele pode criar ou modificar arquivos, variáveis de ambiente, etc. Essas mudanças vão existir somente nesse contêiner, mas não persistirão na imagem subjacente do container (não serão salvas no disco).
Uma imagem de contêiner é comparável ao arquivo de **programa** e seus conteúdos, ex.: `python` e algum arquivo `main.py`.
E o **contêiner** em si (em contraste à **imagem de contêiner**) é a própria instância da imagem rodando, comparável a um **processo**. Na verdade, um contêiner está rodando somente quando há um **processo rodando** (e normalmente é somente um processo). O contêiner finaliza quando não há um processo rodando nele.
## Imagens de contêiner { #container-images }
Docker tem sido uma das principais ferramentas para criar e gerenciar **imagens de contêiner** e **contêineres**.
E existe um <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> público com **imagens de contêiner oficiais** pré-prontas para diversas ferramentas, ambientes, bancos de dados e aplicações.
Por exemplo, há uma <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Imagem Python</a> oficial.
E existe muitas outras imagens para diferentes coisas, como bancos de dados, por exemplo:
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, etc.
Usando imagens de contêiner pré-prontas é muito fácil **combinar** e usar diferentes ferramentas. Por exemplo, para testar um novo banco de dados. Em muitos casos, você pode usar as **imagens oficiais**, precisando somente de variáveis de ambiente para configurá-las.
Dessa forma, em muitos casos você pode aprender sobre contêineres e Docker e reusar essa experiência com diversos componentes e ferramentas.
Então, você rodaria **vários contêineres** com coisas diferentes, como um banco de dados, uma aplicação Python, um servidor web com uma aplicação frontend React, e conectá-los juntos via sua rede interna.
Todos os sistemas de gerenciamento de contêineres (como Docker ou Kubernetes) possuem essas funcionalidades de rede integradas a eles.
## Contêineres e Processos { #containers-and-processes }
Uma **imagem de contêiner** normalmente inclui em seus metadados o programa padrão ou comando que deve ser executado quando o **contêiner** é iniciado e os parâmetros a serem passados para esse programa. Muito similar ao que seria se estivesse na linha de comando.
Quando um **contêiner** é iniciado, ele irá rodar esse comando/programa (embora você possa sobrescrevê-lo e fazer com que ele rode um comando/programa diferente).
Um contêiner está rodando enquanto o **processo principal** (comando ou programa) estiver rodando.
Um contêiner normalmente tem um **único processo**, mas também é possível iniciar sub-processos a partir do processo principal, e dessa forma você terá **vários processos** no mesmo contêiner.
Mas não é possível ter um contêiner rodando sem **pelo menos um processo rodando**. Se o processo principal parar, o contêiner também para.
## Construir uma Imagem Docker para FastAPI { #build-a-docker-image-for-fastapi }
Okay, vamos construir algo agora! 🚀
Eu vou mostrar como construir uma **imagem Docker** para FastAPI **do zero**, baseada na imagem **oficial do Python**.
Isso é o que você quer fazer na **maioria dos casos**, por exemplo:
* Usando **Kubernetes** ou ferramentas similares
* Quando rodando em uma **Raspberry Pi**
* Usando um serviço em nuvem que irá rodar uma imagem de contêiner para você, etc.
### Requisitos de Pacotes { #package-requirements }
Você normalmente teria os **requisitos de pacotes** da sua aplicação em algum arquivo.
Isso pode depender principalmente da ferramenta que você usa para **instalar** esses requisitos.
A forma mais comum de fazer isso é ter um arquivo `requirements.txt` com os nomes dos pacotes e suas versões, um por linha.
Você, naturalmente, usaria as mesmas ideias que você leu em [Sobre versões do FastAPI](versions.md){.internal-link target=_blank} para definir os intervalos de versões.
Por exemplo, seu `requirements.txt` poderia parecer com:
```
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
E você normalmente instalaria essas dependências de pacote com `pip`, por exemplo:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
```
</div>
/// info | Informação
Há outros formatos e ferramentas para definir e instalar dependências de pacotes.
///
### Crie o código do **FastAPI** { #create-the-fastapi-code }
* Crie um diretório `app` e entre nele.
* Crie um arquivo vazio `__init__.py`.
* Crie um arquivo `main.py` com:
```Python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Agora, no mesmo diretório do projeto, crie um arquivo `Dockerfile` com:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.9
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)!
COPY ./app /code/app
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Inicie a partir da imagem base oficial do Python.
2. Defina o diretório de trabalho atual para `/code`.
Esse é o diretório onde colocaremos o arquivo `requirements.txt` e o diretório `app`.
3. Copie o arquivo com os requisitos para o diretório `/code`.
Copie **somente** o arquivo com os requisitos primeiro, não o resto do código.
Como esse arquivo **não muda com frequência**, o Docker irá detectá-lo e usar o **cache** para esse passo, habilitando o cache para o próximo passo também.
4. Instale as dependências de pacote vindas do arquivo de requisitos.
A opção `--no-cache-dir` diz ao `pip` para não salvar os pacotes baixados localmente, pois isso só aconteceria se `pip` fosse executado novamente para instalar os mesmos pacotes, mas esse não é o caso quando trabalhamos com contêineres.
/// note | Nota
`--no-cache-dir` é apenas relacionado ao `pip`, não tem nada a ver com Docker ou contêineres.
///
A opção `--upgrade` diz ao `pip` para atualizar os pacotes se eles já estiverem instalados.
Por causa do passo anterior de copiar o arquivo, ele pode ser detectado pelo **cache do Docker**, esse passo também **usará o cache do Docker** quando disponível.
Usando o cache nesse passo irá **salvar** muito **tempo** quando você for construir a imagem repetidas vezes durante o desenvolvimento, ao invés de **baixar e instalar** todas as dependências **toda vez**.
5. Copie o diretório `./app` dentro do diretório `/code`.
Como isso tem todo o código contendo o que **muda com mais frequência**, o **cache do Docker** não será usado para esse passo ou para **qualquer passo seguinte** facilmente.
Então, é importante colocar isso **perto do final** do `Dockerfile`, para otimizar o tempo de construção da imagem do contêiner.
6. Defina o **comando** para usar `fastapi run`, que utiliza o Uvicorn por baixo dos panos.
`CMD` recebe uma lista de strings, cada uma dessas strings é o que você digitaria na linha de comando separado por espaços.
Esse comando será executado a partir do **diretório de trabalho atual**, o mesmo diretório `/code` que você definiu acima com `WORKDIR /code`.
/// tip | Dica
Revise o que cada linha faz clicando em cada bolha com o número no código. 👆
///
/// warning | Atenção
Certifique-se de **sempre** usar a **forma exec** da instrução `CMD`, como explicado abaixo.
///
#### Use `CMD` - Forma Exec { #use-cmd-exec-form }
A instrução <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> no Docker pode ser escrita de duas formas:
✅ Forma **Exec**:
```Dockerfile
# ✅ Faça assim
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Forma **Shell**:
```Dockerfile
# ⛔️ Não faça assim
CMD fastapi run app/main.py --port 80
```
Garanta que você sempre use a forma **exec** para assegurar que o FastAPI consiga encerrar graciosamente e que os [eventos de lifespan](../advanced/events.md){.internal-link target=_blank} sejam disparados.
Você pode ler mais na <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">documentação do Docker sobre as formas shell e exec</a>.
Isso pode ser bem perceptível ao usar `docker compose`. Veja esta seção de FAQ do Docker Compose para mais detalhes técnicos: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Por que meus serviços demoram 10 segundos para recriar ou parar?</a>.
#### Estrutura de diretórios { #directory-structure }
Agora você deve haver uma estrutura de diretório como:
```
.
├── app
│   ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Por trás de um Proxy de Terminação TLS { #behind-a-tls-termination-proxy }
Se você está executando seu contêiner atrás de um Proxy de Terminação TLS (load balancer) como Nginx ou Traefik, adicione a opção `--proxy-headers`, isso fará com que o Uvicorn (pela CLI do FastAPI) confie nos cabeçalhos enviados por esse proxy, informando que o aplicativo está sendo executado atrás do HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Cache Docker { #docker-cache }
Existe um truque importante nesse `Dockerfile`, primeiro copiamos o **arquivo com as dependências sozinho**, não o resto do código. Deixe-me te contar o porquê disso.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker e outras ferramentas **constróem** essas imagens de contêiner **incrementalmente**, adicionando **uma camada em cima da outra**, começando do topo do `Dockerfile` e adicionando qualquer arquivo criado por cada uma das instruções do `Dockerfile`.
Docker e ferramentas similares também usam um **cache interno** ao construir a imagem, se um arquivo não mudou desde a última vez que a imagem do contêiner foi construída, então ele irá **reutilizar a mesma camada** criada na última vez, ao invés de copiar o arquivo novamente e criar uma nova camada do zero.
Somente evitar a cópia de arquivos não melhora muito as coisas, mas porque ele usou o cache para esse passo, ele pode **usar o cache para o próximo passo**. Por exemplo, ele pode usar o cache para a instrução que instala as dependências com:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
O arquivo com os requisitos de pacote **não muda com frequência**. Então, ao copiar apenas esse arquivo, o Docker será capaz de **usar o cache** para esse passo.
E então, o Docker será capaz de **usar o cache para o próximo passo** que baixa e instala essas dependências. E é aqui que **salvamos muito tempo**. ✨ ...e evitamos tédio esperando. 😪😆
Baixar e instalar as dependências do pacote **pode levar minutos**, mas usando o **cache** leva **segundos** no máximo.
E como você estaria construindo a imagem do contêiner novamente e novamente durante o desenvolvimento para verificar se suas alterações de código estão funcionando, há muito tempo acumulado que isso economizaria.
A partir daí, perto do final do `Dockerfile`, copiamos todo o código. Como isso é o que **muda com mais frequência**, colocamos perto do final, porque quase sempre, qualquer coisa depois desse passo não será capaz de usar o cache.
```Dockerfile
COPY ./app /code/app
```
### Construa a Imagem Docker { #build-the-docker-image }
Agora que todos os arquivos estão no lugar, vamos construir a imagem do contêiner.
* Vá para o diretório do projeto (onde está o seu `Dockerfile`, contendo o diretório `app`).
* Construa sua imagem FastAPI:
<div class="termy">
```console
$ docker build -t myimage .
---> 100%
```
</div>
/// tip | Dica
Note o `.` no final, é equivalente a `./`, ele diz ao Docker o diretório a ser usado para construir a imagem do contêiner.
Nesse caso, é o mesmo diretório atual (`.`).
///
### Inicie o Contêiner Docker { #start-the-docker-container }
* Execute um contêiner baseado na sua imagem:
<div class="termy">
```console
$ docker run -d --name mycontainer -p 80:80 myimage
```
</div>
## Verifique { #check-it }
Você deve ser capaz de verificar isso no URL do seu contêiner Docker, por exemplo: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou equivalente, usando seu host Docker).
Você verá algo como:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Documentação interativa da API { #interactive-api-docs }
Agora você pode ir para <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou equivalente, usando seu host Docker).
Você verá a documentação interativa automática da API (fornecida pelo <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
## Documentação alternativa da API { #alternative-api-docs }
E você também pode ir para <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> ou <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (ou equivalente, usando seu host Docker).
Você verá a documentação alternativa automática (fornecida pela <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Construa uma Imagem Docker com um FastAPI de Arquivo Único { #build-a-docker-image-with-a-single-file-fastapi }
Se seu FastAPI for um único arquivo, por exemplo, `main.py` sem um diretório `./app`, sua estrutura de arquivos poderia ser assim:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Então você só teria que alterar os caminhos correspondentes para copiar o arquivo dentro do `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copie o arquivo `main.py` para o diretório `/code` diretamente (sem nenhum diretório `./app`).
2. Use `fastapi run` para servir sua aplicação no arquivo único `main.py`.
Quando você passa o arquivo para `fastapi run` ele detecta automaticamente que é um arquivo único e não parte de um pacote e sabe como importá-lo e servir sua aplicação FastAPI. 😎
## Conceitos de Implantação { #deployment-concepts }
Vamos falar novamente sobre alguns dos mesmos [Conceitos de Implantação](concepts.md){.internal-link target=_blank} em termos de contêineres.
Contêineres são principalmente uma ferramenta para simplificar o processo de **construção e implantação** de um aplicativo, mas eles não impõem uma abordagem particular para lidar com esses **conceitos de implantação** e existem várias estratégias possíveis.
A **boa notícia** é que com cada estratégia diferente há uma maneira de cobrir todos os conceitos de implantação. 🎉
Vamos revisar esses **conceitos de implantação** em termos de contêineres:
* HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (número de processos rodando)
* Memória
* Passos anteriores antes de começar
## HTTPS { #https }
Se nos concentrarmos apenas na **imagem do contêiner** para um aplicativo FastAPI (e posteriormente no **contêiner** em execução), o HTTPS normalmente seria tratado **externamente** por outra ferramenta.
Isso poderia ser outro contêiner, por exemplo, com <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, lidando com **HTTPS** e aquisição **automática** de **certificados**.
/// tip | Dica
Traefik tem integrações com Docker, Kubernetes e outros, portanto, é muito fácil configurar o HTTPS para seus contêineres com ele.
///
Alternativamente, o HTTPS poderia ser tratado por um provedor de nuvem como um de seus serviços (enquanto ainda executasse o aplicativo em um contêiner).
## Executando na inicialização e reinicializações { #running-on-startup-and-restarts }
Normalmente, outra ferramenta é responsável por **iniciar e executar** seu contêiner.
Ela poderia ser o **Docker** diretamente, **Docker Compose**, **Kubernetes**, um **serviço de nuvem**, etc.
Na maioria (ou em todos) os casos, há uma opção simples para habilitar a execução do contêiner na inicialização e habilitar reinicializações em falhas. Por exemplo, no Docker, é a opção de linha de comando `--restart`.
Sem usar contêineres, fazer aplicativos executarem na inicialização e com reinicializações pode ser trabalhoso e difícil. Mas quando **trabalhando com contêineres** em muitos casos essa funcionalidade é incluída por padrão. ✨
## Replicação - Número de Processos { #replication-number-of-processes }
Se você tiver um <abbr title="Um grupo de máquinas que são configuradas para estarem conectadas e trabalharem juntas de alguma forma.">cluster</abbr> de máquinas com **Kubernetes**, Docker Swarm Mode, Nomad ou outro sistema complexo semelhante para gerenciar contêineres distribuídos em várias máquinas, então provavelmente desejará **lidar com a replicação** no **nível do cluster** em vez de usar um **gerenciador de processos** (como Uvicorn com workers) em cada contêiner.
Um desses sistemas de gerenciamento de contêineres distribuídos como o Kubernetes normalmente tem alguma maneira integrada de lidar com a **replicação de contêineres** enquanto ainda oferece **balanceamento de carga** para as solicitações recebidas. Tudo no **nível do cluster**.
Nesses casos, você provavelmente desejará criar uma **imagem Docker do zero** como [explicado acima](#dockerfile), instalando suas dependências e executando **um único processo Uvicorn** em vez de usar múltiplos workers do Uvicorn.
### Balanceador de Carga { #load-balancer }
Quando usando contêineres, normalmente você terá algum componente **escutando na porta principal**. Poderia ser outro contêiner que também é um **Proxy de Terminação TLS** para lidar com **HTTPS** ou alguma ferramenta semelhante.
Como esse componente assumiria a **carga** de solicitações e distribuiria isso entre os workers de uma maneira (esperançosamente) **balanceada**, ele também é comumente chamado de **Balanceador de Carga**.
/// tip | Dica
O mesmo componente **Proxy de Terminação TLS** usado para HTTPS provavelmente também seria um **Balanceador de Carga**.
///
E quando trabalhar com contêineres, o mesmo sistema que você usa para iniciar e gerenciá-los já terá ferramentas internas para transmitir a **comunicação de rede** (por exemplo, solicitações HTTP) do **balanceador de carga** (que também pode ser um **Proxy de Terminação TLS**) para o(s) contêiner(es) com seu aplicativo.
### Um Balanceador de Carga - Múltiplos Contêineres de Workers { #one-load-balancer-multiple-worker-containers }
Quando trabalhando com **Kubernetes** ou sistemas similares de gerenciamento de contêiner distribuído, usar seus mecanismos de rede internos permite que o único **balanceador de carga** que está escutando na **porta principal** transmita a comunicação (solicitações) para possivelmente **múltiplos contêineres** executando seu aplicativo.
Cada um desses contêineres executando seu aplicativo normalmente teria **apenas um processo** (ex.: um processo Uvicorn executando seu aplicativo FastAPI). Todos seriam **contêineres idênticos**, executando a mesma coisa, mas cada um com seu próprio processo, memória, etc. Dessa forma, você aproveitaria a **paralelização** em **núcleos diferentes** da CPU, ou até mesmo em **máquinas diferentes**.
E o sistema de contêiner com o **balanceador de carga** iria **distribuir as solicitações** para cada um dos contêineres com seu aplicativo **em turnos**. Portanto, cada solicitação poderia ser tratada por um dos múltiplos **contêineres replicados** executando seu aplicativo.
E normalmente esse **balanceador de carga** seria capaz de lidar com solicitações que vão para *outros* aplicativos em seu cluster (por exemplo, para um domínio diferente, ou sob um prefixo de URL diferente), e transmitiria essa comunicação para os contêineres certos para *esse outro* aplicativo em execução em seu cluster.
### Um Processo por Contêiner { #one-process-per-container }
Nesse tipo de cenário, provavelmente você desejará ter **um único processo (Uvicorn) por contêiner**, pois já estaria lidando com a replicação no nível do cluster.
Então, nesse caso, você **não** desejará ter múltiplos workers no contêiner, por exemplo com a opção de linha de comando `--workers`. Você desejará ter apenas um **único processo Uvicorn** por contêiner (mas provavelmente vários contêineres).
Ter outro gerenciador de processos dentro do contêiner (como seria com múltiplos workers) só adicionaria **complexidade desnecessária** que você provavelmente já está cuidando com seu sistema de cluster.
### Contêineres com Múltiplos Processos e Casos Especiais { #containers-with-multiple-processes-and-special-cases }
Claro, existem **casos especiais** em que você pode querer ter **um contêiner** com vários **processos workers do Uvicorn** dentro.
Nesses casos, você pode usar a opção de linha de comando `--workers` para definir o número de workers que deseja executar:
```{ .dockerfile .annotate }
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Aqui usamos a opção de linha de comando `--workers` para definir o número de workers como 4.
Aqui estão alguns exemplos de quando isso pode fazer sentido:
#### Um Aplicativo Simples { #a-simple-app }
Você pode querer um gerenciador de processos no contêiner se seu aplicativo for **simples o suficiente** para rodar em um **único servidor**, não em um cluster.
#### Docker Compose { #docker-compose }
Você pode estar implantando em um **único servidor** (não em um cluster) com o **Docker Compose**, então você não teria uma maneira fácil de gerenciar a replicação de contêineres (com o Docker Compose) enquanto preserva a rede compartilhada e o **balanceamento de carga**.
Então você pode querer ter **um único contêiner** com um **gerenciador de processos** iniciando **vários processos workers** dentro.
---
O ponto principal é que **nenhum** desses são **regras escritas em pedra** que você deve seguir cegamente. Você pode usar essas ideias para **avaliar seu próprio caso de uso** e decidir qual é a melhor abordagem para seu sistema, verificando como gerenciar os conceitos de:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Passos anteriores antes de iniciar
## Memória { #memory }
Se você executar **um único processo por contêiner**, terá uma quantidade mais ou menos bem definida, estável e limitada de memória consumida por cada um desses contêineres (mais de um se eles forem replicados).
E então você pode definir esses mesmos limites e requisitos de memória em suas configurações para seu sistema de gerenciamento de contêineres (por exemplo, no **Kubernetes**). Dessa forma, ele poderá **replicar os contêineres** nas **máquinas disponíveis** levando em consideração a quantidade de memória necessária por eles e a quantidade disponível nas máquinas no cluster.
Se sua aplicação for **simples**, isso provavelmente **não será um problema**, e você pode não precisar especificar limites de memória rígidos. Mas se você estiver **usando muita memória** (por exemplo, com **modelos de aprendizado de máquina**), deve verificar quanta memória está consumindo e ajustar o **número de contêineres** que executa em **cada máquina** (e talvez adicionar mais máquinas ao seu cluster).
Se você executar **múltiplos processos por contêiner**, deve garantir que o número de processos iniciados não **consuma mais memória** do que o disponível.
## Passos anteriores antes de iniciar e contêineres { #previous-steps-before-starting-and-containers }
Se você estiver usando contêineres (por exemplo, Docker, Kubernetes), existem duas abordagens principais que você pode usar.
### Contêineres Múltiplos { #multiple-containers }
Se você tiver **múltiplos contêineres**, provavelmente cada um executando um **único processo** (por exemplo, em um cluster do **Kubernetes**), então provavelmente você gostaria de ter um **contêiner separado** fazendo o trabalho dos **passos anteriores** em um único contêiner, executando um único processo, **antes** de executar os contêineres workers replicados.
/// info | Informação
Se você estiver usando o Kubernetes, provavelmente será um <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>.
///
Se no seu caso de uso não houver problema em executar esses passos anteriores **em paralelo várias vezes** (por exemplo, se você não estiver executando migrações de banco de dados, mas apenas verificando se o banco de dados está pronto), então você também pode colocá-los em cada contêiner logo antes de iniciar o processo principal.
### Contêiner Único { #single-container }
Se você tiver uma configuração simples, com um **único contêiner** que então inicia vários **processos workers** (ou também apenas um processo), então poderia executar esses passos anteriores no mesmo contêiner, logo antes de iniciar o processo com o aplicativo.
### Imagem Docker base { #base-docker-image }
Antes havia uma imagem oficial do FastAPI para Docker: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Mas agora ela está descontinuada. ⛔️
Você provavelmente **não** deve usar essa imagem base do Docker (ou qualquer outra semelhante).
Se você está usando **Kubernetes** (ou outros) e já está definindo a **replicação** no nível do cluster, com vários **contêineres**. Nesses casos, é melhor **construir uma imagem do zero** como descrito acima: [Construir uma Imagem Docker para FastAPI](#build-a-docker-image-for-fastapi).
E se você precisar ter múltiplos workers, você pode simplesmente usar a opção de linha de comando `--workers`.
/// note | Detalhes Técnicos
A imagem Docker foi criada quando o Uvicorn não suportava gerenciar e reiniciar workers mortos, então era necessário usar o Gunicorn com o Uvicorn, o que adicionava bastante complexidade, apenas para que o Gunicorn gerenciasse e reiniciasse os processos workers do Uvicorn.
Mas agora que o Uvicorn (e o comando `fastapi`) suportam o uso de `--workers`, não há razão para usar uma imagem base do Docker em vez de construir a sua própria (é praticamente a mesma quantidade de código 😅).
///
## Deploy da Imagem do Contêiner { #deploy-the-container-image }
Depois de ter uma imagem de contêiner (Docker), existem várias maneiras de implantá-la.
Por exemplo:
* Com **Docker Compose** em um único servidor
* Com um cluster **Kubernetes**
* Com um cluster Docker Swarm Mode
* Com outra ferramenta como o Nomad
* Com um serviço de nuvem que pega sua imagem de contêiner e a implanta
## Imagem Docker com `uv` { #docker-image-with-uv }
Se você está usando o <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> para instalar e gerenciar seu projeto, você pode seguir o <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">guia de Docker do uv</a>.
## Recapitulando { #recap }
Usando sistemas de contêiner (por exemplo, com **Docker** e **Kubernetes**), torna-se bastante simples lidar com todos os **conceitos de implantação**:
* HTTPS
* Executando na inicialização
* Reinícios
* Replicação (o número de processos rodando)
* Memória
* Passos anteriores antes de iniciar
Na maioria dos casos, você provavelmente não desejará usar nenhuma imagem base e, em vez disso, **construir uma imagem de contêiner do zero** baseada na imagem oficial do Docker Python.
Tendo cuidado com a **ordem** das instruções no `Dockerfile` e o **cache do Docker**, você pode **minimizar os tempos de construção**, para maximizar sua produtividade (e evitar o tédio). 😎
+231
View File
@@ -0,0 +1,231 @@
# Sobre HTTPS { #about-https }
É fácil assumir que HTTPS é algo que é apenas "habilitado" ou não.
Mas é bem mais complexo do que isso.
/// tip | Dica
Se você está com pressa ou não se importa, continue com as seções seguintes para instruções passo a passo para configurar tudo com diferentes técnicas.
///
Para aprender o básico de HTTPS do ponto de vista do consumidor, verifique <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
Agora, a partir de uma perspectiva do desenvolvedor, aqui estão algumas coisas para ter em mente ao pensar em HTTPS:
* Para HTTPS, o servidor precisa ter "certificados" gerados por um terceiro.
* Esses certificados são na verdade adquiridos de um terceiro, eles não são simplesmente "gerados".
* Certificados têm um tempo de vida.
* Eles expiram.
* E então eles precisam ser renovados, adquirindo-os novamente de um terceiro.
* A criptografia da conexão acontece no nível TCP.
* Essa é uma camada abaixo do HTTP.
* Portanto, o manuseio do certificado e da criptografia é feito antes do HTTP.
* O TCP não sabe sobre "domínios". Apenas sobre endereços IP.
* As informações sobre o domínio específico solicitado vão nos dados HTTP.
* Os certificados HTTPS “certificam” um determinado domínio, mas o protocolo e a encriptação acontecem ao nível do TCP, antes de sabermos de que domínio se trata.
* Por padrão, isso significa que você só pode ter um certificado HTTPS por endereço IP.
* Não importa o tamanho do seu servidor ou quão pequeno cada aplicativo que você tem nele possa ser.
* No entanto, existe uma solução para isso.
* Há uma extensão para o protocolo TLS (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamada <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
* Esta extensão SNI permite que um único servidor (com um único endereço IP) tenha vários certificados HTTPS e atenda a vários domínios / aplicativos HTTPS.
* Para que isso funcione, um único componente (programa) em execução no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor.
* Depois de obter uma conexão segura, o protocolo de comunicação ainda é HTTP.
* Os conteúdos são criptografados, embora sejam enviados com o protocolo HTTP.
É uma prática comum ter um programa/servidor HTTP em execução no servidor (máquina, host, etc.) e gerenciar todas as partes HTTPS: recebendo as requisições HTTPS encriptadas, enviando as solicitações HTTP descriptografadas para o aplicativo HTTP real em execução no mesmo servidor (a aplicação FastAPI, neste caso), pegar a resposta HTTP do aplicativo, criptografá-la usando o certificado HTTPS apropriado e enviá-la de volta ao cliente usando HTTPS. Este servidor é frequentemente chamado de <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Proxy de Terminação TLS</a>.
Algumas das opções que você pode usar como Proxy de Terminação TLS são:
* Traefik (que também pode gerenciar a renovação de certificados)
* Caddy (que também pode gerenciar a renovação de certificados)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Antes de Let's Encrypt, esses certificados HTTPS eram vendidos por terceiros confiáveis.
O processo de aquisição de um desses certificados costumava ser complicado, exigia bastante papelada e os certificados eram bastante caros.
Mas então o <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> foi criado.
Ele é um projeto da Linux Foundation que fornece certificados HTTPS gratuitamente. De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a segurança é, na verdade, melhor por causa do seu lifespan reduzido.
Os domínios são verificados com segurança e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados.
A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha HTTPS seguro, de graça e para sempre.
## HTTPS para Desenvolvedores { #https-for-developers }
Aqui está um exemplo de como uma API HTTPS poderia ser estruturada, passo a passo, com foco principal nas ideias relevantes para desenvolvedores.
### Nome do domínio { #domain-name }
A etapa inicial provavelmente seria adquirir algum nome de domínio. Então, você iria configurá-lo em um servidor DNS (possivelmente no mesmo provedor em nuvem).
Você provavelmente usaria um servidor em nuvem (máquina virtual) ou algo parecido, e ele teria um <abbr title="Que não muda">fixo</abbr> Endereço IP público.
No(s) servidor(es) DNS, você configuraria um registro (um `A record`) para apontar seu domínio para o endereço IP público do seu servidor.
Você provavelmente fará isso apenas uma vez, na primeira vez em que tudo estiver sendo configurado.
/// tip | Dica
Essa parte do Nome do Domínio se dá muito antes do HTTPS, mas como tudo depende do domínio e endereço IP público, vale a pena mencioná-la aqui.
///
### DNS { #dns }
Agora vamos focar em todas as partes que realmente fazem parte do HTTPS.
Primeiro, o navegador iria verificar com os servidores DNS qual o IP do domínio, nesse caso, `someapp.example.com`.
Os servidores DNS iriam informar o navegador para utilizar algum endereço IP específico. Esse seria o endereço IP público em uso no seu servidor, que você configurou nos servidores DNS.
<img src="/img/deployment/https/https01.drawio.svg">
### Início do Handshake TLS { #tls-handshake-start }
O navegador então irá comunicar-se com esse endereço IP na porta 443 (a porta HTTPS).
A primeira parte dessa comunicação é apenas para estabelecer a conexão entre o cliente e o servidor e para decidir as chaves criptográficas a serem utilizadas, etc.
<img src="/img/deployment/https/https02.drawio.svg">
Esse interação entre o cliente e o servidor para estabelecer uma conexão TLS é chamada de Handshake TLS.
### TLS com a Extensão SNI { #tls-with-sni-extension }
Apenas um processo no servidor pode se conectar a uma porta em um endereço IP. Poderiam existir outros processos conectados em outras portas desse mesmo endereço IP, mas apenas um para cada combinação de endereço IP e porta.
TLS (HTTPS) usa a porta `443` por padrão. Então essa é a porta que precisamos.
Como apenas um único processo pode se comunicar com essa porta, o processo que faria isso seria o Proxy de Terminação TLS.
O Proxy de Terminação TLS teria acesso a um ou mais certificados TLS (certificados HTTPS).
Utilizando a extensão SNI discutida acima, o Proxy de Terminação TLS iria checar qual dos certificados TLS (HTTPS) disponíveis deve ser usado para essa conexão, utilizando o que corresponda ao domínio esperado pelo cliente.
Nesse caso, ele usaria o certificado para `someapp.example.com`.
<img src="/img/deployment/https/https03.drawio.svg">
O cliente já confia na entidade que gerou o certificado TLS (nesse caso, o Let's Encrypt, mas veremos sobre isso mais tarde), então ele pode verificar que o certificado é válido.
Então, utilizando o certificado, o cliente e o Proxy de Terminação TLS decidem como encriptar o resto da comunicação TCP. Isso completa a parte do Handshake TLS.
Após isso, o cliente e o servidor possuem uma conexão TCP encriptada, que é provida pelo TLS. E então eles podem usar essa conexão para começar a comunicação HTTP propriamente dita.
E isso resume o que é HTTPS, apenas HTTP simples dentro de uma conexão TLS segura em vez de uma conexão TCP pura (não encriptada).
/// tip | Dica
Percebe que a encriptação da comunicação acontece no nível do TCP, não no nível do HTTP.
///
### Solicitação HTTPS { #https-request }
Agora que o cliente e servidor (especialmente o navegador e o Proxy de Terminação TLS) possuem uma conexão TCP encriptada, eles podem iniciar a comunicação HTTP.
Então, o cliente envia uma solicitação HTTPS. Que é apenas uma solicitação HTTP sobre uma conexão TLS encriptada.
<img src="/img/deployment/https/https04.drawio.svg">
### Desencriptando a Solicitação { #decrypt-the-request }
O Proxy de Terminação TLS então usaria a encriptação combinada para desencriptar a solicitação, e transmitiria a solicitação básica (desencriptada) para o processo executando a aplicação (por exemplo, um processo com Uvicorn executando a aplicação FastAPI).
<img src="/img/deployment/https/https05.drawio.svg">
### Resposta HTTP { #http-response }
A aplicação processaria a solicitação e retornaria uma resposta HTTP básica (não encriptada) para o Proxy de Terminação TLS.
<img src="/img/deployment/https/https06.drawio.svg">
### Resposta HTTPS { #https-response }
O Proxy de Terminação TLS iria encriptar a resposta utilizando a criptografia combinada anteriormente (que foi definida com o certificado para `someapp.example.com`), e devolveria para o navegador.
No próximo passo, o navegador verifica que a resposta é válida e encriptada com a chave criptográfica correta, etc. E depois desencripta a resposta e a processa.
<img src="/img/deployment/https/https07.drawio.svg">
O cliente (navegador) saberá que a resposta vem do servidor correto por que ela usa a criptografia que foi combinada entre eles usando o certificado HTTPS anterior.
### Múltiplas Aplicações { #multiple-applications }
Podem existir múltiplas aplicações em execução no mesmo servidor (ou servidores), por exemplo: outras APIs ou um banco de dados.
Apenas um processo pode estar vinculado a um IP e porta (o Proxy de Terminação TLS, por exemplo), mas outras aplicações/processos também podem estar em execução no(s) servidor(es), desde que não tentem usar a mesma combinação de IP público e porta.
<img src="/img/deployment/https/https08.drawio.svg">
Dessa forma, o Proxy de Terminação TLS pode gerenciar o HTTPS e os certificados de múltiplos domínios, para múltiplas aplicações, e então transmitir as requisições para a aplicação correta em cada caso.
### Renovação de Certificados { #certificate-renewal }
Em algum momento futuro, cada certificado irá expirar (aproximadamente 3 meses após a aquisição).
E então, haverá outro programa (em alguns casos pode ser o próprio Proxy de Terminação TLS) que irá interagir com o Let's Encrypt e renovar o(s) certificado(s).
<img src="/img/deployment/https/https.drawio.svg">
Os certificados TLS são associados com um nome de domínio, e não a um endereço IP.
Então para renovar os certificados, o programa de renovação precisa provar para a autoridade (Let's Encrypt) que ele realmente "possui" e controla esse domínio.
Para fazer isso, e acomodar as necessidades de diferentes aplicações, existem diferentes opções para esse programa. Algumas escolhas populares são:
* Modificar alguns registros DNS
* Para isso, o programa de renovação precisa ter suporte às APIs do provedor DNS, então, dependendo do provedor DNS que você utilize, isso pode ou não ser uma opção viável.
* Executar como um servidor (ao menos durante o processo de aquisição do certificado) no endereço IP público associado com o domínio.
* Como dito anteriormente, apenas um processo pode estar ligado a uma porta e IP específicos.
* Essa é uma dos motivos que fazem utilizar o mesmo Proxy de Terminação TLS para gerenciar a renovação de certificados ser tão útil.
* Caso contrário, você pode ter que parar a execução do Proxy de Terminação TLS momentaneamente, inicializar o programa de renovação para renovar os certificados, e então reiniciar o Proxy de Terminação TLS. Isso não é o ideal, já que sua(s) aplicação(ões) não vão estar disponíveis enquanto o Proxy de Terminação TLS estiver desligado.
Todo esse processo de renovação, enquanto o aplicativo ainda funciona, é uma das principais razões para preferir um sistema separado para gerenciar HTTPS com um Proxy de Terminação TLS em vez de usar os certificados TLS no servidor da aplicação diretamente (e.g. com o Uvicorn).
## Cabeçalhos encaminhados por Proxy { #proxy-forwarded-headers }
Ao usar um proxy para lidar com HTTPS, seu servidor de aplicação (por exemplo, Uvicorn via FastAPI CLI) não sabe nada sobre o processo de HTTPS; ele se comunica com HTTP simples com o Proxy de Terminação TLS.
Esse proxy normalmente define alguns cabeçalhos HTTP dinamicamente antes de transmitir a requisição para o servidor de aplicação, para informar ao servidor de aplicação que a requisição está sendo encaminhada pelo proxy.
/// note | Detalhes Técnicos
Os cabeçalhos do proxy são:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
No entanto, como o servidor de aplicação não sabe que está atrás de um proxy confiável, por padrão ele não confiaria nesses cabeçalhos.
Mas você pode configurar o servidor de aplicação para confiar nos cabeçalhos encaminhados enviados pelo proxy. Se você estiver usando o FastAPI CLI, pode usar a opção de CLI `--forwarded-allow-ips` para dizer de quais IPs ele deve confiar nesses cabeçalhos encaminhados.
Por exemplo, se o servidor de aplicação só estiver recebendo comunicação do proxy confiável, você pode defini-lo como `--forwarded-allow-ips="*"` para fazê-lo confiar em todos os IPs de entrada, já que ele só receberá requisições de seja lá qual for o IP usado pelo proxy.
Dessa forma, a aplicação seria capaz de saber qual é sua própria URL pública, se está usando HTTPS, o domínio, etc.
Isso seria útil, por exemplo, para lidar corretamente com redirecionamentos.
/// tip | Dica
Você pode saber mais sobre isso na documentação em [Atrás de um Proxy - Habilitar cabeçalhos encaminhados pelo proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
///
## Recapitulando { #recap }
Possuir HTTPS habilitado na sua aplicação é bastante importante, e até crítico na maioria dos casos. A maior parte do esforço que você tem que colocar sobre o HTTPS como desenvolvedor está em entender esses conceitos e como eles funcionam.
Mas uma vez que você saiba o básico de HTTPS para desenvolvedores, você pode combinar e configurar diferentes ferramentas facilmente para gerenciar tudo de uma forma simples.
Em alguns dos próximos capítulos, eu mostrarei para você vários exemplos concretos de como configurar o HTTPS para aplicações FastAPI. 🔒
+21
View File
@@ -0,0 +1,21 @@
# Implantação { #deployment }
Implantar uma aplicação **FastAPI** é relativamente fácil.
## O que significa Implantação { #what-does-deployment-mean }
Implantar uma aplicação significa executar as etapas necessárias para torná-la disponível para os usuários.
Para uma **web API**, isso normalmente envolve colocá-la em uma **máquina remota**, com um **programa de servidor** que ofereça bom desempenho, estabilidade, etc., de modo que seus **usuários** possam **acessar** a aplicação com eficiência e sem interrupções ou problemas.
Isso contrasta com as fases de **desenvolvimento**, em que você está constantemente alterando o código, quebrando e consertando, parando e reiniciando o servidor de desenvolvimento, etc.
## Estratégias de Implantação { #deployment-strategies }
Há várias maneiras de fazer isso, dependendo do seu caso de uso específico e das ferramentas que você utiliza.
Você pode **implantar um servidor** por conta própria usando uma combinação de ferramentas, pode usar um **serviço em nuvem** que faça parte do trabalho por você, entre outras opções.
Vou mostrar alguns dos principais conceitos que você provavelmente deve ter em mente ao implantar uma aplicação **FastAPI** (embora a maior parte se aplique a qualquer outro tipo de aplicação web).
Você verá mais detalhes para ter em mente e algumas das técnicas para fazer isso nas próximas seções. ✨
+157
View File
@@ -0,0 +1,157 @@
# Execute um Servidor Manualmente { #run-a-server-manually }
## Utilize o comando `fastapi run` { #use-the-fastapi-run-command }
Em resumo, utilize o comando `fastapi run` para inicializar sua aplicação FastAPI:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
```
</div>
Isto deve funcionar para a maioria dos casos. 😎
Você pode utilizar esse comando, por exemplo, para iniciar sua aplicação **FastAPI** em um contêiner, em um servidor, etc.
## Servidores ASGI { #asgi-servers }
Vamos nos aprofundar um pouco mais em detalhes.
FastAPI utiliza um padrão para construir frameworks e servidores web em Python chamado <abbr title="Asynchronous Server Gateway Interface Interface de Gateway de Servidor Assíncrono">ASGI</abbr>. FastAPI é um framework web ASGI.
A principal coisa que você precisa para executar uma aplicação **FastAPI** (ou qualquer outra aplicação ASGI) em uma máquina de servidor remoto é um programa de servidor ASGI como o **Uvicorn**, que é o que vem por padrão no comando `fastapi`.
Existem diversas alternativas, incluindo:
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>: um servidor ASGI de alta performance.
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: um servidor ASGI compatível com HTTP/2, Trio e outros recursos.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: servidor ASGI construído para Django Channels.
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: um servidor HTTP Rust para aplicações Python.
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: NGINX Unit é um runtime de aplicação web leve e versátil.
## Máquina Servidora e Programa Servidor { #server-machine-and-server-program }
Existe um pequeno detalhe sobre estes nomes para se manter em mente. 💡
A palavra "**servidor**" é comumente usada para se referir tanto ao computador remoto/nuvem (a máquina física ou virtual) quanto ao programa que está sendo executado nessa máquina (por exemplo, Uvicorn).
Apenas tenha em mente que quando você ler "servidor" em geral, isso pode se referir a uma dessas duas coisas.
Quando se refere à máquina remota, é comum chamá-la de **servidor**, mas também de **máquina**, **VM** (máquina virtual), **nó**. Todos esses termos se referem a algum tipo de máquina remota, normalmente executando Linux, onde você executa programas.
## Instale o Programa Servidor { #install-the-server-program }
Quando você instala o FastAPI, ele vem com um servidor de produção, o Uvicorn, e você pode iniciá-lo com o comando `fastapi run`.
Mas você também pode instalar um servidor ASGI manualmente.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e, em seguida, você pode instalar a aplicação do servidor.
Por exemplo, para instalar o Uvicorn:
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
Um processo semelhante se aplicaria a qualquer outro programa de servidor ASGI.
/// tip | Dica
Adicionando o `standard`, o Uvicorn instalará e usará algumas dependências extras recomendadas.
Isso inclui o `uvloop`, a substituição de alto desempenho para `asyncio`, que fornece um grande aumento de desempenho de concorrência.
Quando você instala o FastAPI com algo como `pip install "fastapi[standard]"`, você já obtém `uvicorn[standard]` também.
///
## Execute o Programa Servidor { #run-the-server-program }
Se você instalou um servidor ASGI manualmente, normalmente precisará passar uma string de importação em um formato especial para que ele importe sua aplicação FastAPI:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
/// note | Nota
O comando `uvicorn main:app` refere-se a:
* `main`: o arquivo `main.py` (o "módulo" Python).
* `app`: o objeto criado dentro de `main.py` com a linha `app = FastAPI()`.
É equivalente a:
```Python
from main import app
```
///
Cada programa de servidor ASGI alternativo teria um comando semelhante, você pode ler mais na documentação respectiva.
/// warning | Atenção
Uvicorn e outros servidores suportam a opção `--reload` que é útil durante o desenvolvimento.
A opção `--reload` consome muito mais recursos, é mais instável, etc.
Ela ajuda muito durante o **desenvolvimento**, mas você **não deve** usá-la em **produção**.
///
## Conceitos de Implantação { #deployment-concepts }
Esses exemplos executam o programa do servidor (por exemplo, Uvicorn), iniciando **um único processo**, ouvindo em todos os IPs (`0.0.0.0`) em uma porta predefinida (por exemplo, `80`).
Esta é a ideia básica. Mas você provavelmente vai querer cuidar de algumas coisas adicionais, como:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Passos anteriores antes de começar
Vou te contar mais sobre cada um desses conceitos, como pensar sobre eles e alguns exemplos concretos com estratégias para lidar com eles nos próximos capítulos. 🚀
+139
View File
@@ -0,0 +1,139 @@
# Trabalhadores do Servidor - Uvicorn com Trabalhadores { #server-workers-uvicorn-with-workers }
Vamos rever os conceitos de implantação anteriores:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* **Replicação (o número de processos em execução)**
* Memória
* Etapas anteriores antes de iniciar
Até este ponto, com todos os tutoriais nos documentos, você provavelmente estava executando um **programa de servidor**, por exemplo, usando o comando `fastapi`, que executa o Uvicorn, executando um **único processo**.
Ao implantar aplicativos, você provavelmente desejará ter alguma **replicação de processos** para aproveitar **vários núcleos** e poder lidar com mais solicitações.
Como você viu no capítulo anterior sobre [Conceitos de implantação](concepts.md){.internal-link target=_blank}, há várias estratégias que você pode usar.
Aqui mostrarei como usar o **Uvicorn** com **processos de trabalho** usando o comando `fastapi` ou o comando `uvicorn` diretamente.
/// info | Informação
Se você estiver usando contêineres, por exemplo com Docker ou Kubernetes, falarei mais sobre isso no próximo capítulo: [FastAPI em contêineres - Docker](docker.md){.internal-link target=_blank}.
Em particular, ao executar no **Kubernetes** você provavelmente **não** vai querer usar vários trabalhadores e, em vez disso, executar **um único processo Uvicorn por contêiner**, mas falarei sobre isso mais adiante neste capítulo.
///
## Vários trabalhadores { #multiple-workers }
Você pode iniciar vários trabalhadores com a opção de linha de comando `--workers`:
//// tab | `fastapi`
Se você usar o comando `fastapi`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
////
//// tab | `uvicorn`
Se você preferir usar o comando `uvicorn` diretamente:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
```
</div>
////
A única opção nova aqui é `--workers` informando ao Uvicorn para iniciar 4 processos de trabalho.
Você também pode ver que ele mostra o **PID** de cada processo, `27365` para o processo pai (este é o **gerenciador de processos**) e um para cada processo de trabalho: `27368`, `27369`, `27370` e `27367`.
## Conceitos de Implantação { #deployment-concepts }
Aqui você viu como usar vários **trabalhadores** para **paralelizar** a execução do aplicativo, aproveitar **vários núcleos** na CPU e conseguir atender **mais solicitações**.
Da lista de conceitos de implantação acima, o uso de trabalhadores ajudaria principalmente com a parte da **replicação** e um pouco com as **reinicializações**, mas você ainda precisa cuidar dos outros:
* **Segurança - HTTPS**
* **Executando na inicialização**
* ***Reinicializações***
* Replicação (o número de processos em execução)
* **Memória**
* **Etapas anteriores antes de iniciar**
## Contêineres e Docker { #containers-and-docker }
No próximo capítulo sobre [FastAPI em contêineres - Docker](docker.md){.internal-link target=_blank}, explicarei algumas estratégias que você pode usar para lidar com os outros **conceitos de implantação**.
Vou mostrar como **construir sua própria imagem do zero** para executar um único processo Uvicorn. É um processo simples e provavelmente é o que você gostaria de fazer ao usar um sistema de gerenciamento de contêineres distribuídos como o **Kubernetes**.
## Recapitular { #recap }
Você pode usar vários processos de trabalho com a opção CLI `--workers` com os comandos `fastapi` ou `uvicorn` para aproveitar as vantagens de **CPUs multi-core** e executar **vários processos em paralelo**.
Você pode usar essas ferramentas e ideias se estiver configurando **seu próprio sistema de implantação** enquanto cuida dos outros conceitos de implantação.
Confira o próximo capítulo para aprender sobre **FastAPI** com contêineres (por exemplo, Docker e Kubernetes). Você verá que essas ferramentas têm maneiras simples de resolver os outros **conceitos de implantação** também. ✨
+93
View File
@@ -0,0 +1,93 @@
# Sobre as versões do FastAPI { #about-fastapi-versions }
**FastAPI** já está sendo usado em produção em muitas aplicações e sistemas. E a cobertura de testes é mantida em 100%. Mas seu desenvolvimento ainda está avançando rapidamente.
Novas funcionalidades são adicionadas com frequência, bugs são corrigidos regularmente e o código continua melhorando continuamente.
É por isso que as versões atuais ainda são `0.x.x`, isso reflete que cada versão pode potencialmente ter mudanças significativas. Isso segue as convenções de <a href="https://semver.org/" class="external-link" target="_blank">Versionamento Semântico</a>.
Você pode criar aplicações de produção com **FastAPI** agora mesmo (e provavelmente já vem fazendo isso há algum tempo), apenas certifique-se de usar uma versão que funcione corretamente com o resto do seu código.
## Fixe a sua versão de `fastapi` { #pin-your-fastapi-version }
A primeira coisa que você deve fazer é "fixar" a versão do **FastAPI** que você está utilizando na versão mais recente específica que você sabe que funciona corretamente para a sua aplicação.
Por exemplo, suponha que você esteja usando a versão `0.112.0` em sua aplicação.
Se você usa um arquivo `requirements.txt`, você poderia especificar a versão com:
```txt
fastapi[standard]==0.112.0
```
isso significaria que você usaria exatamente a versão `0.112.0`.
Ou você também poderia fixá-la com:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
isso significaria que você usaria as versões `0.112.0` ou superiores, mas menores que `0.113.0`, por exemplo, a versão `0.112.2` ainda seria aceita.
Se você usa qualquer outra ferramenta para gerenciar suas instalações, como `uv`, Poetry, Pipenv ou outras, todas elas têm uma forma de definir versões específicas para seus pacotes.
## Versões disponíveis { #available-versions }
Você pode ver as versões disponíveis (por exemplo, para verificar qual é a mais recente) nas [Release Notes](../release-notes.md){.internal-link target=_blank}.
## Sobre versões { #about-versions }
Seguindo as convenções de Versionamento Semântico, qualquer versão abaixo de `1.0.0` pode potencialmente adicionar mudanças significativas.
FastAPI também segue a convenção de que qualquer alteração de versão "PATCH" é para correções de bugs e mudanças que não quebram compatibilidade.
/// tip | Dica
O "PATCH" é o último número, por exemplo, em `0.2.3`, a versão PATCH é `3`.
///
Logo, você deveria conseguir fixar a versão, como:
```txt
fastapi>=0.45.0,<0.46.0
```
Mudanças significativas e novas funcionalidades são adicionadas em versões "MINOR".
/// tip | Dica
O "MINOR" é o número do meio, por exemplo, em `0.2.3`, a versão MINOR é `2`.
///
## Atualizando as versões do FastAPI { #upgrading-the-fastapi-versions }
Você deve adicionar testes para a sua aplicação.
Com **FastAPI** isso é muito fácil (graças ao Starlette), veja a documentação: [Testing](../tutorial/testing.md){.internal-link target=_blank}
Depois que você tiver testes, você pode atualizar a sua versão do **FastAPI** para uma mais recente e se certificar de que todo o seu código está funcionando corretamente executando seus testes.
Se tudo estiver funcionando, ou após você realizar as alterações necessárias e todos os testes estiverem passando, então você pode fixar sua versão de `fastapi` para essa versão mais recente.
## Sobre Starlette { #about-starlette }
Não é recomendado fixar a versão de `starlette`.
Versões diferentes de **FastAPI** utilizarão uma versão específica e mais recente de Starlette.
Então, você pode deixar **FastAPI** usar a versão correta do Starlette.
## Sobre Pydantic { #about-pydantic }
Pydantic inclui os testes para **FastAPI** em seus próprios testes, então novas versões do Pydantic (acima de `1.0.0`) são sempre compatíveis com FastAPI.
Você pode fixar o Pydantic em qualquer versão acima de `1.0.0` que funcione para você.
Por exemplo:
```txt
pydantic>=2.7.0,<3.0.0
```
+298
View File
@@ -0,0 +1,298 @@
# Variáveis de Ambiente { #environment-variables }
/// tip | Dica
Se você já sabe o que são "variáveis de ambiente" e como usá-las, pode pular esta seção.
///
Uma variável de ambiente (também conhecida como "**env var**") é uma variável que existe **fora** do código Python, no **sistema operacional**, e pode ser lida pelo seu código Python (ou por outros programas também).
Variáveis de ambiente podem ser úteis para lidar com **configurações** do aplicativo, como parte da **instalação** do Python, etc.
## Criar e Usar Variáveis de Ambiente { #create-and-use-env-vars }
Você pode **criar** e usar variáveis de ambiente no **shell (terminal)**, sem precisar do Python:
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
// Você pode criar uma variável de ambiente MY_NAME com
$ export MY_NAME="Wade Wilson"
// Então você pode usá-la com outros programas, como
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
// Criar uma variável de ambiente MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Usá-la com outros programas, como
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
</div>
////
## Ler Variáveis de Ambiente no Python { #read-env-vars-in-python }
Você também pode criar variáveis de ambiente **fora** do Python, no terminal (ou com qualquer outro método) e depois **lê-las no Python**.
Por exemplo, você poderia ter um arquivo `main.py` com:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Dica
O segundo argumento para <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> é o valor padrão a ser retornado.
Se não for fornecido, é `None` por padrão, Aqui fornecemos `"World"` como o valor padrão a ser usado.
///
Então você poderia chamar esse programa Python:
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
// Aqui ainda não definimos a variável de ambiente
$ python main.py
// Como não definimos a variável de ambiente, obtemos o valor padrão
Hello World from Python
// Mas se criarmos uma variável de ambiente primeiro
$ export MY_NAME="Wade Wilson"
// E então chamar o programa novamente
$ python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
// Aqui ainda não definimos a variável de ambiente
$ python main.py
// Como não definimos a variável de ambiente, obtemos o valor padrão
Hello World from Python
// Mas se criarmos uma variável de ambiente primeiro
$ $Env:MY_NAME = "Wade Wilson"
// E então chamar o programa novamente
$ python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
```
</div>
////
Como as variáveis de ambiente podem ser definidas fora do código, mas podem ser lidas pelo código e não precisam ser armazenadas (com versão no `git`) com o restante dos arquivos, é comum usá-las para configurações ou **definições**.
Você também pode criar uma variável de ambiente apenas para uma **invocação específica do programa**, que só está disponível para aquele programa e apenas pela duração dele.
Para fazer isso, crie-a na mesma linha, antes do próprio programa:
<div class="termy">
```console
// Criar uma variável de ambiente MY_NAME para esta chamada de programa
$ MY_NAME="Wade Wilson" python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
// A variável de ambiente não existe mais depois
$ python main.py
Hello World from Python
```
</div>
/// tip | Dica
Você pode ler mais sobre isso em <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>.
///
## Tipos e Validação { #types-and-validation }
Essas variáveis de ambiente só podem lidar com **strings de texto**, pois são externas ao Python e precisam ser compatíveis com outros programas e com o resto do sistema (e até mesmo com diferentes sistemas operacionais, como Linux, Windows, macOS).
Isso significa que **qualquer valor** lido em Python de uma variável de ambiente **será uma `str`**, e qualquer conversão para um tipo diferente ou qualquer validação precisa ser feita no código.
Você aprenderá mais sobre como usar variáveis de ambiente para lidar com **configurações do aplicativo** no [Guia do Usuário Avançado - Configurações e Variáveis de Ambiente](./advanced/settings.md){.internal-link target=_blank}.
## Variável de Ambiente `PATH` { #path-environment-variable }
Existe uma variável de ambiente **especial** chamada **`PATH`** que é usada pelos sistemas operacionais (Linux, macOS, Windows) para encontrar programas para executar.
O valor da variável `PATH` é uma longa string composta por diretórios separados por dois pontos `:` no Linux e macOS, e por ponto e vírgula `;` no Windows.
Por exemplo, a variável de ambiente `PATH` poderia ter esta aparência:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Isso significa que o sistema deve procurar programas nos diretórios:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Isso significa que o sistema deve procurar programas nos diretórios:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Quando você digita um **comando** no terminal, o sistema operacional **procura** o programa em **cada um dos diretórios** listados na variável de ambiente `PATH`.
Por exemplo, quando você digita `python` no terminal, o sistema operacional procura um programa chamado `python` no **primeiro diretório** dessa lista.
Se ele o encontrar, então ele o **usará**. Caso contrário, ele continua procurando nos **outros diretórios**.
### Instalando o Python e Atualizando o `PATH` { #installing-python-and-updating-the-path }
Durante a instalação do Python, você pode ser questionado sobre a atualização da variável de ambiente `PATH`.
//// tab | Linux, macOS
Vamos supor que você instale o Python e ele fique em um diretório `/opt/custompython/bin`.
Se você concordar em atualizar a variável de ambiente `PATH`, o instalador adicionará `/opt/custompython/bin` para a variável de ambiente `PATH`.
Poderia parecer assim:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Dessa forma, ao digitar `python` no terminal, o sistema encontrará o programa Python em `/opt/custompython/bin` (último diretório) e o utilizará.
////
//// tab | Windows
Digamos que você instala o Python e ele acaba em um diretório `C:\opt\custompython\bin`.
Se você disser sim para atualizar a variável de ambiente `PATH`, o instalador adicionará `C:\opt\custompython\bin` à variável de ambiente `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Dessa forma, quando você digitar `python` no terminal, o sistema encontrará o programa Python em `C:\opt\custompython\bin` (o último diretório) e o utilizará.
////
Então, se você digitar:
<div class="termy">
```console
$ python
```
</div>
//// tab | Linux, macOS
O sistema **encontrará** o programa `python` em `/opt/custompython/bin` e o executará.
Seria aproximadamente equivalente a digitar:
<div class="termy">
```console
$ /opt/custompython/bin/python
```
</div>
////
//// tab | Windows
O sistema **encontrará** o programa `python` em `C:\opt\custompython\bin\python` e o executará.
Seria aproximadamente equivalente a digitar:
<div class="termy">
```console
$ C:\opt\custompython\bin\python
```
</div>
////
Essas informações serão úteis ao aprender sobre [Ambientes Virtuais](virtual-environments.md){.internal-link target=_blank}.
## Conclusão { #conclusion }
Com isso, você deve ter uma compreensão básica do que são **variáveis de ambiente** e como usá-las em Python.
Você também pode ler mais sobre elas na <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">Wikipedia para Variáveis de Ambiente</a>.
Em muitos casos, não é muito óbvio como as variáveis de ambiente seriam úteis e aplicáveis imediatamente. Mas elas continuam aparecendo em muitos cenários diferentes quando você está desenvolvendo, então é bom saber sobre elas.
Por exemplo, você precisará dessas informações na próxima seção, sobre [Ambientes Virtuais](virtual-environments.md).
+75
View File
@@ -0,0 +1,75 @@
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** é um programa de linha de comando que você pode usar para servir sua aplicação FastAPI, gerenciar seu projeto FastAPI e muito mais.
Quando você instala o FastAPI (por exemplo, com `pip install "fastapi[standard]"`), isso inclui um pacote chamado `fastapi-cli`; esse pacote disponibiliza o comando `fastapi` no terminal.
Para executar sua aplicação FastAPI durante o desenvolvimento, você pode usar o comando `fastapi dev`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
O programa de linha de comando chamado `fastapi` é o **FastAPI CLI**.
O FastAPI CLI recebe o caminho para o seu programa Python (por exemplo, `main.py`), detecta automaticamente a instância de `FastAPI` (comumente nomeada `app`), determina a forma correta de importação e então a serve.
Para produção, você usaria `fastapi run`. 🚀
Internamente, o **FastAPI CLI** usa o <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, um servidor ASGI de alta performance e pronto para produção. 😎
## `fastapi dev` { #fastapi-dev }
Executar `fastapi dev` inicia o modo de desenvolvimento.
Por padrão, o recarregamento automático está ativado, recarregando o servidor automaticamente quando você faz mudanças no seu código. Isso consome muitos recursos e pode ser menos estável do que quando está desativado. Você deve usá-lo apenas no desenvolvimento. Ele também escuta no endereço IP `127.0.0.1`, que é o IP para a sua máquina se comunicar apenas consigo mesma (`localhost`).
## `fastapi run` { #fastapi-run }
Executar `fastapi run` inicia o FastAPI em modo de produção por padrão.
Por padrão, o recarregamento automático está desativado. Ele também escuta no endereço IP `0.0.0.0`, o que significa todos os endereços IP disponíveis; dessa forma, ficará acessível publicamente para qualquer pessoa que consiga se comunicar com a máquina. É assim que você normalmente o executaria em produção, por exemplo, em um contêiner.
Na maioria dos casos, você teria (e deveria ter) um "proxy de terminação" tratando o HTTPS por cima; isso dependerá de como você faz o deploy da sua aplicação, seu provedor pode fazer isso por você ou talvez seja necessário que você configure isso por conta própria.
/// tip | Dica
Você pode aprender mais sobre isso na [documentação de deployment](deployment/index.md){.internal-link target=_blank}.
///
+201
View File
@@ -0,0 +1,201 @@
# Recursos { #features }
## Recursos do FastAPI { #fastapi-features }
**FastAPI** te oferece o seguinte:
### Baseado em padrões abertos { #based-on-open-standards }
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> para criação de APIs, incluindo declarações de <abbr title="também conhecido como: endpoints, routes">caminho</abbr> <abbr title="também conhecido como métodos HTTP, como POST, GET, PUT, DELETE">operações</abbr>, parâmetros, requisições de corpo, segurança etc.
* Modelo de documentação automática com <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (já que o OpenAPI em si é baseado no JSON Schema).
* Projetado em cima desses padrões após um estudo meticuloso, em vez de uma reflexão breve.
* Isso também permite o uso de **geração de código do cliente** automaticamente em muitas linguagens.
### Documentação automática { #automatic-docs }
Documentação interativa da API e navegação web da interface de usuário. Como o framework é baseado no OpenAPI, há várias opções, 2 incluídas por padrão.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, com navegação interativa, chame e teste sua API diretamente do navegador.
![Interação Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Documentação alternativa da API com <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Apenas Python moderno { #just-modern-python }
Tudo é baseado no padrão das declarações de **tipos do Python** (graças ao Pydantic). Nenhuma sintaxe nova para aprender. Apenas o padrão moderno do Python.
Se você precisa refrescar a memória rapidamente sobre como usar tipos do Python (mesmo que você não use o FastAPI), confira esse rápido tutorial: [Tipos do Python](python-types.md){.internal-link target=_blank}.
Você escreve Python padrão com tipos:
```Python
from datetime import date
from pydantic import BaseModel
# Declare uma variável como str
# e obtenha suporte do editor dentro da função
def main(user_id: str):
return user_id
# Um modelo do Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Que então pode ser usado como:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Informação
`**second_user_data` quer dizer:
Passe as chaves e valores do dicionário `second_user_data` diretamente como argumentos chave-valor, equivalente a: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Suporte de editores { #editor-support }
Todo o framework foi projetado para ser fácil e intuitivo de usar, todas as decisões foram testadas em vários editores antes do início do desenvolvimento, para garantir a melhor experiência de desenvolvimento.
Na pesquisa de desenvolvedores Python, ficou claro <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">que um dos recursos mais utilizados é o "preenchimento automático"</a>.
Todo o framework **FastAPI** é feito para satisfazer isso. O preenchimento automático funciona em todos os lugares.
Você raramente precisará voltar à documentação.
Aqui está como o editor poderá te ajudar:
* no <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
* no <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
Você terá preenchimento automático no seu código que você poderia considerar impossível antes. Como por exemplo, a chave `price` dentro do corpo JSON (que poderia ter sido aninhado) que vem de uma requisição.
Sem a necessidade de digitar nomes de chaves erroneamente, ir e voltar entre documentações, ou rolar pela página para descobrir se você utilizou `username` ou `user_name`.
### Breve { #short }
**padrões** sensíveis para tudo, com configurações adicionais em todos os lugares. Todos os parâmetros podem ser regulados para fazer o que você precisa e para definir a API que você necessita.
Por padrão, tudo **"simplesmente funciona"**.
### Validação { #validation }
* Validação para a maioria dos (ou todos?) **tipos de dados** do Python, incluindo:
* objetos JSON (`dict`).
* arrays JSON (`list`), definindo tipos dos itens.
* campos String (`str`), definindo tamanho mínimo e máximo.
* Numbers (`int`, `float`) com valores mínimos e máximos, etc.
* Validação de tipos mais exóticos, como:
* URL.
* Email.
* UUID.
* ...e outros.
Toda a validação é controlada pelo robusto e bem estabelecido **Pydantic**.
### Segurança e autenticação { #security-and-authentication }
Segurança e autenticação integradas. Sem nenhum compromisso com bancos de dados ou modelos de dados.
Todos os esquemas de seguranças definidos no OpenAPI, incluindo:
* HTTP Basic.
* **OAuth2** (também com **tokens JWT**). Confira o tutorial em [OAuth2 com JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
* Chaves de API em:
* Headers.
* parâmetros da Query.
* Cookies etc.
Além disso, todos os recursos de segurança do Starlette (incluindo **cookies de sessão**).
Tudo construído como ferramentas e componentes reutilizáveis que são fáceis de integrar com seus sistemas, armazenamento de dados, banco de dados relacionais e não-relacionais etc.
### Injeção de dependência { #dependency-injection }
FastAPI inclui um sistema de <abbr title='também conhecido como "components", "resources", "services", "providers"'><strong>injeção de dependência</strong></abbr> extremamente fácil de usar, mas extremamente poderoso.
* Mesmo dependências podem ter dependências, criando uma hierarquia ou **"grafo" de dependências**.
* Tudo **automaticamente controlado** pelo framework.
* Todas as dependências podem pedir dados das requisições e **ampliar** as restrições e documentação automática da **operação de caminho**.
* **Validação automática** mesmo para parâmetros da *operação de caminho* definidos em dependências.
* Suporte para sistemas de autenticação complexos, **conexões com banco de dados** etc.
* **Sem comprometer** os bancos de dados, frontends etc. Mas fácil integração com todos eles.
### "Plug-ins" ilimitados { #unlimited-plug-ins }
Ou, de outra forma, sem a necessidade deles, importe e use o código que precisar.
Qualquer integração é projetada para ser tão simples de usar (com dependências) que você pode criar um "plug-in" para suas aplicações com 2 linhas de código usando a mesma estrutura e sintaxe para as suas *operações de caminho*.
### Testado { #tested }
* 100% <abbr title="A quantidade de código que é testada automaticamente">de cobertura de testes</abbr>.
* 100% do código com <abbr title="Anotações de tipo do Python, com isso seu editor e ferramentas externas podem te dar um suporte melhor">anotações de tipo</abbr>.
* Usado para aplicações em produção.
## Recursos do Starlette { #starlette-features }
**FastAPI** é totalmente compatível com (e baseado no) <a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Então, qualquer código adicional Starlette que você tiver, também funcionará.
`FastAPI` é na verdade uma sub-classe do `Starlette`. Então, se você já conhece ou usa Starlette, a maioria das funcionalidades se comportará da mesma forma.
Com **FastAPI**, você terá todos os recursos do **Starlette** (já que FastAPI é apenas um Starlette com esteróides):
* Desempenho realmente impressionante. É <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">um dos frameworks Python disponíveis mais rápidos, a par com o **NodeJS** e **Go**</a>.
* Suporte a **WebSocket**.
* Tarefas em processo background.
* Eventos na inicialização e encerramento.
* Cliente de testes construído sobre HTTPX.
* Respostas em **CORS**, GZip, Static Files, Streaming.
* Suporte a **Session e Cookie**.
* 100% de cobertura de testes.
* 100% do código utilizando anotações de tipo.
## Recursos do Pydantic { #pydantic-features }
**FastAPI** é totalmente compatível com (e baseado no) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Então, qualquer código Pydantic adicional que você tiver, também funcionará.
Incluindo bibliotecas externas também baseadas no Pydantic, como <abbr title="Object-Relational Mapper">ORM</abbr>s e <abbr title="Object-Document Mapper">ODM</abbr>s para bancos de dados.
Isso também significa que em muitos casos você poderá passar o mesmo objeto que você receber de uma requisição **diretamente para o banco de dados**, já que tudo é validado automaticamente.
O mesmo se aplica no sentido inverso, em muitos casos você poderá simplesmente passar o objeto que você recebeu do banco de dados **diretamente para o cliente**.
Com **FastAPI** você terá todos os recursos do **Pydantic** (já que FastAPI utiliza o Pydantic para todo o controle dos dados):
* **Sem pegadinhas**:
* Sem novas definições de esquema de micro-linguagem para aprender.
* Se você conhece os tipos do Python, você sabe como usar o Pydantic.
* Vai bem com o/a seu/sua **<abbr title="Ambiente de Desenvolvimento Integrado, similar a um editor de código">IDE</abbr>/<abbr title="Um programa que confere erros de código">linter</abbr>/cérebro**:
* Como as estruturas de dados do Pydantic são apenas instâncias de classes que você define, o preenchimento automático, linting, mypy e a sua intuição devem funcionar corretamente com seus dados validados.
* Valida **estruturas complexas**:
* Use modelos hierárquicos do Pydantic, `List` e `Dict` do `typing` do Python, etc.
* Validadores permitem que esquemas de dados complexos sejam limpos e facilmente definidos, conferidos e documentados como JSON Schema.
* Você pode ter **JSONs aninhados** profundamente e tê-los todos validados e anotados.
* **Extensível**:
* Pydantic permite que tipos de dados personalizados sejam definidos ou você pode estender a validação com métodos em um modelo decorado com seu decorador de validador.
* 100% de cobertura de testes.
+256
View File
@@ -0,0 +1,256 @@
# Ajude o FastAPI - Obtenha ajuda { #help-fastapi-get-help }
Você gosta do **FastAPI**?
Você gostaria de ajudar o FastAPI, outras pessoas usuárias e o autor?
Ou você gostaria de obter ajuda com o **FastAPI**?
Há maneiras muito simples de ajudar (várias envolvem apenas um ou dois cliques).
E também há várias formas de obter ajuda.
## Assine a newsletter { #subscribe-to-the-newsletter }
Você pode assinar a (infrequente) [newsletter do **FastAPI and friends**](newsletter.md){.internal-link target=_blank} para ficar por dentro de:
* Notícias sobre FastAPI e amigos 🚀
* Tutoriais 📝
* Funcionalidades ✨
* Mudanças incompatíveis 🚨
* Dicas e truques ✅
## Siga o FastAPI no X (Twitter) { #follow-fastapi-on-x-twitter }
<a href="https://x.com/fastapi" class="external-link" target="_blank">Siga @fastapi no **X (Twitter)**</a> para receber as últimas notícias sobre o **FastAPI**. 🐦
## Dê uma estrela ao **FastAPI** no GitHub { #star-fastapi-in-github }
Você pode “marcar com estrela” o FastAPI no GitHub (clicando no botão de estrela no canto superior direito): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
Ao adicionar uma estrela, outras pessoas conseguirão encontrá-lo com mais facilidade e verão que já foi útil para muita gente.
## Acompanhe o repositório no GitHub para lançamentos { #watch-the-github-repository-for-releases }
Você pode “acompanhar” (watch) o FastAPI no GitHub (clicando no botão “watch” no canto superior direito): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Lá você pode selecionar “Apenas lançamentos” (Releases only).
Fazendo isso, você receberá notificações (no seu email) sempre que houver um novo lançamento (uma nova versão) do **FastAPI** com correções de bugs e novas funcionalidades.
## Conecte-se com o autor { #connect-with-the-author }
Você pode se conectar <a href="https://tiangolo.com" class="external-link" target="_blank">comigo (Sebastián Ramírez / `tiangolo`)</a>, o autor.
Você pode:
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Me seguir no **GitHub**</a>.
* Ver outros projetos Open Source que criei e que podem ajudar você.
* Me seguir para saber quando eu criar um novo projeto Open Source.
* <a href="https://x.com/tiangolo" class="external-link" target="_blank">Me seguir no **X (Twitter)**</a> ou no <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>.
* Me contar como você usa o FastAPI (adoro saber disso).
* Ficar sabendo quando eu fizer anúncios ou lançar novas ferramentas.
* Você também pode <a href="https://x.com/fastapi" class="external-link" target="_blank">seguir @fastapi no X (Twitter)</a> (uma conta separada).
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Me seguir no **LinkedIn**</a>.
* Ver quando eu fizer anúncios ou lançar novas ferramentas (embora eu use mais o X (Twitter) 🤷‍♂).
* Ler o que escrevo (ou me seguir) no <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> ou no <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Ler outras ideias, artigos e conhecer ferramentas que criei.
* Me seguir para ver quando eu publicar algo novo.
## Tweet sobre o **FastAPI** { #tweet-about-fastapi }
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Tweet sobre o **FastAPI**</a> e conte para mim e para outras pessoas por que você gosta dele. 🎉
Eu adoro saber como o **FastAPI** está sendo usado, o que você tem curtido nele, em qual projeto/empresa você o utiliza, etc.
## Vote no FastAPI { #vote-for-fastapi }
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vote no **FastAPI** no Slant</a>.
* <a href="https://alternativeto.net/software/fastapi/about/" class="external-link" target="_blank">Vote no **FastAPI** no AlternativeTo</a>.
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Diga que você usa o **FastAPI** no StackShare</a>.
## Ajude outras pessoas com perguntas no GitHub { #help-others-with-questions-in-github }
Você pode tentar ajudar outras pessoas com suas perguntas em:
* <a href="https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub Discussions</a>
* <a href="https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub Issues</a>
Em muitos casos você já pode saber a resposta para aquelas perguntas. 🤓
Se você estiver ajudando muitas pessoas com suas perguntas, você se tornará um(a) [Especialista em FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank} oficial. 🎉
Apenas lembre-se, o ponto mais importante é: tente ser gentil. As pessoas chegam com frustrações e, em muitos casos, não perguntam da melhor forma, mas tente ao máximo ser gentil. 🤗
A ideia é que a comunidade do **FastAPI** seja gentil e acolhedora. Ao mesmo tempo, não aceite bullying ou comportamentos desrespeitosos com outras pessoas. Temos que cuidar uns dos outros.
---
Veja como ajudar outras pessoas com perguntas (em discussions ou issues):
### Entenda a pergunta { #understand-the-question }
* Verifique se você consegue entender qual é o **objetivo** e o caso de uso de quem está perguntando.
* Depois verifique se a pergunta (a grande maioria são perguntas) está **clara**.
* Em muitos casos a pergunta feita é sobre uma solução imaginada pela pessoa usuária, mas pode haver uma solução **melhor**. Se você entender melhor o problema e o caso de uso, pode sugerir uma **solução alternativa** melhor.
* Se você não entender a pergunta, peça mais **detalhes**.
### Reproduza o problema { #reproduce-the-problem }
Na maioria dos casos e na maioria das perguntas há algo relacionado ao **código original** da pessoa.
Em muitos casos ela só copia um fragmento do código, mas isso não é suficiente para **reproduzir o problema**.
* Você pode pedir que forneçam um <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">exemplo mínimo, reproduzível</a>, que você possa **copiar e colar** e executar localmente para ver o mesmo erro ou comportamento que elas estão vendo, ou para entender melhor o caso de uso.
* Se você estiver muito generoso(a), pode tentar **criar um exemplo** assim você mesmo(a), apenas com base na descrição do problema. Só tenha em mente que isso pode levar bastante tempo e pode ser melhor pedir primeiro que esclareçam o problema.
### Sugira soluções { #suggest-solutions }
* Depois de conseguir entender a pergunta, você pode dar uma possível **resposta**.
* Em muitos casos, é melhor entender o **problema subjacente ou caso de uso**, pois pode haver uma forma melhor de resolver do que aquilo que estão tentando fazer.
### Peça para encerrar { #ask-to-close }
Se a pessoa responder, há uma grande chance de você ter resolvido o problema, parabéns, **você é um(a) herói(na)**! 🦸
* Agora, se isso resolveu o problema, você pode pedir para:
* No GitHub Discussions: marcar o comentário como a **resposta**.
* No GitHub Issues: **encerrar** a issue.
## Acompanhe o repositório do GitHub { #watch-the-github-repository }
Você pode “acompanhar” (watch) o FastAPI no GitHub (clicando no botão “watch” no canto superior direito): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Se você selecionar “Acompanhando” (Watching) em vez de “Apenas lançamentos” (Releases only), receberá notificações quando alguém criar uma nova issue ou pergunta. Você também pode especificar que quer ser notificado(a) apenas sobre novas issues, ou discussions, ou PRs, etc.
Assim você pode tentar ajudar a resolver essas questões.
## Faça perguntas { #ask-questions }
Você pode <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">criar uma nova pergunta</a> no repositório do GitHub, por exemplo para:
* Fazer uma **pergunta** ou perguntar sobre um **problema**.
* Sugerir uma nova **funcionalidade**.
**Nota**: se você fizer isso, então vou pedir que você também ajude outras pessoas. 😉
## Revise Pull Requests { #review-pull-requests }
Você pode me ajudar revisando pull requests de outras pessoas.
Novamente, por favor tente ao máximo ser gentil. 🤗
---
Veja o que ter em mente e como revisar um pull request:
### Entenda o problema { #understand-the-problem }
* Primeiro, garanta que você **entendeu o problema** que o pull request tenta resolver. Pode haver uma discussão mais longa em uma Discussion ou issue do GitHub.
* Também há uma boa chance de o pull request não ser realmente necessário porque o problema pode ser resolvido de uma **forma diferente**. Aí você pode sugerir ou perguntar sobre isso.
### Não se preocupe com estilo { #dont-worry-about-style }
* Não se preocupe muito com coisas como estilos de mensagens de commit, eu vou fazer squash e merge personalizando o commit manualmente.
* Também não se preocupe com regras de estilo, já há ferramentas automatizadas verificando isso.
E se houver qualquer outra necessidade de estilo ou consistência, vou pedir diretamente, ou vou adicionar commits por cima com as mudanças necessárias.
### Verifique o código { #check-the-code }
* Verifique e leia o código, veja se faz sentido, **execute localmente** e veja se realmente resolve o problema.
* Depois **comente** dizendo que você fez isso, é assim que saberei que você realmente verificou.
/// info | Informação
Infelizmente, eu não posso simplesmente confiar em PRs que têm várias aprovações.
Já aconteceu várias vezes de haver PRs com 3, 5 ou mais aprovações, provavelmente porque a descrição é atraente, mas quando eu verifico os PRs, eles estão quebrados, têm um bug, ou não resolvem o problema que afirmam resolver. 😅
Por isso, é realmente importante que você leia e execute o código, e me avise nos comentários que você fez isso. 🤓
///
* Se o PR puder ser simplificado de alguma forma, você pode pedir isso, mas não há necessidade de ser exigente demais, pode haver muitos pontos de vista subjetivos (e eu terei o meu também 🙈), então é melhor focar nas coisas fundamentais.
### Testes { #tests }
* Me ajude a verificar se o PR tem **testes**.
* Verifique se os testes **falham** antes do PR. 🚨
* Depois verifique se os testes **passam** após o PR. ✅
* Muitos PRs não têm testes, você pode **lembrar** a pessoa de adicionar testes, ou até **sugerir** alguns testes você mesmo(a). Essa é uma das coisas que consomem mais tempo e você pode ajudar muito com isso.
* Depois também comente o que você testou, assim vou saber que você verificou. 🤓
## Crie um Pull Request { #create-a-pull-request }
Você pode [contribuir](contributing.md){.internal-link target=_blank} com o código-fonte fazendo Pull Requests, por exemplo:
* Para corrigir um erro de digitação que você encontrou na documentação.
* Para compartilhar um artigo, vídeo ou podcast que você criou ou encontrou sobre o FastAPI, <a href="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">editando este arquivo</a>.
* Garanta que você adicione seu link no início da seção correspondente.
* Para ajudar a [traduzir a documentação](contributing.md#translations){.internal-link target=_blank} para seu idioma.
* Você também pode ajudar a revisar as traduções criadas por outras pessoas.
* Para propor novas seções de documentação.
* Para corrigir uma issue/bug existente.
* Garanta que você adicione testes.
* Para adicionar uma nova funcionalidade.
* Garanta que você adicione testes.
* Garanta que você adicione documentação se for relevante.
## Ajude a manter o FastAPI { #help-maintain-fastapi }
Ajude-me a manter o **FastAPI**! 🤓
Há muito trabalho a fazer e, para a maior parte dele, **VOCÊ** pode ajudar.
As principais tarefas que você pode fazer agora são:
* [Ajudar outras pessoas com perguntas no GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (veja a seção acima).
* [Revisar Pull Requests](#review-pull-requests){.internal-link target=_blank} (veja a seção acima).
Essas duas tarefas são as que **mais consomem tempo**. Esse é o principal trabalho de manter o FastAPI.
Se você puder me ajudar com isso, **você está me ajudando a manter o FastAPI** e garantindo que ele continue **avançando mais rápido e melhor**. 🚀
## Entre no chat { #join-the-chat }
Entre no 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">servidor de chat do Discord</a> 👥 e converse com outras pessoas da comunidade FastAPI.
/// tip | Dica
Para perguntas, faça-as no <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, há uma chance muito maior de você receber ajuda pelos [Especialistas em FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}.
Use o chat apenas para outras conversas gerais.
///
### Não use o chat para perguntas { #dont-use-the-chat-for-questions }
Tenha em mente que, como os chats permitem uma “conversa mais livre”, é fácil fazer perguntas muito gerais e mais difíceis de responder, então você pode acabar não recebendo respostas.
No GitHub, o template vai orientar você a escrever a pergunta certa para que você consiga obter uma boa resposta com mais facilidade, ou até resolver o problema sozinho(a) antes de perguntar. E no GitHub eu consigo garantir que sempre vou responder tudo, mesmo que leve algum tempo. Eu pessoalmente não consigo fazer isso com os sistemas de chat. 😅
As conversas nos sistemas de chat também não são tão fáceis de pesquisar quanto no GitHub, então perguntas e respostas podem se perder na conversa. E somente as que estão no GitHub contam para você se tornar um(a) [Especialista em FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}, então é bem provável que você receba mais atenção no GitHub.
Por outro lado, há milhares de usuários nos sistemas de chat, então há uma grande chance de você encontrar alguém para conversar por lá quase o tempo todo. 😄
## Patrocine o autor { #sponsor-the-author }
Se o seu **produto/empresa** depende de ou está relacionado ao **FastAPI** e você quer alcançar suas pessoas usuárias, você pode patrocinar o autor (eu) através do <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>. Dependendo do nível, você pode obter benefícios extras, como um selo na documentação. 🎁
---
Obrigado! 🚀
+79
View File
@@ -0,0 +1,79 @@
# História, Design e Futuro { #history-design-and-future }
Há algum tempo, <a href="https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">um usuário **FastAPI** perguntou</a>:
> Qual é a história desse projeto? Parece que surgiu do nada e se tornou incrível em poucas semanas [...]
Aqui está um pouco dessa história.
## Alternativas { #alternatives }
Eu tenho criado APIs com requisitos complexos por vários anos (Aprendizado de Máquina, sistemas distribuídos, tarefas assíncronas, banco de dados NoSQL etc.), liderando vários times de desenvolvedores.
Como parte disso, eu precisava investigar, testar e usar muitas alternativas.
A história do **FastAPI** é, em grande parte, a história de seus predecessores.
Como dito na seção [Alternativas](alternatives.md){.internal-link target=_blank}:
<blockquote markdown="1">
**FastAPI** não existiria se não pelo trabalho anterior de outros.
Há muitas ferramentas criadas antes que ajudaram a inspirar sua criação.
Eu estive evitando a criação de um novo _framework_ por vários anos. Primeiro tentei resolver todas as funcionalidades cobertas por **FastAPI** usando muitos _frameworks_, _plug-ins_ e ferramentas diferentes.
Mas em algum ponto, não havia outra opção senão criar algo que oferecia todas as funcionalidades, aproveitando as melhores ideias de ferramentas anteriores, e combinando-as da melhor maneira possível, usando funcionalidades da linguagem que nem estavam disponíveis antes (_type hints_ do Python 3.6+).
</blockquote>
## Investigação { #investigation }
Ao usar todas as alternativas anteriores, eu tive a chance de aprender com todas elas, aproveitar ideias e combiná-las da melhor maneira que encontrei para mim e para os times de desenvolvedores com os quais trabalhava.
Por exemplo, estava claro que idealmente ele deveria ser baseado nos _type hints_ padrões do Python.
Também, a melhor abordagem era usar padrões já existentes.
Então, antes mesmo de começar a codificar o **FastAPI**, eu investi vários meses estudando as especificações do OpenAPI, JSON Schema, OAuth2 etc. Entendendo suas relações, sobreposições e diferenças.
## Design { #design }
Eu então dediquei algum tempo projetando a "API" de desenvolvimento que eu queria como usuário (como um desenvolvedor usando o FastAPI).
Eu testei várias ideias nos editores Python mais populares: PyCharm, VS Code, e editores baseados no Jedi.
Pela última <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">Pesquisa do Desenvolvedor Python</a>, isso cobre cerca de 80% dos usuários.
Isso significa que o **FastAPI** foi testado especificamente com os editores usados por 80% dos desenvolvedores Python. Como a maioria dos outros editores tendem a funcionar de forma similar, todos os seus benefícios devem funcionar para virtualmente todos os editores.
Dessa forma eu pude encontrar a melhor maneira de reduzir duplicação de código o máximo possível, ter preenchimento automático em todos os lugares, conferência de tipos e erros etc.
Tudo de uma forma que oferecesse a melhor experiência de desenvolvimento para todos os desenvolvedores.
## Requisitos { #requirements }
Após testar várias alternativas, eu decidi que usaria o <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">**Pydantic**</a> por suas vantagens.
Então eu contribuí com ele, para deixá-lo completamente de acordo com o JSON Schema, para dar suporte a diferentes maneiras de definir declarações de restrições, e melhorar o suporte a editores (conferências de tipos, preenchimento automático) baseado nos testes em vários editores.
Durante o desenvolvimento, eu também contribuí com o <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, outro requisito chave.
## Desenvolvimento { #development }
Quando comecei a criar o **FastAPI** de fato, a maior parte das peças já estavam encaixadas, o design estava definido, os requisitos e ferramentas já estavam prontos, e o conhecimento sobre os padrões e especificações estavam claros e frescos.
## Futuro { #future }
Nesse ponto, já está claro que o **FastAPI** com suas ideias está sendo útil para muitas pessoas.
Ele foi escolhido sobre outras alternativas anteriores por se adequar melhor em muitos casos.
Muitos desenvolvedores e times já dependem do **FastAPI** para seus projetos (incluindo eu e meu time).
Mas ainda há muitas melhorias e funcionalidades a vir.
**FastAPI** tem um grande futuro à frente.
E [sua ajuda](help-fastapi.md){.internal-link target=_blank} é muito bem-vinda.
@@ -0,0 +1,56 @@
# OpenAPI condicional { #conditional-openapi }
Se necessário, você pode usar configurações e variáveis de ambiente para configurar o OpenAPI condicionalmente dependendo do ambiente e até mesmo desativá-lo completamente.
## Sobre segurança, APIs e documentação { #about-security-apis-and-docs }
Ocultar suas interfaces de usuário de documentação na produção não *deveria* ser a maneira de proteger sua API.
Isso não adiciona nenhuma segurança extra à sua API; as *operações de rota* ainda estarão disponíveis onde estão.
Se houver uma falha de segurança no seu código, ela ainda existirá.
Ocultar a documentação apenas torna mais difícil entender como interagir com sua API e pode dificultar sua depuração na produção. Pode ser considerado simplesmente uma forma de <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">Segurança através da obscuridade</a>.
Se você quiser proteger sua API, há várias coisas melhores que você pode fazer, por exemplo:
* Certifique-se de ter modelos Pydantic bem definidos para seus corpos de solicitação e respostas.
* Configure quaisquer permissões e funções necessárias usando dependências.
* Nunca armazene senhas em texto simples, apenas hashes de senha.
* Implemente e use ferramentas criptográficas bem conhecidas, como pwdlib e tokens JWT, etc.
* Adicione controles de permissão mais granulares com escopos OAuth2 quando necessário.
* ...etc.
No entanto, você pode ter um caso de uso muito específico em que realmente precisa desabilitar a documentação da API para algum ambiente (por exemplo, para produção) ou dependendo de configurações de variáveis de ambiente.
## OpenAPI condicional com configurações e variáveis de ambiente { #conditional-openapi-from-settings-and-env-vars }
Você pode usar facilmente as mesmas configurações do Pydantic para configurar sua OpenAPI gerada e as interfaces de usuário da documentação.
Por exemplo:
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
Aqui declaramos a configuração `openapi_url` com o mesmo padrão de `"/openapi.json"`.
E então a usamos ao criar a aplicação `FastAPI`.
Então você pode desabilitar o OpenAPI (incluindo a documentação da interface do usuário) definindo a variável de ambiente `OPENAPI_URL` como uma string vazia, como:
<div class="termy">
```console
$ OPENAPI_URL= uvicorn main:app
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Então, se você acessar as URLs em `/openapi.json`, `/docs` ou `/redoc`, você receberá apenas um erro `404 Not Found` como:
```JSON
{
"detail": "Not Found"
}
```
@@ -0,0 +1,70 @@
# Configure a UI do Swagger { #configure-swagger-ui }
Você pode configurar alguns <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">parâmetros extras da UI do Swagger</a>.
Para configurá-los, passe o argumento `swagger_ui_parameters` ao criar o objeto da aplicação `FastAPI()` ou para a função `get_swagger_ui_html()`.
`swagger_ui_parameters` recebe um dicionário com as configurações passadas diretamente para o Swagger UI.
O FastAPI converte as configurações para **JSON** para torná-las compatíveis com JavaScript, pois é disso que o Swagger UI precisa.
## Desabilitar destaque de sintaxe { #disable-syntax-highlighting }
Por exemplo, você pode desabilitar o destaque de sintaxe na UI do Swagger.
Sem alterar as configurações, o destaque de sintaxe é habilitado por padrão:
<img src="/img/tutorial/extending-openapi/image02.png">
Mas você pode desabilitá-lo definindo `syntaxHighlight` como `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
...e então o Swagger UI não mostrará mais o destaque de sintaxe:
<img src="/img/tutorial/extending-openapi/image03.png">
## Alterar o tema { #change-the-theme }
Da mesma forma que você pode definir o tema de destaque de sintaxe com a chave `"syntaxHighlight.theme"` (observe que há um ponto no meio):
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
Essa configuração alteraria o tema de cores de destaque de sintaxe:
<img src="/img/tutorial/extending-openapi/image04.png">
## Alterar parâmetros de UI padrão do Swagger { #change-default-swagger-ui-parameters }
O FastAPI inclui alguns parâmetros de configuração padrão apropriados para a maioria dos casos de uso.
Inclui estas configurações padrão:
{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
Você pode substituir qualquer um deles definindo um valor diferente no argumento `swagger_ui_parameters`.
Por exemplo, para desabilitar `deepLinking` você pode passar essas configurações para `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
## Outros parâmetros da UI do Swagger { #other-swagger-ui-parameters }
Para ver todas as outras configurações possíveis que você pode usar, leia a <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">documentação oficial dos parâmetros da UI do Swagger</a>.
## Configurações somente JavaScript { #javascript-only-settings }
A UI do Swagger também permite que outras configurações sejam objetos **somente JavaScript** (por exemplo, funções JavaScript).
O FastAPI também inclui estas configurações `presets` somente para JavaScript:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Esses são objetos **JavaScript**, não strings, então você não pode passá-los diretamente do código Python.
Se você precisar usar configurações somente JavaScript como essas, você pode usar um dos métodos acima. Substitua toda a *operação de rota* do Swagger UI e escreva manualmente qualquer JavaScript que você precisar.
@@ -0,0 +1,185 @@
# Recursos Estáticos Personalizados para a UI de Documentação (Hospedagem Própria) { #custom-docs-ui-static-assets-self-hosting }
A documentação da API usa **Swagger UI** e **ReDoc**, e cada um deles precisa de alguns arquivos JavaScript e CSS.
Por padrão, esses arquivos são fornecidos por um <abbr title="Content Delivery Network Rede de Entrega de Conteúdo: Um serviço, normalmente composto por vários servidores, que fornece arquivos estáticos, como JavaScript e CSS. É comumente usado para providenciar esses arquivos do servidor mais próximo do cliente, melhorando o desempenho.">CDN</abbr>.
Mas é possível personalizá-los, você pode definir um CDN específico ou providenciar os arquivos você mesmo.
## CDN Personalizado para JavaScript e CSS { #custom-cdn-for-javascript-and-css }
Vamos supor que você deseja usar um <abbr title="Content Delivery Network Rede de Entrega de Conteúdo">CDN</abbr> diferente, por exemplo, você deseja usar `https://unpkg.com/`.
Isso pode ser útil se, por exemplo, você mora em um país que restringe algumas URLs.
### Desativar a documentação automática { #disable-the-automatic-docs }
O primeiro passo é desativar a documentação automática, pois por padrão, ela usa o CDN padrão.
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
### Incluir a documentação personalizada { #include-the-custom-docs }
Agora você pode criar as *operações de rota* para a documentação personalizada.
Você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
* `title`: o título da sua API.
* `oauth2_redirect_url`: você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. Este é o URL do CDN personalizado.
* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. Este é o URL do CDN personalizado.
E de forma semelhante para o ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Dica
A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E interagir com ela usando a autenticação OAuth2 real.
Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirecionamento".
///
### Criar uma *operação de rota* para testar { #create-a-path-operation-to-test-it }
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
### Teste { #test-it }
Agora, você deve ser capaz de ir para a documentação em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, e recarregar a página, ela carregará esses recursos do novo CDN.
## Hospedagem Própria de JavaScript e CSS para a documentação { #self-hosting-javascript-and-css-for-docs }
Hospedar o JavaScript e o CSS pode ser útil se, por exemplo, você precisa que seu aplicativo continue funcionando mesmo offline, sem acesso aberto à Internet, ou em uma rede local.
Aqui você verá como providenciar esses arquivos você mesmo, na mesma aplicação FastAPI, e configurar a documentação para usá-los.
### Estrutura de Arquivos do Projeto { #project-file-structure }
Vamos supor que a estrutura de arquivos do seu projeto se pareça com isso:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Agora crie um diretório para armazenar esses arquivos estáticos.
Sua nova estrutura de arquivos poderia se parecer com isso:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/
```
### Baixe os arquivos { #download-the-files }
Baixe os arquivos estáticos necessários para a documentação e coloque-os no diretório `static/`.
Você provavelmente pode clicar com o botão direito em cada link e selecionar uma opção semelhante a `Salvar link como...`.
**Swagger UI** usa os arquivos:
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
E o **ReDoc** usa o arquivo:
* <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
Depois disso, sua estrutura de arquivos deve se parecer com:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Prover os arquivos estáticos { #serve-the-static-files }
* Importe `StaticFiles`.
* "Monte" a instância `StaticFiles()` em um caminho específico.
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
### Teste os arquivos estáticos { #test-the-static-files }
Inicialize seu aplicativo e vá para <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
Você deverá ver um arquivo JavaScript muito longo para o **ReDoc**.
Esse arquivo pode começar com algo como:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Isso confirma que você está conseguindo fornecer arquivos estáticos do seu aplicativo e que você colocou os arquivos estáticos para a documentação no local correto.
Agora, podemos configurar o aplicativo para usar esses arquivos estáticos para a documentação.
### Desativar a documentação automática para arquivos estáticos { #disable-the-automatic-docs-for-static-files }
Da mesma forma que ao usar um CDN personalizado, o primeiro passo é desativar a documentação automática, pois ela usa o CDN padrão.
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
### Incluir a documentação personalizada para arquivos estáticos { #include-the-custom-docs-for-static-files }
E da mesma forma que com um CDN personalizado, agora você pode criar as *operações de rota* para a documentação personalizada.
Novamente, você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
* `title`: o título da sua API.
* `oauth2_redirect_url`: Você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. **Este é o URL que seu aplicativo está fornecendo**.
* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. **Esse é o que seu aplicativo está fornecendo**.
E de forma semelhante para o ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Dica
A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E, então, interagir com ela usando a autenticação OAuth2 real.
Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirect".
///
### Criar uma *operação de rota* para testar arquivos estáticos { #create-a-path-operation-to-test-static-files }
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
### Teste a UI de Arquivos Estáticos { #test-static-files-ui }
Agora, você deve ser capaz de desconectar o WiFi, ir para a documentação em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, e recarregar a página.
E mesmo sem Internet, você será capaz de ver a documentação da sua API e interagir com ela.
@@ -0,0 +1,109 @@
# Request e classe APIRoute personalizadas { #custom-request-and-apiroute-class }
Em alguns casos, você pode querer sobrescrever a lógica usada pelas classes `Request` e `APIRoute`.
Em particular, isso pode ser uma boa alternativa para uma lógica em um middleware.
Por exemplo, se você quiser ler ou manipular o corpo da requisição antes que ele seja processado pela sua aplicação.
/// danger | Cuidado
Isso é um recurso "avançado".
Se você for um iniciante em **FastAPI** você deve considerar pular essa seção.
///
## Casos de Uso { #use-cases }
Alguns casos de uso incluem:
* Converter requisições não-JSON para JSON (por exemplo, <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
* Descomprimir corpos de requisição comprimidos com gzip.
* Registrar automaticamente todos os corpos de requisição.
## Manipulando codificações de corpo de requisição personalizadas { #handling-custom-request-body-encodings }
Vamos ver como usar uma subclasse personalizada de `Request` para descomprimir requisições gzip.
E uma subclasse de `APIRoute` para usar essa classe de requisição personalizada.
### Criar uma classe `GzipRequest` personalizada { #create-a-custom-gziprequest-class }
/// tip | Dica
Isso é um exemplo de brincadeira para demonstrar como funciona, se você precisar de suporte para Gzip, você pode usar o [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank} fornecido.
///
Primeiro, criamos uma classe `GzipRequest`, que irá sobrescrever o método `Request.body()` para descomprimir o corpo na presença de um cabeçalho apropriado.
Se não houver `gzip` no cabeçalho, ele não tentará descomprimir o corpo.
Dessa forma, a mesma classe de rota pode lidar com requisições comprimidas ou não comprimidas.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
### Criar uma classe `GzipRoute` personalizada { #create-a-custom-gziproute-class }
Em seguida, criamos uma subclasse personalizada de `fastapi.routing.APIRoute` que fará uso do `GzipRequest`.
Dessa vez, ele irá sobrescrever o método `APIRoute.get_route_handler()`.
Esse método retorna uma função. E essa função é o que irá receber uma requisição e retornar uma resposta.
Aqui nós usamos para criar um `GzipRequest` a partir da requisição original.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
/// note | Detalhes Técnicos
Um `Request` tem um atributo `request.scope`, que é apenas um `dict` do Python contendo os metadados relacionados à requisição.
Um `Request` também tem um `request.receive`, que é uma função para "receber" o corpo da requisição.
O dicionário `scope` e a função `receive` são ambos parte da especificação ASGI.
E essas duas coisas, `scope` e `receive`, são o que é necessário para criar uma nova instância de `Request`.
Para aprender mais sobre o `Request` confira a <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">documentação do Starlette sobre Requests</a>.
///
A única coisa que a função retornada por `GzipRequest.get_route_handler` faz de diferente é converter o `Request` para um `GzipRequest`.
Fazendo isso, nosso `GzipRequest` irá cuidar de descomprimir os dados (se necessário) antes de passá-los para nossas *operações de rota*.
Depois disso, toda a lógica de processamento é a mesma.
Mas por causa das nossas mudanças em `GzipRequest.body`, o corpo da requisição será automaticamente descomprimido quando for carregado pelo **FastAPI** quando necessário.
## Acessando o corpo da requisição em um manipulador de exceção { #accessing-the-request-body-in-an-exception-handler }
/// tip | Dica
Para resolver esse mesmo problema, é provavelmente muito mais fácil usar o `body` em um manipulador personalizado para `RequestValidationError` ([Tratando Erros](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
Mas esse exemplo ainda é valido e mostra como interagir com os componentes internos.
///
Também podemos usar essa mesma abordagem para acessar o corpo da requisição em um manipulador de exceção.
Tudo que precisamos fazer é manipular a requisição dentro de um bloco `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
Se uma exceção ocorrer, a instância `Request` ainda estará em escopo, então podemos ler e fazer uso do corpo da requisição ao lidar com o erro:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
## Classe `APIRoute` personalizada em um router { #custom-apiroute-class-in-a-router }
Você também pode definir o parâmetro `route_class` de uma `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
Nesse exemplo, as *operações de rota* sob o `router` irão usar a classe `TimedRoute` personalizada, e terão um cabeçalho extra `X-Response-Time` na resposta com o tempo que levou para gerar a resposta:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
+80
View File
@@ -0,0 +1,80 @@
# Extendendo o OpenAPI { #extending-openapi }
Existem alguns casos em que pode ser necessário modificar o esquema OpenAPI gerado.
Nesta seção, você verá como fazer isso.
## O processo normal { #the-normal-process }
O processo normal (padrão) é o seguinte:
Uma aplicação (instância) do `FastAPI` possui um método `.openapi()` que deve retornar o esquema OpenAPI.
Como parte da criação do objeto de aplicação, uma *operação de rota* para `/openapi.json` (ou para o que você definir como `openapi_url`) é registrada.
Ela apenas retorna uma resposta JSON com o resultado do método `.openapi()` da aplicação.
Por padrão, o que o método `.openapi()` faz é verificar se a propriedade `.openapi_schema` tem conteúdo e retorná-lo.
Se não tiver, ele gera o conteúdo usando a função utilitária em `fastapi.openapi.utils.get_openapi`.
E essa função `get_openapi()` recebe como parâmetros:
* `title`: O título do OpenAPI, exibido na documentação.
* `version`: A versão da sua API, por exemplo, `2.5.0`.
* `openapi_version`: A versão da especificação OpenAPI utilizada. Por padrão, a mais recente: `3.1.0`.
* `summary`: Um resumo curto da API.
* `description`: A descrição da sua API, que pode incluir markdown e será exibida na documentação.
* `routes`: Uma lista de rotas, que são cada uma das *operações de rota* registradas. Elas são obtidas de `app.routes`.
/// info | Informação
O parâmetro `summary` está disponível no OpenAPI 3.1.0 e superior, suportado pelo FastAPI 0.99.0 e superior.
///
## Sobrescrevendo os padrões { #overriding-the-defaults }
Com as informações acima, você pode usar a mesma função utilitária para gerar o esquema OpenAPI e sobrescrever cada parte que precisar.
Por exemplo, vamos adicionar <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">Extensão OpenAPI do ReDoc para incluir um logo personalizado</a>.
### **FastAPI** Normal { #normal-fastapi }
Primeiro, escreva toda a sua aplicação **FastAPI** normalmente:
{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
### Gerar o esquema OpenAPI { #generate-the-openapi-schema }
Em seguida, use a mesma função utilitária para gerar o esquema OpenAPI, dentro de uma função `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
### Modificar o esquema OpenAPI { #modify-the-openapi-schema }
Agora, você pode adicionar a extensão do ReDoc, incluindo um `x-logo` personalizado ao "objeto" `info` no esquema OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
### Armazenar em cache o esquema OpenAPI { #cache-the-openapi-schema }
Você pode usar a propriedade `.openapi_schema` como um "cache" para armazenar o esquema gerado.
Dessa forma, sua aplicação não precisará gerar o esquema toda vez que um usuário abrir a documentação da sua API.
Ele será gerado apenas uma vez, e o mesmo esquema armazenado em cache será utilizado nas próximas requisições.
{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
### Sobrescrever o método { #override-the-method }
Agora, você pode substituir o método `.openapi()` pela sua nova função.
{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
### Verificar { #check-it }
Uma vez que você acessar <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>, verá que está usando seu logo personalizado (neste exemplo, o logo do **FastAPI**):
<img src="/img/tutorial/extending-openapi/image01.png">
+38
View File
@@ -0,0 +1,38 @@
# Geral - Como Fazer - Receitas { #general-how-to-recipes }
Aqui estão vários links para outros locais na documentação, para perguntas gerais ou frequentes
## Filtro de dados- Segurança { #filter-data-security }
Para assegurar que você não vai retornar mais dados do que deveria, leia a seção [Tutorial - Modelo de Resposta - Tipo de Retorno](../tutorial/response-model.md){.internal-link target=_blank}.
## Tags de Documentação - OpenAPI { #documentation-tags-openapi }
Para adicionar tags às suas *operações de rota* e agrupá-las na UI da documentação, leia a seção [Tutorial - Configurações da Operação de Rota - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
## Resumo e Descrição da documentação - OpenAPI { #documentation-summary-and-description-openapi }
Para adicionar um resumo e uma descrição às suas *operações de rota* e exibi-los na UI da documentação, leia a seção [Tutorial - Configurações da Operação de Rota - Resumo e Descrição](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
## Documentação das Descrições de Resposta - OpenAPI { #documentation-response-description-openapi }
Para definir a descrição de uma resposta exibida na interface da documentação, leia a seção [Tutorial - Configurações da Operação de Rota - Descrição da Resposta](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
## Documentação para Depreciar uma *Operação de Rota* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Para depreciar uma *operação de rota* e exibi-la na interface da documentação, leia a seção [Tutorial - Configurações da Operação de Rota - Depreciação](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
## Converter qualquer dado para compatível com JSON { #convert-any-data-to-json-compatible }
Para converter qualquer dado para um formato compatível com JSON, leia a seção [Tutorial - Codificador Compatível com JSON](../tutorial/encoder.md){.internal-link target=_blank}.
## OpenAPI Metadata - Docs { #openapi-metadata-docs }
Para adicionar metadados ao seu esquema OpenAPI, incluindo licensa, versão, contato, etc, leia a seção [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md){.internal-link target=_blank}.
## OpenAPI com URL customizada { #openapi-custom-url }
Para customizar a URL do OpenAPI (ou removê-la), leia a seção [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
## URLs de documentação do OpenAPI { #openapi-docs-urls }
Para alterar as URLs usadas para as interfaces de usuário da documentação gerada automaticamente, leia a seção [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.
+60
View File
@@ -0,0 +1,60 @@
# GraphQL { #graphql }
Como o **FastAPI** é baseado no padrão **ASGI**, é muito fácil integrar qualquer biblioteca **GraphQL** também compatível com ASGI.
Você pode combinar *operações de rota* normais do FastAPI com GraphQL na mesma aplicação.
/// tip | Dica
**GraphQL** resolve alguns casos de uso muito específicos.
Ele tem **vantagens** e **desvantagens** quando comparado a **web APIs** comuns.
Certifique-se de avaliar se os **benefícios** para o seu caso de uso compensam as **desvantagens**. 🤓
///
## Bibliotecas GraphQL { #graphql-libraries }
Aqui estão algumas das bibliotecas **GraphQL** que têm suporte **ASGI**. Você pode usá-las com **FastAPI**:
* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> 🍓
* Com <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">docs para FastAPI</a>
* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
* Com <a href="https://ariadnegraphql.org/docs/fastapi-integration" class="external-link" target="_blank">docs para FastAPI</a>
* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
* Com <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a> para fornecer integração ASGI
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
* Com <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>
## GraphQL com Strawberry { #graphql-with-strawberry }
Se você precisar ou quiser trabalhar com **GraphQL**, <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> é a biblioteca **recomendada** pois tem o design mais próximo ao design do **FastAPI**, ela é toda baseada em **anotações de tipo**.
Dependendo do seu caso de uso, você pode preferir usar uma biblioteca diferente, mas se você me perguntasse, eu provavelmente sugeriria que você experimentasse o **Strawberry**.
Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
Você pode aprender mais sobre Strawberry na <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentação do Strawberry</a>.
E também na documentação sobre <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">Strawberry com FastAPI</a>.
## Antigo `GraphQLApp` do Starlette { #older-graphqlapp-from-starlette }
Versões anteriores do Starlette incluiam uma classe `GraphQLApp` para integrar com <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>.
Ela foi descontinuada do Starlette, mas se você tem código que a utilizava, você pode facilmente **migrar** para <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>, que cobre o mesmo caso de uso e tem uma **interface quase idêntica**.
/// tip | Dica
Se você precisa de GraphQL, eu ainda recomendaria que você desse uma olhada no <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>, pois ele é baseado em anotações de tipo em vez de classes e tipos personalizados.
///
## Saiba Mais { #learn-more }
Você pode aprender mais sobre **GraphQL** na <a href="https://graphql.org/" class="external-link" target="_blank">documentação oficial do GraphQL</a>.
Você também pode ler mais sobre cada uma das bibliotecas descritas acima em seus links.
+13
View File
@@ -0,0 +1,13 @@
# Como Fazer - Receitas { #how-to-recipes }
Aqui você encontrará diferentes exemplos práticos ou tutoriais de "como fazer" para **vários tópicos**.
A maioria dessas ideias será mais ou menos **independente**, e na maioria dos casos você só precisará estudá-las se elas se aplicarem diretamente ao **seu projeto**.
Se algo parecer interessante e útil para o seu projeto, vá em frente e dê uma olhada. Caso contrário, você pode simplesmente ignorá-lo.
/// tip | Dica
Se você deseja **aprender FastAPI** de forma estruturada (recomendado), leia capítulo por capítulo [Tutorial - Guia de Usuário](../tutorial/index.md){.internal-link target=_blank} em vez disso.
///
@@ -0,0 +1,133 @@
# Migrar do Pydantic v1 para o Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Se você tem uma aplicação FastAPI antiga, pode estar usando o Pydantic versão 1.
O FastAPI tem suporte ao Pydantic v1 ou v2 desde a versão 0.100.0.
Se você tiver o Pydantic v2 instalado, ele será utilizado. Se, em vez disso, tiver o Pydantic v1, será ele que será utilizado.
O Pydantic v1 está agora descontinuado e o suporte a ele será removido nas próximas versões do FastAPI, você deveria migrar para o Pydantic v2. Assim, você terá as funcionalidades, melhorias e correções mais recentes.
/// warning | Atenção
Além disso, a equipe do Pydantic interrompeu o suporte ao Pydantic v1 para as versões mais recentes do Python, a partir do **Python 3.14**.
Se quiser usar as funcionalidades mais recentes do Python, você precisará garantir que usa o Pydantic v2.
///
Se você tem uma aplicação FastAPI antiga com Pydantic v1, aqui vou mostrar como migrá-la para o Pydantic v2 e as **novas funcionalidades no FastAPI 0.119.0** para ajudar em uma migração gradual.
## Guia oficial { #official-guide }
O Pydantic tem um <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Guia de Migração</a> oficial do v1 para o v2.
Ele também inclui o que mudou, como as validações agora são mais corretas e rigorosas, possíveis ressalvas, etc.
Você pode lê-lo para entender melhor o que mudou.
## Testes { #tests }
Garanta que você tenha [testes](../tutorial/testing.md){.internal-link target=_blank} para sua aplicação e que os execute na integração contínua (CI).
Assim, você pode fazer a atualização e garantir que tudo continua funcionando como esperado.
## `bump-pydantic` { #bump-pydantic }
Em muitos casos, quando você usa modelos Pydantic regulares sem personalizações, será possível automatizar a maior parte do processo de migração do Pydantic v1 para o Pydantic v2.
Você pode usar o <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> da própria equipe do Pydantic.
Essa ferramenta ajuda a alterar automaticamente a maior parte do código que precisa ser modificado.
Depois disso, você pode rodar os testes e verificar se tudo funciona. Se funcionar, está concluído. 😎
## Pydantic v1 no v2 { #pydantic-v1-in-v2 }
O Pydantic v2 inclui tudo do Pydantic v1 como um submódulo `pydantic.v1`.
Isso significa que você pode instalar a versão mais recente do Pydantic v2 e importar e usar os componentes antigos do Pydantic v1 a partir desse submódulo, como se tivesse o Pydantic v1 antigo instalado.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Suporte do FastAPI ao Pydantic v1 no v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Desde o FastAPI 0.119.0, há também suporte parcial ao Pydantic v1 a partir de dentro do Pydantic v2, para facilitar a migração para o v2.
Assim, você pode atualizar o Pydantic para a versão 2 mais recente e alterar os imports para usar o submódulo `pydantic.v1`, e em muitos casos tudo simplesmente funcionará.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Atenção
Tenha em mente que, como a equipe do Pydantic não oferece mais suporte ao Pydantic v1 nas versões recentes do Python, a partir do Python 3.14, o uso de `pydantic.v1` também não é suportado no Python 3.14 e superiores.
///
### Pydantic v1 e v2 na mesma aplicação { #pydantic-v1-and-v2-on-the-same-app }
Não é suportado pelo Pydantic ter um modelo do Pydantic v2 com campos próprios definidos como modelos do Pydantic v1, ou vice-versa.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...but, you can have separated models using Pydantic v1 and v2 in the same app.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
Em alguns casos, é até possível ter modelos Pydantic v1 e v2 na mesma operação de rota na sua aplicação FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
No exemplo acima, o modelo de entrada é um modelo Pydantic v1, e o modelo de saída (definido em `response_model=ItemV2`) é um modelo Pydantic v2.
### Parâmetros do Pydantic v1 { #pydantic-v1-parameters }
Se você precisar usar algumas das ferramentas específicas do FastAPI para parâmetros como `Body`, `Query`, `Form` etc. com modelos do Pydantic v1, pode importá-las de `fastapi.temp_pydantic_v1_params` enquanto conclui a migração para o Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migre em etapas { #migrate-in-steps }
/// tip | Dica
Primeiro tente com o `bump-pydantic`; se seus testes passarem e isso funcionar, então você concluiu tudo com um único comando. ✨
///
Se o `bump-pydantic` não funcionar para o seu caso, você pode usar o suporte a modelos Pydantic v1 e v2 na mesma aplicação para fazer a migração para o Pydantic v2 gradualmente.
Você poderia primeiro atualizar o Pydantic para usar a versão 2 mais recente e alterar os imports para usar `pydantic.v1` para todos os seus modelos.
Depois, você pode começar a migrar seus modelos do Pydantic v1 para o v2 em grupos, em etapas graduais. 🚶
@@ -0,0 +1,104 @@
# Esquemas OpenAPI Separados para Entrada e Saída ou Não { #separate-openapi-schemas-for-input-and-output-or-not }
Ao usar **Pydantic v2**, o OpenAPI gerado é um pouco mais exato e **correto** do que antes. 😎
Inclusive, em alguns casos, ele terá até **dois JSON Schemas** no OpenAPI para o mesmo modelo Pydantic, para entrada e saída, dependendo se eles possuem **valores padrão**.
Vamos ver como isso funciona e como alterar se for necessário.
## Modelos Pydantic para Entrada e Saída { #pydantic-models-for-input-and-output }
Digamos que você tenha um modelo Pydantic com valores padrão, como este:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modelo para Entrada { #model-for-input }
Se você usar esse modelo como entrada, como aqui:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
... então o campo `description` **não será obrigatório**. Porque ele tem um valor padrão de `None`.
### Modelo de Entrada na Documentação { #input-model-in-docs }
Você pode confirmar que na documentação, o campo `description` não tem um **asterisco vermelho**, não é marcado como obrigatório:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
</div>
### Modelo para Saída { #model-for-output }
Mas se você usar o mesmo modelo como saída, como aqui:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
... então, como `description` tem um valor padrão, se você **não retornar nada** para esse campo, ele ainda terá o **valor padrão**.
### Modelo para Dados de Resposta de Saída { #model-for-output-response-data }
Se você interagir com a documentação e verificar a resposta, mesmo que o código não tenha adicionado nada em um dos campos `description`, a resposta JSON contém o valor padrão (`null`):
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
</div>
Isso significa que ele **sempre terá um valor**, só que às vezes o valor pode ser `None` (ou `null` em termos de JSON).
Isso quer dizer que, os clientes que usam sua API não precisam verificar se o valor existe ou não, eles podem **assumir que o campo sempre estará lá**, mas que em alguns casos terá o valor padrão de `None`.
A maneira de descrever isso no OpenAPI é marcar esse campo como **obrigatório**, porque ele sempre estará lá.
Por causa disso, o JSON Schema para um modelo pode ser diferente dependendo se ele é usado para **entrada ou saída**:
* para **entrada**, o `description` **não será obrigatório**
* para **saída**, ele será **obrigatório** (e possivelmente `None`, ou em termos de JSON, `null`)
### Modelo para Saída na Documentação { #model-for-output-in-docs }
Você pode verificar o modelo de saída na documentação também, **ambos** `name` e `description` são marcados como **obrigatórios** com um **asterisco vermelho**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
</div>
### Modelo para Entrada e Saída na Documentação { #model-for-input-and-output-in-docs }
E se você verificar todos os Schemas disponíveis (JSON Schemas) no OpenAPI, verá que há dois, um `Item-Input` e um `Item-Output`.
Para `Item-Input`, `description` **não é obrigatório**, não tem um asterisco vermelho.
Mas para `Item-Output`, `description` **é obrigatório**, tem um asterisco vermelho.
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image04.png">
</div>
Com esse recurso do **Pydantic v2**, sua documentação da API fica mais **precisa**, e se você tiver clientes e SDKs gerados automaticamente, eles serão mais precisos também, proporcionando uma melhor **experiência para desenvolvedores** e consistência. 🎉
## Não Separe Schemas { #do-not-separate-schemas }
Agora, há alguns casos em que você pode querer ter o **mesmo esquema para entrada e saída**.
Provavelmente, o principal caso de uso para isso é se você já tem algum código de cliente/SDK gerado automaticamente e não quer atualizar todo o código de cliente/SDK gerado ainda, você provavelmente vai querer fazer isso em algum momento, mas talvez não agora.
Nesse caso, você pode desativar esse recurso no **FastAPI**, com o parâmetro `separate_input_output_schemas=False`.
/// info | Informação
O suporte para `separate_input_output_schemas` foi adicionado no FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Mesmo Esquema para Modelos de Entrada e Saída na Documentação { #same-schema-for-input-and-output-models-in-docs }
E agora haverá um único esquema para entrada e saída para o modelo, apenas `Item`, e `description` **não será obrigatório**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
</div>
Esse é o mesmo comportamento do Pydantic v1. 🤓
+7
View File
@@ -0,0 +1,7 @@
# Testando a Base de Dados { #testing-a-database }
Você pode estudar sobre bases de dados, SQL e SQLModel na <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">documentação de SQLModel</a>. 🤓
Aqui tem um mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutorial de como usar SQLModel com FastAPI</a>. ✨
Esse tutorial inclui uma seção sobre <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">testar bases de dados SQL</a>. 😎
+501
View File
@@ -0,0 +1,501 @@
# FastAPI { #fastapi }
<style>
.md-content .md-typeset h1 { display: none; }
</style>
<p align="center">
<a href="https://fastapi.tiangolo.com/pt"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>Framework FastAPI, alta performance, fácil de aprender, fácil de codar, pronto para produção</em>
</p>
<p align="center">
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>
---
**Documentação**: <a href="https://fastapi.tiangolo.com/pt" target="_blank">https://fastapi.tiangolo.com</a>
**Código fonte**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
---
FastAPI é um moderno e rápido (alta performance) _framework web_ para construção de APIs com Python, baseado nos _type hints_ padrões do Python.
Os recursos chave são:
* **Rápido**: alta performance, equivalente a **NodeJS** e **Go** (graças ao Starlette e Pydantic). [Um dos frameworks mais rápidos disponíveis](#performance).
* **Rápido para codar**: Aumenta a velocidade para desenvolver recursos entre 200% a 300%. *
* **Poucos bugs**: Reduz cerca de 40% de erros induzidos por humanos (desenvolvedores). *
* **Intuitivo**: Grande suporte a _IDEs_. <abbr title="também conhecido como autocompletar, preenchimento automático, IntelliSense">Preenchimento automático</abbr> em todos os lugares. Menos tempo debugando.
* **Fácil**: Projetado para ser fácil de aprender e usar. Menos tempo lendo documentação.
* **Enxuto**: Minimize duplicação de código. Múltiplas funcionalidades para cada declaração de parâmetro. Menos bugs.
* **Robusto**: Tenha código pronto para produção. E com documentação interativa automática.
* **Baseado em padrões**: Baseado em (e totalmente compatível com) os padrões abertos para APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (anteriormente conhecido como Swagger) e <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimativas baseadas em testes realizados com equipe interna de desenvolvimento, construindo aplicações em produção.</small>
## Patrocinadores { #sponsors }
<!-- sponsors -->
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/pt/fastapi-people/#sponsors" class="external-link" target="_blank">Outros patrocinadores</a>
## Opiniões { #opinions }
"*[...] Estou usando **FastAPI** muito esses dias. [...] Estou na verdade planejando utilizar ele em todos os times de **serviços _Machine Learning_ na Microsoft**. Alguns deles estão sendo integrados no _core_ do produto **Windows** e alguns produtos **Office**.*"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_Nós adotamos a biblioteca **FastAPI** para iniciar um servidor **REST** que pode ser consultado para obter **previsões**. [para o Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, e Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_A **Netflix** tem o prazer de anunciar o lançamento open-source do nosso framework de orquestração de **gerenciamento de crises**: **Dispatch**! [criado com **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"*Estou extremamente entusiasmado com o **FastAPI**. É tão divertido!*"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcaster</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestamente, o que você construiu parece super sólido e rebuscado. De muitas formas, eu queria que o **Hug** fosse assim - é realmente inspirador ver alguém que construiu ele.*"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong>criador do<a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"*Se você está procurando aprender um **_framework_ moderno** para construir aplicações _REST_, dê uma olhada no **FastAPI** [...] É rápido, fácil de usar e fácil de aprender [...]*"
"*Nós trocamos nossas **APIs** por **FastAPI** [...] Acredito que vocês gostarão dele [...]*"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong>fundadores da <a href="https://explosion.ai" target="_blank">Explosion AI</a> - criadores da <a href="https://spacy.io" target="_blank">spaCy</a></strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"_Se alguém estiver procurando construir uma API Python para produção, eu recomendaria fortemente o **FastAPI**. Ele é **lindamente projetado**, **simples de usar** e **altamente escalável**. Ele se tornou um **componente chave** para a nossa estratégia API first de desenvolvimento e está impulsionando diversas automações e serviços, como o nosso Virtual TAC Engineer._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, o FastAPI das interfaces de linhas de comando { #typer-the-fastapi-of-clis }
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
Se você estiver construindo uma aplicação <abbr title="Command Line Interface Interface de Linha de Comando">CLI</abbr> para ser utilizada em um terminal ao invés de uma aplicação web, dê uma olhada no <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** é o irmão menor do FastAPI. E seu propósito é ser o **FastAPI das _CLIs_**. ⌨️ 🚀
## Requisitos { #requirements }
FastAPI está nos ombros de gigantes:
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> para as partes web.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> para a parte de dados.
## Instalação { #installation }
Crie e ative um <a href="https://fastapi.tiangolo.com/pt/virtual-environments/" class="external-link" target="_blank">ambiente virtual</a> e então instale o FastAPI:
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
</div>
**Nota**: Certifique-se de que você colocou `"fastapi[standard]"` com aspas, para garantir que funcione em todos os terminais.
## Exemplo { #example }
### Crie { #create-it }
Crie um arquivo `main.py` com:
```Python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Ou use <code>async def</code>...</summary>
Se seu código utiliza `async` / `await`, use `async def`:
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
**Nota**:
Se você não sabe, verifique a seção _"Com pressa?"_ sobre <a href="https://fastapi.tiangolo.com/pt/async/#in-a-hurry" target="_blank">`async` e `await` nas docs</a>.
</details>
### Rode { #run-it }
Rode o servidor com:
<div class="termy">
```console
$ fastapi dev main.py
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
</div>
<details markdown="1">
<summary>Sobre o comando <code>fastapi dev main.py</code>...</summary>
O comando `fastapi dev` lê o seu arquivo `main.py`, identifica o aplicativo **FastAPI** nele, e inicia um servidor usando o <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>.
Por padrão, o `fastapi dev` iniciará com *auto-reload* habilitado para desenvolvimento local.
Você pode ler mais sobre isso na <a href="https://fastapi.tiangolo.com/pt/fastapi-cli/" target="_blank">documentação do FastAPI CLI</a>.
</details>
### Verifique { #check-it }
Abra seu navegador em <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
Você verá a resposta JSON como:
```JSON
{"item_id": 5, "q": "somequery"}
```
Você acabou de criar uma API que:
* Recebe requisições HTTP nas _rotas_ `/` e `/items/{item_id}`.
* Ambas _rotas_ fazem <em>operações</em> `GET` (também conhecido como _métodos_ HTTP).
* A _rota_ `/items/{item_id}` tem um _parâmetro de rota_ `item_id` que deve ser um `int`.
* A _rota_ `/items/{item_id}` tem um _parâmetro query_ `q` `str` opcional.
### Documentação Interativa da API { #interactive-api-docs }
Agora vá para <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá a documentação automática interativa da API (fornecida por <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Documentação Alternativa da API { #alternative-api-docs }
E agora, vá para <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Você verá a documentação automática alternativa (fornecida por <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Evoluindo o Exemplo { #example-upgrade }
Agora modifique o arquivo `main.py` para receber um corpo para uma requisição `PUT`.
Declare o corpo utilizando tipos padrão Python, graças ao Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
O servidor `fastapi dev` deverá recarregar automaticamente.
### Evoluindo a Documentação Interativa da API { #interactive-api-docs-upgrade }
Agora vá para <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* A documentação interativa da API será automaticamente atualizada, incluindo o novo corpo:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Clique no botão "Try it out", ele permitirá que você preencha os parâmetros e interaja diretamente com a API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Então clique no botão "Execute", a interface do usuário irá se comunicar com a API, enviar os parâmetros, pegar os resultados e mostrá-los na tela:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Evoluindo a Documentação Alternativa da API { #alternative-api-docs-upgrade }
E agora, vá para <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* A documentação alternativa também irá refletir o novo parâmetro da _query_ e o corpo:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recapitulando { #recap }
Resumindo, você declara **uma vez** os tipos dos parâmetros, corpo etc. como parâmetros de função.
Você faz isso com os tipos padrão do Python moderno.
Você não terá que aprender uma nova sintaxe, métodos ou classes de uma biblioteca específica etc.
Apenas **Python** padrão.
Por exemplo, para um `int`:
```Python
item_id: int
```
ou para um modelo mais complexo, `Item`:
```Python
item: Item
```
...e com essa única declaração você tem:
* Suporte ao Editor, incluindo:
* Completação.
* Verificação de tipos.
* Validação de dados:
* Erros automáticos e claros quando o dado é inválido.
* Validação até para objetos JSON profundamente aninhados.
* <abbr title="também conhecido como: serialização, parsing, marshalling">Conversão</abbr> de dados de entrada: vindo da rede para dados e tipos Python. Consegue ler:
* JSON.
* Parâmetros de rota.
* Parâmetros de _query_ .
* _Cookies_.
* Cabeçalhos.
* Formulários.
* Arquivos.
* <abbr title="também conhecido como: serialização, parsing, marshalling">Conversão</abbr> de dados de saída de tipos e dados Python para dados de rede (como JSON):
* Converte tipos Python (`str`, `int`, `float`, `bool`, `list` etc).
* Objetos `datetime`.
* Objetos `UUID`.
* Modelos de Banco de Dados.
* ...e muito mais.
* Documentação interativa automática da API, incluindo 2 alternativas de interface de usuário:
* Swagger UI.
* ReDoc.
---
Voltando ao código do exemplo anterior, **FastAPI** irá:
* Validar que existe um `item_id` na rota para requisições `GET` e `PUT`.
* Validar que `item_id` é do tipo `int` para requisições `GET` e `PUT`.
* Se não é validado, o cliente verá um útil, claro erro.
* Verificar se existe um parâmetro de _query_ opcional nomeado como `q` (como em `http://127.0.0.1:8000/items/foo?q=somequery`) para requisições `GET`.
* Como o parâmetro `q` é declarado com `= None`, ele é opcional.
* Sem o `None` ele poderia ser obrigatório (como o corpo no caso de `PUT`).
* Para requisições `PUT` para `/items/{item_id}`, lerá o corpo como JSON:
* Verifica que tem um atributo obrigatório `name` que deve ser `str`.
* Verifica que tem um atributo obrigatório `price` que deve ser `float`.
* Verifica que tem an atributo opcional `is_offer`, que deve ser `bool`, se presente.
* Tudo isso também funciona para objetos JSON profundamente aninhados.
* Converter de e para JSON automaticamente.
* Documentar tudo com OpenAPI, que poderá ser usado por:
* Sistemas de documentação interativos.
* Sistemas de clientes de geração de código automáticos, para muitas linguagens.
* Fornecer diretamente 2 interfaces _web_ de documentação interativa.
---
Nós apenas arranhamos a superfície, mas você já tem idéia de como tudo funciona.
Experimente mudar a seguinte linha:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...de:
```Python
... "item_name": item.name ...
```
...para:
```Python
... "item_price": item.price ...
```
...e veja como seu editor irá auto-completar os atributos e saberá os tipos:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fastapi.tiangolo.com/pt/tutorial/">Tutorial - Guia do Usuário</a>.
**Alerta de Spoiler**: o tutorial - guia do usuário inclui:
* Declaração de **parâmetetros** de diferentes lugares como: **cabeçalhos**, **cookies**, **campos de formulários** e **arquivos**.
* Como configurar **Limitações de Validação** como `maximum_length` ou `regex`.
* Um poderoso e fácil de usar sistema de **<abbr title="também conhecido como componentes, recursos, fornecedores, serviços, injetáveis">Injeção de Dependência</abbr>**.
* Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação **JWT tokens** e **HTTP Basic**.
* Técnicas mais avançadas (mas igualmente fáceis) para declaração de **modelos JSON profundamente aninhados** (graças ao Pydantic).
* Integrações **GraphQL** com o <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> e outras bibliotecas.
* Muitos recursos extras (graças ao Starlette) como:
* **WebSockets**
* testes extrememamente fáceis baseados em HTTPX e `pytest`
* **CORS**
* **Cookie Sessions**
* ...e mais.
## Performance { #performance }
Testes de performance da _Independent TechEmpower_ mostram aplicações **FastAPI** rodando sob Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">um dos _frameworks_ Python mais rápidos disponíveis</a>, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*)
Para entender mais sobre performance, veja a seção <a href="https://fastapi.tiangolo.com/pt/benchmarks/" class="internal-link" target="_blank">Comparações</a>.
## Dependências { #dependencies }
O FastAPI depende do Pydantic e do Starlette.
### Dependências `standard` { #standard-dependencies }
Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vêm com o grupo `standard` (padrão) de dependências opcionais:
Utilizado pelo Pydantic:
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - para validação de email.
Utilizado pelo Starlette:
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Obrigatório caso você queira utilizar o `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Obrigatório se você quer utilizar a configuração padrão de templates.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obrigatório se você deseja suporte a <abbr title="convertendo a string que vem de uma requisição HTTP em dados Python">"parsing"</abbr> de formulário, com `request.form()`.
Utilizado pelo FastAPI:
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
* `fastapi-cli[standard]` - que disponibiliza o comando `fastapi`.
* Isso inclui `fastapi-cloud-cli`, que permite implantar sua aplicação FastAPI na <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
### Sem as dependências `standard` { #without-standard-dependencies }
Se você não deseja incluir as dependências opcionais `standard`, você pode instalar utilizando `pip install fastapi` ao invés de `pip install "fastapi[standard]"`.
### Sem o `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Se você quiser instalar o FastAPI com as dependências padrão, mas sem o `fastapi-cloud-cli`, você pode instalar com `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Dependências opcionais adicionais { #additional-optional-dependencies }
Existem algumas dependências adicionais que você pode querer instalar.
Dependências opcionais adicionais do Pydantic:
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - para gerenciamento de configurações.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - tipos extras para serem utilizados com o Pydantic.
Dependências opcionais adicionais do FastAPI:
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Obrigatório se você deseja utilizar o `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Obrigatório se você deseja utilizar o `UJSONResponse`.
## Licença { #license }
Esse projeto é licenciado sob os termos da licença MIT.
+5
View File
@@ -0,0 +1,5 @@
# Aprender { #learn }
Aqui estão as seções introdutórias e os tutoriais para aprender o **FastAPI**.
Pode considerar isto um **livro**, um **curso**, a forma **oficial** e recomendada de aprender o FastAPI. 😎
+28
View File
@@ -0,0 +1,28 @@
# Full Stack FastAPI Template { #full-stack-fastapi-template }
_Templates_, embora tipicamente venham com alguma configuração específica, são desenhados para serem flexíveis e customizáveis. Isso permite que você os modifique e adapte para as especificações do seu projeto, fazendo-os um excelente ponto de partida. 🏁
Você pode usar esse _template_ para começar, já que ele inclui várias configurações iniciais, segurança, banco de dados, e alguns _endpoints_ de API já feitos para você.
Repositório GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI Template</a>
## Full Stack FastAPI Template - Pilha de Tecnologias e Recursos { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/pt) para a API do backend em Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para as interações do Python com bancos de dados SQL (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), usado pelo FastAPI, para validação de dados e gerenciamento de configurações.
- 💾 [PostgreSQL](https://www.postgresql.org) como banco de dados SQL.
- 🚀 [React](https://react.dev) para o frontend.
- 💃 Usando TypeScript, hooks, [Vite](https://vitejs.dev), e outras partes de uma _stack_ frontend moderna.
- 🎨 [Chakra UI](https://chakra-ui.com) para os componentes de frontend.
- 🤖 Um cliente frontend automaticamente gerado.
- 🧪 [Playwright](https://playwright.dev) para testes Ponta-a-Ponta.
- 🦇 Suporte para modo escuro.
- 🐋 [Docker Compose](https://www.docker.com) para desenvolvimento e produção.
- 🔒 _Hash_ seguro de senhas por padrão.
- 🔑 Autenticação por token JWT.
- 📫 Recuperação de senhas baseada em email.
- ✅ Testes com [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) como proxy reverso / balanceador de carga.
- 🚢 Instruções de _deployment_ usando Docker Compose, incluindo como configurar um proxy frontend com Traefik para gerenciar automaticamente certificados HTTPS.
- 🏭 CI (Integração Contínua) e CD (_Deploy_ Contínuo) baseado em GitHub Actions.
+576
View File
@@ -0,0 +1,576 @@
# Introdução aos tipos Python { #python-types-intro }
O Python possui suporte para "dicas de tipo" ou "type hints" (também chamado de "anotações de tipo" ou "type annotations")
Esses **"type hints"** são uma sintaxe especial que permite declarar o <abbr title = "por exemplo: str, int, float, bool">tipo</abbr> de uma variável.
Ao declarar tipos para suas variáveis, editores e ferramentas podem oferecer um melhor suporte.
Este é apenas um **tutorial rápido / atualização** sobre type hints do Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI**... que é realmente muito pouco.
O **FastAPI** é baseado nesses type hints, eles oferecem muitas vantagens e benefícios.
Mas mesmo que você nunca use o **FastAPI**, você se beneficiaria de aprender um pouco sobre eles.
/// note | Nota
Se você é um especialista em Python e já sabe tudo sobre type hints, pule para o próximo capítulo.
///
## Motivação { #motivation }
Vamos começar com um exemplo simples:
{* ../../docs_src/python_types/tutorial001.py *}
A chamada deste programa gera:
```
John Doe
```
A função faz o seguinte:
* Pega um `first_name` e `last_name`.
* Converte a primeira letra de cada uma em maiúsculas com `title()`.
* <abbr title = "Agrupa-os, como um. Com o conteúdo de um após o outro.">Concatena</abbr> com um espaço no meio.
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
### Edite-o { #edit-it }
É um programa muito simples.
Mas agora imagine que você estava escrevendo do zero.
Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos...
Mas então você deve chamar "esse método que converte a primeira letra em maiúscula".
Era `upper`? Era `uppercase`? `first_uppercase`? `capitalize`?
Em seguida, tente com o velho amigo do programador, o preenchimento automático do editor.
Você digita o primeiro parâmetro da função, `first_name`, depois um ponto (`.`) e, em seguida, pressiona `Ctrl + Space` para acionar a conclusão.
Mas, infelizmente, você não obtém nada útil:
<img src="/img/python-types/image01.png">
### Adicionar tipos { #add-types }
Vamos modificar uma única linha da versão anterior.
Vamos mudar exatamente esse fragmento, os parâmetros da função, de:
```Python
first_name, last_name
```
para:
```Python
first_name: str, last_name: str
```
É isso aí.
Esses são os "type hints":
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
Isso não é o mesmo que declarar valores padrão como seria com:
```Python
first_name="john", last_name="doe"
```
É uma coisa diferente.
Estamos usando dois pontos (`:`), não é igual a (`=`).
E adicionar type hints normalmente não muda o que acontece do que aconteceria sem eles.
Mas agora, imagine que você está novamente no meio da criação dessa função, mas com type hints.
No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl+Space` e vê:
<img src="/img/python-types/image02.png">
Com isso, você pode rolar, vendo as opções, até encontrar o que "soa familiar":
<img src="/img/python-types/image03.png">
## Mais motivação { #more-motivation }
Verifique esta função, ela já possui type hints:
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
Como o editor conhece os tipos de variáveis, você não obtém apenas o preenchimento automático, mas também as verificações de erro:
<img src="/img/python-types/image04.png">
Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str(age)`:
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
## Declarando Tipos { #declaring-types }
Você acabou de ver o local principal para declarar type hints. Como parâmetros de função.
Este também é o principal local em que você os usaria com o **FastAPI**.
### Tipos simples { #simple-types }
Você pode declarar todos os tipos padrão de Python, não apenas `str`.
Você pode usar, por exemplo:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
### Tipos genéricos com parâmetros de tipo { #generic-types-with-type-parameters }
Existem algumas estruturas de dados que podem conter outros valores, como `dict`, `list`, `set` e `tuple`. E os valores internos também podem ter seu próprio tipo.
Estes tipos que possuem tipos internos são chamados de tipos "**genéricos**". E é possível declará-los mesmo com os seus tipos internos.
Para declarar esses tipos e os tipos internos, você pode usar o módulo Python padrão `typing`. Ele existe especificamente para suportar esses type hints.
#### Versões mais recentes do Python { #newer-versions-of-python }
A sintaxe utilizando `typing` é **compatível** com todas as versões, desde o Python 3.6 até as últimas, incluindo o Python 3.9, 3.10, etc.
Conforme o Python evolui, **novas versões** chegam com suporte melhorado para esses type annotations, e em muitos casos, você não precisará nem importar e utilizar o módulo `typing` para declarar os type annotations.
Se você pode escolher uma versão mais recente do Python para o seu projeto, você poderá aproveitar isso ao seu favor.
Em todos os documentos existem exemplos compatíveis com cada versão do Python (quando existem diferenças).
Por exemplo, "**Python 3.6+**" significa que é compatível com o Python 3.6 ou superior (incluindo o 3.7, 3.8, 3.9, 3.10, etc). E "**Python 3.9+**" significa que é compatível com o Python 3.9 ou mais recente (incluindo o 3.10, etc).
Se você pode utilizar a **versão mais recente do Python**, utilize os exemplos para as últimas versões. Eles terão as **melhores e mais simples sintaxes**, como por exemplo, "**Python 3.10+**".
#### List { #list }
Por exemplo, vamos definir uma variável para ser uma `list` de `str`.
//// tab | Python 3.9+
Declare uma variável com a mesma sintaxe com dois pontos (`:`)
Como tipo, coloque `list`.
Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial006_py39.py!}
```
////
//// tab | Python 3.8+
De `typing`, importe `List` (com o `L` maiúsculo):
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial006.py!}
```
Declare uma variável com a mesma sintaxe com dois pontos (`:`)
Como tipo, coloque o `List` que você importou de `typing`.
Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
```Python hl_lines="4"
{!> ../../docs_src/python_types/tutorial006.py!}
```
////
/// info | Informação
Estes tipos internos dentro dos colchetes são chamados "parâmetros de tipo" (type parameters).
Neste caso, `str` é o parâmetro de tipo passado para `List` (ou `list` no Python 3.9 ou superior).
///
Isso significa: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`".
/// tip | Dica
Se você usa o Python 3.9 ou superior, você não precisa importar `List` de `typing`. Você pode utilizar o mesmo tipo `list` no lugar.
///
Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da lista:
<img src="/img/python-types/image05.png">
Sem tipos, isso é quase impossível de alcançar.
Observe que a variável `item` é um dos elementos da lista `items`.
E, ainda assim, o editor sabe que é um `str` e fornece suporte para isso.
#### Tuple e Set { #tuple-and-set }
Você faria o mesmo para declarar `tuple`s e `set`s:
//// tab | Python 3.9+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial007_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial007.py!}
```
////
Isso significa que:
* A variável `items_t` é uma `tuple` com 3 itens, um `int`, outro `int` e uma `str`.
* A variável `items_s` é um `set`, e cada um de seus itens é do tipo `bytes`.
#### Dict { #dict }
Para definir um `dict`, você passa 2 parâmetros de tipo, separados por vírgulas.
O primeiro parâmetro de tipo é para as chaves do `dict`.
O segundo parâmetro de tipo é para os valores do `dict`:
//// tab | Python 3.9+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008.py!}
```
////
Isso significa que:
* A variável `prices` é um `dict`:
* As chaves deste `dict` são do tipo `str` (digamos, o nome de cada item).
* Os valores deste `dict` são do tipo `float` (digamos, o preço de cada item).
#### Union { #union }
Você pode declarar que uma variável pode ser de qualquer um dentre **diversos tipos**. Por exemplo, um `int` ou um `str`.
No Python 3.6 e superior (incluindo o Python 3.10), você pode utilizar o tipo `Union` de `typing`, e colocar dentro dos colchetes os possíveis tipos aceitáveis.
No Python 3.10 também existe uma **nova sintaxe** onde você pode colocar os possíveis tipos separados por uma <abbr title='também chamado de "bitwise ou operador", mas o significado é irrelevante aqui'>barra vertical (`|`)</abbr>.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b.py!}
```
////
Em ambos os casos, isso significa que `item` poderia ser um `int` ou um `str`.
#### Possivelmente `None` { #possibly-none }
Você pode declarar que um valor pode ter um tipo, como `str`, mas que ele também pode ser `None`.
No Python 3.6 e superior (incluindo o Python 3.10) você pode declará-lo importando e utilizando `Optional` do módulo `typing`.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009.py!}
```
O uso de `Optional[str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`.
`Optional[Something]` é na verdade um atalho para `Union[Something, None]`, eles são equivalentes.
Isso também significa que no Python 3.10, você pode utilizar `Something | None`:
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009.py!}
```
////
//// tab | Python 3.8+ alternativa
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b.py!}
```
////
#### Utilizando `Union` ou `Optional` { #using-union-or-optional }
Se você está utilizando uma versão do Python abaixo da 3.10, aqui vai uma dica do meu ponto de vista bem **subjetivo**:
* 🚨 Evite utilizar `Optional[SomeType]`
* No lugar, ✨ **use `Union[SomeType, None]`** ✨.
Ambos são equivalentes, e no final das contas, eles são o mesmo. Mas eu recomendaria o `Union` ao invés de `Optional` porque a palavra **Optional** parece implicar que o valor é opcional, quando na verdade significa "isso pode ser `None`", mesmo que ele não seja opcional e ainda seja obrigatório.
Eu penso que `Union[SomeType, None]` é mais explícito sobre o que ele significa.
Isso é apenas sobre palavras e nomes. Mas estas palavras podem afetar como os seus colegas de trabalho pensam sobre o código.
Por exemplo, vamos pegar esta função:
{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
O parâmetro `name` é definido como `Optional[str]`, mas ele **não é opcional**, você não pode chamar a função sem o parâmetro:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
O parâmetro `name` **ainda é obrigatório** (não *opcional*) porque ele não possui um valor padrão. Mesmo assim, `name` aceita `None` como valor:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
A boa notícia é, quando você estiver no Python 3.10 você não precisará se preocupar mais com isso, pois você poderá simplesmente utilizar o `|` para definir uniões de tipos:
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
E então você não precisará mais se preocupar com nomes como `Optional` e `Union`. 😎
#### Tipos genéricos { #generic-types }
Esses tipos que usam parâmetros de tipo entre colchetes são chamados **tipos genéricos** ou **genéricos**. Por exemplo:
//// tab | Python 3.10+
Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro):
* `list`
* `tuple`
* `set`
* `dict`
E o mesmo como no Python 3.8, do módulo `typing`:
* `Union`
* `Optional` (o mesmo que com o 3.8)
* ...entre outros.
No Python 3.10, como uma alternativa para a utilização dos genéricos `Union` e `Optional`, você pode usar a <abbr title='também chamado de "bitwise ou operador", mas o significado não é relevante aqui'>barra vertical (`|`)</abbr> para declarar uniões de tipos. Isso é muito melhor e mais simples.
////
//// tab | Python 3.9+
Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro):
* `list`
* `tuple`
* `set`
* `dict`
E o mesmo como no Python 3.8, do módulo `typing`:
* `Union`
* `Optional`
* ...entre outros.
////
//// tab | Python 3.8+
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Union`
* `Optional`
* ...entre outros.
////
### Classes como tipos { #classes-as-types }
Você também pode declarar uma classe como o tipo de uma variável.
Digamos que você tenha uma classe `Person`, com um nome:
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
Então você pode declarar que uma variável é do tipo `Person`:
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
E então, novamente, você recebe todo o suporte do editor:
<img src="/img/python-types/image06.png">
Perceba que isso significa que "`one_person` é uma **instância** da classe `Person`".
Isso não significa que "`one_person` é a **classe** chamada `Person`".
## Modelos Pydantic { #pydantic-models }
O <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> é uma biblioteca Python para executar a validação de dados.
Você declara a "forma" dos dados como classes com atributos.
E cada atributo tem um tipo.
Em seguida, você cria uma instância dessa classe com alguns valores e ela os validará, os converterá para o tipo apropriado (se for esse o caso) e fornecerá um objeto com todos os dados.
E você recebe todo o suporte do editor com esse objeto resultante.
Retirado dos documentos oficiais dos Pydantic:
//// tab | Python 3.10+
```Python
{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!> ../../docs_src/python_types/tutorial011_py39.py!}
```
////
//// tab | Python 3.8+
```Python
{!> ../../docs_src/python_types/tutorial011.py!}
```
////
/// info | Informação
Para saber mais sobre o <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, verifique a sua documentação</a>.
///
O **FastAPI** é todo baseado em Pydantic.
Você verá muito mais disso na prática no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}.
/// tip | Dica
O Pydantic tem um comportamento especial quando você usa `Optional` ou `Union[Something, None]` sem um valor padrão. Você pode ler mais sobre isso na documentação do Pydantic sobre <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">campos Opcionais Obrigatórios</a>.
///
## Type Hints com Metadados de Anotações { #type-hints-with-metadata-annotations }
O Python possui uma funcionalidade que nos permite incluir **<abbr title="Informação sobre a informação, neste caso, informação sobre o tipo, e.g. uma descrição.">metadados</abbr> adicionais** nos type hints utilizando `Annotated`.
//// tab | Python 3.9+
No Python 3.9, `Annotated` é parte da biblioteca padrão, então você pode importá-lo de `typing`.
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial013_py39.py!}
```
////
//// tab | Python 3.8+
Em versões abaixo do Python 3.9, você importa `Annotated` de `typing_extensions`.
Ele já estará instalado com o **FastAPI**.
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial013.py!}
```
////
O Python em si não faz nada com este `Annotated`. E para editores e outras ferramentas, o tipo ainda é `str`.
Mas você pode utilizar este espaço dentro do `Annotated` para fornecer ao **FastAPI** metadata adicional sobre como você deseja que a sua aplicação se comporte.
O importante aqui de se lembrar é que **o primeiro *type parameter*** que você informar ao `Annotated` é o **tipo de fato**. O resto é apenas metadado para outras ferramentas.
Por hora, você precisa apenas saber que o `Annotated` existe, e que ele é Python padrão. 😎
Mais tarde você verá o quão **poderoso** ele pode ser.
/// tip | Dica
O fato de que isso é **Python padrão** significa que você ainda obtém a **melhor experiência de desenvolvedor possível** no seu editor, com as ferramentas que você utiliza para analisar e refatorar o seu código, etc. ✨
E também que o seu código será muito compatível com diversas outras ferramentas e bibliotecas Python. 🚀
///
## Type hints no **FastAPI** { #type-hints-in-fastapi }
O **FastAPI** aproveita esses type hints para fazer várias coisas.
Com o **FastAPI**, você declara parâmetros com type hints e obtém:
* **Suporte ao editor**.
* **Verificações de tipo**.
... e o **FastAPI** usa as mesmas declarações para:
* **Definir requisitos**: dos parâmetros de rota, parâmetros da consulta, cabeçalhos, corpos, dependências, etc.
* **Converter dados**: da solicitação para o tipo necessário.
* **Validar dados**: provenientes de cada solicitação:
* Gerando **erros automáticos** retornados ao cliente quando os dados são inválidos.
* **Documentar** a API usando OpenAPI:
* que é usado pelas interfaces de usuário da documentação interativa automática.
Tudo isso pode parecer abstrato. Não se preocupe. Você verá tudo isso em ação no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}.
O importante é que, usando tipos padrão de Python, em um único local (em vez de adicionar mais classes, decoradores, etc.), o **FastAPI** fará muito trabalho para você.
/// info | Informação
Se você já passou por todo o tutorial e voltou para ver mais sobre os tipos, um bom recurso é <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank"> a "cheat sheet" do `mypy` </a>.
///
+3
View File
@@ -0,0 +1,3 @@
# Recursos { #resources }
Material complementar, links externos, artigos e muito mais. ✈️
+85
View File
@@ -0,0 +1,85 @@
# Tarefas em segundo plano { #background-tasks }
Você pode definir tarefas em segundo plano para serem executadas *após* retornar uma resposta.
Isso é útil para operações que precisam acontecer após uma request, mas que o cliente não precisa realmente esperar a operação terminar antes de receber a resposta.
Isso inclui, por exemplo:
* Notificações por e-mail enviadas após realizar uma ação:
* Como conectar-se a um servidor de e-mail e enviar um e-mail tende a ser “lento” (vários segundos), você pode retornar a resposta imediatamente e enviar a notificação por e-mail em segundo plano.
* Processamento de dados:
* Por exemplo, digamos que você receba um arquivo que precisa passar por um processo lento; você pode retornar uma resposta “Accepted” (HTTP 202) e processar o arquivo em segundo plano.
## Usando `BackgroundTasks` { #using-backgroundtasks }
Primeiro, importe `BackgroundTasks` e defina um parâmetro na sua *função de operação de rota* com uma declaração de tipo `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
O **FastAPI** criará o objeto do tipo `BackgroundTasks` para você e o passará como esse parâmetro.
## Crie uma função de tarefa { #create-a-task-function }
Crie uma função para ser executada como a tarefa em segundo plano.
É apenas uma função padrão que pode receber parâmetros.
Pode ser uma função `async def` ou um `def` normal, o **FastAPI** saberá como lidar com isso corretamente.
Neste caso, a função da tarefa escreverá em um arquivo (simulando o envio de um e-mail).
E como a operação de escrita não usa `async` e `await`, definimos a função com um `def` normal:
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## Adicione a tarefa em segundo plano { #add-the-background-task }
Dentro da sua *função de operação de rota*, passe sua função de tarefa para o objeto de *tarefas em segundo plano* com o método `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
O `.add_task()` recebe como argumentos:
* Uma função de tarefa a ser executada em segundo plano (`write_notification`).
* Qualquer sequência de argumentos que deve ser passada para a função de tarefa na ordem (`email`).
* Quaisquer argumentos nomeados que devem ser passados para a função de tarefa (`message="some notification"`).
## Injeção de dependências { #dependency-injection }
Usar `BackgroundTasks` também funciona com o sistema de injeção de dependências; você pode declarar um parâmetro do tipo `BackgroundTasks` em vários níveis: em uma *função de operação de rota*, em uma dependência (dependable), em uma subdependência, etc.
O **FastAPI** sabe o que fazer em cada caso e como reutilizar o mesmo objeto, de forma que todas as tarefas em segundo plano sejam combinadas e executadas em segundo plano depois:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
Neste exemplo, as mensagens serão escritas no arquivo `log.txt` *após* o envio da resposta.
Se houver uma query na request, ela será registrada em uma tarefa em segundo plano.
E então outra tarefa em segundo plano gerada na *função de operação de rota* escreverá uma mensagem usando o parâmetro de path `email`.
## Detalhes técnicos { #technical-details }
A classe `BackgroundTasks` vem diretamente de <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>.
Ela é importada/incluída diretamente no FastAPI para que você possa importá-la de `fastapi` e evitar importar acidentalmente a alternativa `BackgroundTask` (sem o `s` no final) de `starlette.background`.
Usando apenas `BackgroundTasks` (e não `BackgroundTask`), é possível usá-la como um parâmetro de *função de operação de rota* e deixar o **FastAPI** cuidar do resto para você, assim como ao usar o objeto `Request` diretamente.
Ainda é possível usar `BackgroundTask` sozinho no FastAPI, mas você precisa criar o objeto no seu código e retornar uma `Response` da Starlette incluindo-o.
Você pode ver mais detalhes na <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">documentação oficial da Starlette para tarefas em segundo plano</a>.
## Ressalva { #caveat }
Se você precisar realizar computação pesada em segundo plano e não necessariamente precisar que seja executada pelo mesmo processo (por exemplo, você não precisa compartilhar memória, variáveis, etc.), pode se beneficiar do uso de outras ferramentas maiores, como o <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
Elas tendem a exigir configurações mais complexas, um gerenciador de fila de mensagens/tarefas, como RabbitMQ ou Redis, mas permitem executar tarefas em segundo plano em vários processos e, especialmente, em vários servidores.
Mas se você precisa acessar variáveis e objetos da mesma aplicação **FastAPI**, ou precisa realizar pequenas tarefas em segundo plano (como enviar uma notificação por e-mail), você pode simplesmente usar `BackgroundTasks`.
## Recapitulando { #recap }
Importe e use `BackgroundTasks` com parâmetros em *funções de operação de rota* e dependências para adicionar tarefas em segundo plano.
@@ -0,0 +1,556 @@
# Aplicações Maiores - Múltiplos Arquivos { #bigger-applications-multiple-files }
Se você está construindo uma aplicação ou uma API web, é raro que você possa colocar tudo em um único arquivo.
**FastAPI** oferece uma ferramenta conveniente para estruturar sua aplicação, mantendo toda a flexibilidade.
/// info | Informação
Se você vem do Flask, isso seria o equivalente aos Blueprints do Flask.
///
## Um exemplo de estrutura de arquivos { #an-example-file-structure }
Digamos que você tenha uma estrutura de arquivos como esta:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   └── routers
│   │ ├── __init__.py
│   │ ├── items.py
│   │ └── users.py
│   └── internal
│   ├── __init__.py
│   └── admin.py
```
/// tip | Dica
Existem vários arquivos `__init__.py` presentes em cada diretório ou subdiretório.
Isso permite a importação de código de um arquivo para outro.
Por exemplo, no arquivo `app/main.py`, você poderia ter uma linha como:
```
from app.routers import items
```
///
* O diretório `app` contém todo o código da aplicação. Ele possui um arquivo `app/__init__.py` vazio, o que o torna um "pacote Python" (uma coleção de "módulos Python"): `app`.
* Dentro dele, o arquivo `app/main.py` está localizado em um pacote Python (diretório com `__init__.py`). Portanto, ele é um "módulo" desse pacote: `app.main`.
* Existem também um arquivo `app/dependencies.py`, assim como o `app/main.py`, ele é um "módulo": `app.dependencies`.
* Há um subdiretório `app/routers/` com outro arquivo `__init__.py`, então ele é um "subpacote Python": `app.routers`.
* O arquivo `app/routers/items.py` está dentro de um pacote, `app/routers/`, portanto, é um "submódulo": `app.routers.items`.
* O mesmo com `app/routers/users.py`, ele é outro submódulo: `app.routers.users`.
* Há também um subdiretório `app/internal/` com outro arquivo `__init__.py`, então ele é outro "subpacote Python":`app.internal`.
* E o arquivo `app/internal/admin.py` é outro submódulo: `app.internal.admin`.
<img src="/img/tutorial/bigger-applications/package.drawio.svg">
A mesma estrutura de arquivos com comentários:
```
.
├── app # "app" é um pacote Python
│   ├── __init__.py # este arquivo torna "app" um "pacote Python"
│   ├── main.py # "main" módulo, e.g. import app.main
│   ├── dependencies.py # "dependencies" módulo, e.g. import app.dependencies
│   └── routers # "routers" é um "subpacote Python"
│   │ ├── __init__.py # torna "routers" um "subpacote Python"
│   │ ├── items.py # "items" submódulo, e.g. import app.routers.items
│   │ └── users.py # "users" submódulo, e.g. import app.routers.users
│   └── internal # "internal" é um "subpacote Python"
│   ├── __init__.py # torna "internal" um "subpacote Python"
│   └── admin.py # "admin" submódulo, e.g. import app.internal.admin
```
## `APIRouter` { #apirouter }
Vamos supor que o arquivo dedicado a lidar apenas com usuários seja o submódulo em `/app/routers/users.py`.
Você quer manter as *operações de rota* relacionadas aos seus usuários separadas do restante do código, para mantê-lo organizado.
Mas ele ainda faz parte da mesma aplicação/web API **FastAPI** (faz parte do mesmo "pacote Python").
Você pode criar as *operações de rotas* para esse módulo usando o `APIRouter`.
### Importe `APIRouter` { #import-apirouter }
você o importa e cria uma "instância" da mesma maneira que faria com a classe `FastAPI`:
```Python hl_lines="1 3" title="app/routers/users.py"
{!../../docs_src/bigger_applications/app/routers/users.py!}
```
### *Operações de Rota* com `APIRouter` { #path-operations-with-apirouter }
E então você o utiliza para declarar suas *operações de rota*.
Utilize-o da mesma maneira que utilizaria a classe `FastAPI`:
```Python hl_lines="6 11 16" title="app/routers/users.py"
{!../../docs_src/bigger_applications/app/routers/users.py!}
```
Você pode pensar em `APIRouter` como uma classe "mini `FastAPI`".
Todas as mesmas opções são suportadas.
Todos os mesmos `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip | Dica
Neste exemplo, a variável é chamada de `router`, mas você pode nomeá-la como quiser.
///
Vamos incluir este `APIRouter` na aplicação principal `FastAPI`, mas primeiro, vamos verificar as dependências e outro `APIRouter`.
## Dependências { #dependencies }
Vemos que precisaremos de algumas dependências usadas em vários lugares da aplicação.
Então, as colocamos em seu próprio módulo de `dependencies` (`app/dependencies.py`).
Agora usaremos uma dependência simples para ler um cabeçalho `X-Token` personalizado:
//// tab | Python 3.9+
```Python hl_lines="3 6-8" title="app/dependencies.py"
{!> ../../docs_src/bigger_applications/app_an_py39/dependencies.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 5-7" title="app/dependencies.py"
{!> ../../docs_src/bigger_applications/app_an/dependencies.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated` se possível.
///
```Python hl_lines="1 4-6" title="app/dependencies.py"
{!> ../../docs_src/bigger_applications/app/dependencies.py!}
```
////
/// tip | Dica
Estamos usando um cabeçalho inventado para simplificar este exemplo.
Mas em casos reais, você obterá melhores resultados usando os [Utilitários de Segurança](security/index.md){.internal-link target=_blank} integrados.
///
## Outro módulo com `APIRouter` { #another-module-with-apirouter }
Digamos que você também tenha os endpoints dedicados a manipular "itens" do seu aplicativo no módulo em `app/routers/items.py`.
Você tem *operações de rota* para:
* `/items/`
* `/items/{item_id}`
É tudo a mesma estrutura de `app/routers/users.py`.
Mas queremos ser mais inteligentes e simplificar um pouco o código.
Sabemos que todas as *operações de rota* neste módulo têm o mesmo:
* Path `prefix`: `/items`.
* `tags`: (apenas uma tag: `items`).
* Extra `responses`.
* `dependencies`: todas elas precisam da dependência `X-Token` que criamos.
Então, em vez de adicionar tudo isso a cada *operação de rota*, podemos adicioná-lo ao `APIRouter`.
```Python hl_lines="5-10 16 21" title="app/routers/items.py"
{!../../docs_src/bigger_applications/app/routers/items.py!}
```
Como o caminho de cada *operação de rota* deve começar com `/`, como em:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...o prefixo não deve incluir um `/` final.
Então, o prefixo neste caso é `/items`.
Também podemos adicionar uma lista de `tags` e `responses` extras que serão aplicadas a todas as *operações de rota* incluídas neste roteador.
E podemos adicionar uma lista de `dependencies` que serão adicionadas a todas as *operações de rota* no roteador e serão executadas/resolvidas para cada solicitação feita a elas.
/// tip | Dica
Observe que, assim como [dependências em *decoradores de operação de rota*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, nenhum valor será passado para sua *função de operação de rota*.
///
O resultado final é que os caminhos dos itens agora são:
* `/items/`
* `/items/{item_id}`
...como pretendíamos.
* Elas serão marcadas com uma lista de tags que contêm uma única string `"items"`.
* Essas "tags" são especialmente úteis para os sistemas de documentação interativa automática (usando OpenAPI).
* Todas elas incluirão as `responses` predefinidas.
* Todas essas *operações de rota* terão a lista de `dependencies` avaliada/executada antes delas.
* Se você também declarar dependências em uma *operação de rota* específica, **elas também serão executadas**.
* As dependências do roteador são executadas primeiro, depois as [`dependencies` no decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank} e, em seguida, as dependências de parâmetros normais.
* Você também pode adicionar [dependências de `Segurança` com `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
/// tip | Dica
Ter `dependencies` no `APIRouter` pode ser usado, por exemplo, para exigir autenticação para um grupo inteiro de *operações de rota*. Mesmo que as dependências não sejam adicionadas individualmente a cada uma delas.
///
/// check | Verifique
Os parâmetros `prefix`, `tags`, `responses` e `dependencies` são (como em muitos outros casos) apenas um recurso do **FastAPI** para ajudar a evitar duplicação de código.
///
### Importe as dependências { #import-the-dependencies }
Este código reside no módulo `app.routers.items`, o arquivo `app/routers/items.py`.
E precisamos obter a função de dependência do módulo `app.dependencies`, o arquivo `app/dependencies.py`.
Então usamos uma importação relativa com `..` para as dependências:
```Python hl_lines="3" title="app/routers/items.py"
{!../../docs_src/bigger_applications/app/routers/items.py!}
```
#### Como funcionam as importações relativas { #how-relative-imports-work }
/// tip | Dica
Se você sabe perfeitamente como funcionam as importações, continue para a próxima seção abaixo.
///
Um único ponto `.`, como em:
```Python
from .dependencies import get_token_header
```
significaria:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)...
* encontre o módulo `dependencies` (um arquivo imaginário em `app/routers/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Mas esse arquivo não existe, nossas dependências estão em um arquivo em `app/dependencies.py`.
Lembre-se de como nossa estrutura app/file se parece:
<img src="/img/tutorial/bigger-applications/package.drawio.svg">
---
Os dois pontos `..`, como em:
```Python
from ..dependencies import get_token_header
```
significa:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) reside (o diretório `app/routers/`)...
* vá para o pacote pai (o diretório `app/`)...
* e lá, encontre o módulo `dependencies` (o arquivo em `app/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Isso funciona corretamente! 🎉
---
Da mesma forma, se tivéssemos usado três pontos `...`, como em:
```Python
from ...dependencies import get_token_header
```
isso significaria:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)...
* vá para o pacote pai (o diretório `app/`)...
* então vá para o pai daquele pacote (não há pacote pai, `app` é o nível superior 😱)...
* e lá, encontre o módulo `dependencies` (o arquivo em `app/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Isso se referiria a algum pacote acima de `app/`, com seu próprio arquivo `__init__.py`, etc. Mas não temos isso. Então, isso geraria um erro em nosso exemplo. 🚨
Mas agora você sabe como funciona, então você pode usar importações relativas em seus próprios aplicativos, não importa o quão complexos eles sejam. 🤓
### Adicione algumas `tags`, `responses` e `dependencies` personalizadas { #add-some-custom-tags-responses-and-dependencies }
Não estamos adicionando o prefixo `/items` nem `tags=["items"]` a cada *operação de rota* porque os adicionamos ao `APIRouter`.
Mas ainda podemos adicionar _mais_ `tags` que serão aplicadas a uma *operação de rota* específica, e também algumas `responses` extras específicas para essa *operação de rota*:
```Python hl_lines="30-31" title="app/routers/items.py"
{!../../docs_src/bigger_applications/app/routers/items.py!}
```
/// tip | Dica
Esta última operação de caminho terá a combinação de tags: `["items", "custom"]`.
E também terá ambas as respostas na documentação, uma para `404` e uma para `403`.
///
## O principal `FastAPI` { #the-main-fastapi }
Agora, vamos ver o módulo em `app/main.py`.
Aqui é onde você importa e usa a classe `FastAPI`.
Este será o arquivo principal em seu aplicativo que une tudo.
E como a maior parte de sua lógica agora viverá em seu próprio módulo específico, o arquivo principal será bem simples.
### Importe o `FastAPI` { #import-fastapi }
Você importa e cria uma classe `FastAPI` normalmente.
E podemos até declarar [dependências globais](dependencies/global-dependencies.md){.internal-link target=_blank} que serão combinadas com as dependências para cada `APIRouter`:
```Python hl_lines="1 3 7" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
### Importe o `APIRouter` { #import-the-apirouter }
Agora importamos os outros submódulos que possuem `APIRouter`s:
```Python hl_lines="4-5" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
Como os arquivos `app/routers/users.py` e `app/routers/items.py` são submódulos que fazem parte do mesmo pacote Python `app`, podemos usar um único ponto `.` para importá-los usando "importações relativas".
### Como funciona a importação { #how-the-importing-works }
A seção:
```Python
from .routers import items, users
```
significa:
* Começando no mesmo pacote em que este módulo (o arquivo `app/main.py`) reside (o diretório `app/`)...
* procure o subpacote `routers` (o diretório em `app/routers/`)...
* e dele, importe o submódulo `items` (o arquivo em `app/routers/items.py`) e `users` (o arquivo em `app/routers/users.py`)...
O módulo `items` terá uma variável `router` (`items.router`). Esta é a mesma que criamos no arquivo `app/routers/items.py`, é um objeto `APIRouter`.
E então fazemos o mesmo para o módulo `users`.
Também poderíamos importá-los como:
```Python
from app.routers import items, users
```
/// info | Informação
A primeira versão é uma "importação relativa":
```Python
from .routers import items, users
```
A segunda versão é uma "importação absoluta":
```Python
from app.routers import items, users
```
Para saber mais sobre pacotes e módulos Python, leia <a href="https://docs.python.org/3/tutorial/modules.html" class="external-link" target="_blank">a documentação oficial do Python sobre módulos</a>.
///
### Evite colisões de nomes { #avoid-name-collisions }
Estamos importando o submódulo `items` diretamente, em vez de importar apenas sua variável `router`.
Isso ocorre porque também temos outra variável chamada `router` no submódulo `users`.
Se tivéssemos importado um após o outro, como:
```Python
from .routers.items import router
from .routers.users import router
```
o `router` de `users` sobrescreveria o de `items` e não poderíamos usá-los ao mesmo tempo.
Então, para poder usar ambos no mesmo arquivo, importamos os submódulos diretamente:
```Python hl_lines="5" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
### Inclua os `APIRouter`s para `usuários` e `itens` { #include-the-apirouters-for-users-and-items }
Agora, vamos incluir os `router`s dos submódulos `users` e `items`:
```Python hl_lines="10-11" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
/// info | Informação
`users.router` contém o `APIRouter` dentro do arquivo `app/routers/users.py`.
E `items.router` contém o `APIRouter` dentro do arquivo `app/routers/items.py`.
///
Com `app.include_router()` podemos adicionar cada `APIRouter` ao aplicativo principal `FastAPI`.
Ele incluirá todas as rotas daquele roteador como parte dele.
/// note | Detalhes Técnicos
Na verdade, ele criará internamente uma *operação de rota* para cada *operação de rota* que foi declarada no `APIRouter`.
Então, nos bastidores, ele realmente funcionará como se tudo fosse o mesmo aplicativo único.
///
/// check | Verifique
Você não precisa se preocupar com desempenho ao incluir roteadores.
Isso levará microssegundos e só acontecerá na inicialização.
Então não afetará o desempenho. ⚡
///
### Inclua um `APIRouter` com um `prefix`, `tags`, `responses` e `dependencies` personalizados { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Agora, vamos imaginar que sua organização lhe deu o arquivo `app/internal/admin.py`.
Ele contém um `APIRouter` com algumas *operações de rota* de administração que sua organização compartilha entre vários projetos.
Para este exemplo, será super simples. Mas digamos que, como ele é compartilhado com outros projetos na organização, não podemos modificá-lo e adicionar um `prefix`, `dependencies`, `tags`, etc. diretamente ao `APIRouter`:
```Python hl_lines="3" title="app/internal/admin.py"
{!../../docs_src/bigger_applications/app/internal/admin.py!}
```
Mas ainda queremos definir um `prefix` personalizado ao incluir o `APIRouter` para que todas as suas *operações de rota* comecem com `/admin`, queremos protegê-lo com as `dependencies` que já temos para este projeto e queremos incluir `tags` e `responses`.
Podemos declarar tudo isso sem precisar modificar o `APIRouter` original passando esses parâmetros para `app.include_router()`:
```Python hl_lines="14-17" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
Dessa forma, o `APIRouter` original permanecerá inalterado, para que possamos compartilhar o mesmo arquivo `app/internal/admin.py` com outros projetos na organização.
O resultado é que em nosso aplicativo, cada uma das *operações de rota* do módulo `admin` terá:
* O prefixo `/admin`.
* A tag `admin`.
* A dependência `get_token_header`.
* A resposta `418`. 🍵
Mas isso afetará apenas o `APIRouter` em nosso aplicativo, e não em nenhum outro código que o utilize.
Assim, por exemplo, outros projetos poderiam usar o mesmo `APIRouter` com um método de autenticação diferente.
### Inclua uma *operação de rota* { #include-a-path-operation }
Também podemos adicionar *operações de rota* diretamente ao aplicativo `FastAPI`.
Aqui fazemos isso... só para mostrar que podemos 🤷:
```Python hl_lines="21-23" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
e funcionará corretamente, junto com todas as outras *operações de rota* adicionadas com `app.include_router()`.
/// note | Detalhes Técnicos Avançados
**Observação**: este é um detalhe muito técnico que você provavelmente pode **simplesmente pular**.
---
Os `APIRouter`s não são "montados", eles não são isolados do resto do aplicativo.
Isso ocorre porque queremos incluir suas *operações de rota* no esquema OpenAPI e nas interfaces de usuário.
Como não podemos simplesmente isolá-los e "montá-los" independentemente do resto, as *operações de rota* são "clonadas" (recriadas), não incluídas diretamente.
///
## Verifique a documentação automática da API { #check-the-automatic-api-docs }
Agora, execute sua aplicação:
<div class="termy">
```console
$ fastapi dev app/main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
E abra os documentos em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá a documentação automática da API, incluindo os caminhos de todos os submódulos, usando os caminhos (e prefixos) corretos e as tags corretas:
<img src="/img/tutorial/bigger-applications/image01.png">
## Inclua o mesmo roteador várias vezes com `prefix` diferentes { #include-the-same-router-multiple-times-with-different-prefix }
Você também pode usar `.include_router()` várias vezes com o *mesmo* roteador usando prefixos diferentes.
Isso pode ser útil, por exemplo, para expor a mesma API sob prefixos diferentes, por exemplo, `/api/v1` e `/api/latest`.
Esse é um uso avançado que você pode não precisar, mas está lá caso precise.
## Inclua um `APIRouter` em outro { #include-an-apirouter-in-another }
Da mesma forma que você pode incluir um `APIRouter` em um aplicativo `FastAPI`, você pode incluir um `APIRouter` em outro `APIRouter` usando:
```Python
router.include_router(other_router)
```
Certifique-se de fazer isso antes de incluir `router` no aplicativo `FastAPI`, para que as *operações de rota* de `other_router` também sejam incluídas.
+60
View File
@@ -0,0 +1,60 @@
# Corpo - Campos { #body-fields }
Da mesma forma que você pode declarar validações adicionais e metadados nos parâmetros de uma *função de operação de rota* com `Query`, `Path` e `Body`, você pode declarar validações e metadados dentro de modelos do Pydantic usando `Field` do Pydantic.
## Importe `Field` { #import-field }
Primeiro, você tem que importá-lo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Atenção
Note que `Field` é importado diretamente do `pydantic`, não do `fastapi` como todo o resto (`Query`, `Path`, `Body`, etc).
///
## Declare atributos do modelo { #declare-model-attributes }
Você pode então utilizar `Field` com atributos do modelo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` funciona da mesma forma que `Query`, `Path` e `Body`, ele possui todos os mesmos parâmetros, etc.
/// note | Detalhes Técnicos
Na realidade, `Query`, `Path` e outros que você verá em seguida, criam objetos de subclasses de uma classe `Param` comum, que é ela mesma uma subclasse da classe `FieldInfo` do Pydantic.
E `Field` do Pydantic retorna uma instância de `FieldInfo` também.
`Body` também retorna objetos de uma subclasse de `FieldInfo` diretamente. E tem outras que você verá mais tarde que são subclasses da classe `Body`.
Lembre-se que quando você importa `Query`, `Path`, e outros de `fastapi`, esse são na realidade funções que retornam classes especiais.
///
/// tip | Dica
Note como cada atributo do modelo com um tipo, valor padrão e `Field` possuem a mesma estrutura que parâmetros de *funções de operações de rota*, com `Field` ao invés de `Path`, `Query` e `Body`.
///
## Adicione informações extras { #add-extra-information }
Você pode declarar informação extra em `Field`, `Query`, `Body`, etc. E isso será incluído no JSON Schema gerado.
Você irá aprender mais sobre adicionar informações extras posteriormente nessa documentação, quando estiver aprendendo a declarar exemplos.
/// warning | Atenção
Chaves extras passadas para `Field` também estarão presentes no schema OpenAPI resultante da sua aplicação.
Como essas chaves podem não fazer necessariamente parte da especificação OpenAPI, algumas ferramentas de OpenAPI, por exemplo [o validador do OpenAPI](https://validator.swagger.io/), podem não funcionar com o schema gerado.
///
## Recapitulando { #recap }
Você pode usar `Field` do Pydantic para declarar validações extras e metadados para atributos do modelo.
Você também pode usar os argumentos de palavras-chave extras para passar metadados do JSON Schema adicionais.
@@ -0,0 +1,171 @@
# Corpo - Múltiplos parâmetros { #body-multiple-parameters }
Agora que nós vimos como usar `Path` e `Query`, veremos usos mais avançados de declarações no corpo da requisição.
## Misture `Path`, `Query` e parâmetros de corpo { #mix-path-query-and-body-parameters }
Primeiro, é claro, você pode misturar `Path`, `Query` e declarações de parâmetro no corpo da requisição livremente e o **FastAPI** saberá o que fazer.
E você também pode declarar parâmetros de corpo como opcionais, definindo o valor padrão com `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Nota
Repare que, neste caso, o `item` que seria capturado a partir do corpo é opcional. Visto que ele possui `None` como valor padrão.
///
## Múltiplos parâmetros de corpo { #multiple-body-parameters }
No exemplo anterior, as *operações de rota* esperariam um JSON no corpo contendo os atributos de um `Item`, exemplo:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Mas você pode também declarar múltiplos parâmetros de corpo, por exemplo, `item` e `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
Neste caso, o **FastAPI** perceberá que existe mais de um parâmetro de corpo na função (dois parâmetros que são modelos Pydantic).
Então, ele usará o nome dos parâmetros como chaves (nome dos campos) no corpo, e espera um corpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Nota
Repare que mesmo que o `item` esteja declarado da mesma maneira que antes, agora é esperado que ele esteja dentro do corpo com uma chave `item`.
///
O **FastAPI** fará a conversão automática a partir da requisição, assim esse parâmetro `item` receberá seu respectivo conteúdo e o mesmo ocorrerá com `user`.
Ele executará a validação dos dados compostos e irá documentá-los de maneira compatível com o esquema OpenAPI e documentação automática.
## Valores singulares no corpo { #singular-values-in-body }
Assim como existem uma `Query` e uma `Path` para definir dados adicionais para parâmetros de consulta e de rota, o **FastAPI** provê o equivalente para `Body`.
Por exemplo, extendendo o modelo anterior, você poder decidir por ter uma outra chave `importance` no mesmo corpo, além de `item` e `user`.
Se você declará-lo como é, porque é um valor singular, o **FastAPI** assumirá que se trata de um parâmetro de consulta.
Mas você pode instruir o **FastAPI** para tratá-lo como outra chave do corpo usando `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
Neste caso, o **FastAPI** esperará um corpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Mais uma vez, ele converterá os tipos de dados, validar, documentar, etc.
## Múltiplos parâmetros de corpo e consulta { #multiple-body-params-and-query }
Obviamente, você também pode declarar parâmetros de consulta assim que você precisar, de modo adicional a quaisquer parâmetros de corpo.
Dado que, por padrão, valores singulares são interpretados como parâmetros de consulta, você não precisa explicitamente adicionar uma `Query`, você pode somente:
```Python
q: Union[str, None] = None
```
Ou como em Python 3.10 e versões superiores:
```Python
q: str | None = None
```
Por exemplo:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Informação
`Body` também possui todas as validações adicionais e metadados de parâmetros como em `Query`,`Path` e outras que você verá depois.
///
## Declare um único parâmetro de corpo indicando sua chave { #embed-a-single-body-parameter }
Suponha que você tem um único parâmetro de corpo `item`, a partir de um modelo Pydantic `Item`.
Por padrão, o **FastAPI** esperará que seu conteúdo venha no corpo diretamente.
Mas se você quiser que ele espere por um JSON com uma chave `item` e dentro dele os conteúdos do modelo, como ocorre ao declarar vários parâmetros de corpo, você pode usar o parâmetro especial de `Body` chamado `embed`:
```Python
item: Item = Body(embed=True)
```
como em:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
Neste caso o **FastAPI** esperará um corpo como:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
ao invés de:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Recapitulando { #recap }
Você pode adicionar múltiplos parâmetros de corpo para sua *função de operação de rota*, mesmo que a requisição possa ter somente um único corpo.
E o **FastAPI** vai manipulá-los, mandar para você os dados corretos na sua função, e validar e documentar o schema correto na *operação de rota*.
Você também pode declarar valores singulares para serem recebidos como parte do corpo.
E você pode instruir o **FastAPI** para requisitar no corpo a indicação de chave mesmo quando existe somente um único parâmetro declarado.
+247
View File
@@ -0,0 +1,247 @@
# Corpo - Modelos aninhados { #body-nested-models }
Com o **FastAPI**, você pode definir, validar, documentar e usar modelos arbitrariamente e profundamente aninhados (graças ao Pydantic).
## Campos do tipo Lista { #list-fields }
Você pode definir um atributo como um subtipo. Por exemplo, uma `list` do Python:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Isso fará com que tags seja uma lista de itens mesmo sem declarar o tipo dos elementos desta lista.
## Campos do tipo Lista com um parâmetro de tipo { #list-fields-with-type-parameter }
Mas o Python tem uma maneira específica de declarar listas com tipos internos ou "parâmetros de tipo":
### Importe `List` do typing { #import-typings-list }
No Python 3.9 e superior você pode usar a `list` padrão para declarar essas anotações de tipo, como veremos abaixo. 💡
Mas nas versões do Python anteriores à 3.9 (3.6 e superiores), primeiro é necessário importar `List` do módulo padrão `typing` do Python:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### Declare uma `list` com um parâmetro de tipo { #declare-a-list-with-a-type-parameter }
Para declarar tipos que têm parâmetros de tipo (tipos internos), como `list`, `dict`, `tuple`:
* Se você estiver em uma versão do Python inferior a 3.9, importe a versão equivalente do módulo `typing`
* Passe o(s) tipo(s) interno(s) como "parâmetros de tipo" usando colchetes: `[` e `]`
No Python 3.9, seria:
```Python
my_list: list[str]
```
Em versões do Python anteriores à 3.9, seria:
```Python
from typing import List
my_list: List[str]
```
Essa é a sintaxe padrão do Python para declarações de tipo.
Use a mesma sintaxe padrão para atributos de modelo com tipos internos.
Portanto, em nosso exemplo, podemos fazer com que `tags` sejam especificamente uma "lista de strings":
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Tipos "set" { #set-types }
Mas então, quando nós pensamos mais, percebemos que as tags não devem se repetir, elas provavelmente devem ser strings únicas.
E que o Python tem um tipo de dados especial para conjuntos de itens únicos, o `set`.
Então podemos declarar `tags` como um conjunto de strings:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Com isso, mesmo que você receba uma requisição contendo dados duplicados, ela será convertida em um conjunto de itens exclusivos.
E sempre que você enviar esses dados como resposta, mesmo se a fonte tiver duplicatas, eles serão gerados como um conjunto de itens exclusivos.
E também teremos anotações/documentação em conformidade.
## Modelos aninhados { #nested-models }
Cada atributo de um modelo Pydantic tem um tipo.
Mas esse tipo pode ser outro modelo Pydantic.
Portanto, você pode declarar "objects" JSON profundamente aninhados com nomes, tipos e validações de atributos específicos.
Tudo isso, aninhado arbitrariamente.
### Defina um sub-modelo { #define-a-submodel }
Por exemplo, nós podemos definir um modelo `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Use o sub-modelo como um tipo { #use-the-submodel-as-a-type }
E então podemos usa-lo como o tipo de um atributo:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Isso significa que o **FastAPI** vai esperar um corpo similar à:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Novamente, apenas fazendo essa declaração, com o **FastAPI**, você ganha:
* Suporte do editor (preenchimento automático, etc.), inclusive para modelos aninhados
* Conversão de dados
* Validação de dados
* Documentação automatica
## Tipos especiais e validação { #special-types-and-validation }
Além dos tipos singulares normais como `str`, `int`, `float`, etc. Você também pode usar tipos singulares mais complexos que herdam de `str`.
Para ver todas as opções possíveis, consulte a <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Visão geral dos tipos do Pydantic</a>. Você verá alguns exemplos no próximo capítulo.
Por exemplo, no modelo `Image` nós temos um campo `url`, nós podemos declara-lo como um `HttpUrl` do Pydantic invés de como uma `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
A string será verificada para se tornar uma URL válida e documentada no JSON Schema / OpenAPI como tal.
## Atributos como listas de submodelos { #attributes-with-lists-of-submodels }
Você também pode usar modelos Pydantic como subtipos de `list`, `set`, etc:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Isso vai esperar(converter, validar, documentar, etc) um corpo JSON tal qual:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Informação
Observe como a chave `images` agora tem uma lista de objetos de imagem.
///
## Modelos profundamente aninhados { #deeply-nested-models }
Você pode definir modelos profundamente aninhados de forma arbitrária:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Informação
Observe como `Offer` tem uma lista de `Item`s, que por sua vez têm uma lista opcional de `Image`s
///
## Corpos de listas puras { #bodies-of-pure-lists }
Se o valor de primeiro nível do corpo JSON que você espera for um `array` do JSON (uma` lista` do Python), você pode declarar o tipo no parâmetro da função, da mesma forma que nos modelos do Pydantic:
```Python
images: List[Image]
```
ou no Python 3.9 e superior:
```Python
images: list[Image]
```
como em:
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
## Suporte de editor em todo canto { #editor-support-everywhere }
E você obtém suporte do editor em todos os lugares.
Mesmo para itens dentro de listas:
<img src="/img/tutorial/body-nested-models/image01.png">
Você não conseguiria este tipo de suporte de editor se estivesse trabalhando diretamente com `dict` em vez de modelos Pydantic.
Mas você também não precisa se preocupar com eles, os dicts de entrada são convertidos automaticamente e sua saída é convertida automaticamente para JSON também.
## Corpos de `dict`s arbitrários { #bodies-of-arbitrary-dicts }
Você também pode declarar um corpo como um `dict` com chaves de algum tipo e valores de outro tipo.
Sem ter que saber de antemão quais são os nomes de campos/atributos válidos (como seria o caso dos modelos Pydantic).
Isso seria útil se você deseja receber chaves que ainda não conhece.
---
Outro caso útil é quando você deseja ter chaves de outro tipo, por exemplo, `int`.
É isso que vamos ver aqui.
Neste caso, você aceitaria qualquer `dict`, desde que tenha chaves` int` com valores `float`:
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
/// tip | Dica
Leve em condideração que o JSON só suporta `str` como chaves.
Mas o Pydantic tem conversão automática de dados.
Isso significa que, embora os clientes da API só possam enviar strings como chaves, desde que essas strings contenham inteiros puros, o Pydantic irá convertê-los e validá-los.
E o `dict` que você recebe como `weights` terá, na verdade, chaves `int` e valores` float`.
///
## Recapitulação { #recap }
Com **FastAPI** você tem a flexibilidade máxima fornecida pelos modelos Pydantic, enquanto seu código é mantido simples, curto e elegante.
Mas com todos os benefícios:
* Suporte do editor (preenchimento automático em todo canto!)
* Conversão de dados (parsing/serialização)
* Validação de dados
* Documentação dos esquemas
* Documentação automática
+116
View File
@@ -0,0 +1,116 @@
# Corpo - Atualizações { #body-updates }
## Atualização de dados existentes com `PUT` { #update-replacing-with-put }
Para atualizar um item, você pode usar a operação <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
Você pode usar `jsonable_encoder` para converter os dados de entrada em dados que podem ser armazenados como JSON (por exemplo, com um banco de dados NoSQL). Por exemplo, convertendo `datetime` em `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` é usado para receber dados que devem substituir os dados existentes.
### Aviso sobre a substituição { #warning-about-replacing }
Isso significa que, se você quiser atualizar o item `bar` usando `PUT` com um corpo contendo:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
Como ele não inclui o atributo já armazenado `"tax": 20.2`, o modelo de entrada assumiria o valor padrão de `"tax": 10.5`.
E os dados seriam salvos com esse "novo" `tax` de `10.5`.
## Atualizações parciais com `PATCH` { #partial-updates-with-patch }
Você também pode usar a operação <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> para atualizar parcialmente os dados.
Isso significa que você pode enviar apenas os dados que deseja atualizar, deixando o restante intacto.
/// note | Nota
`PATCH` é menos comumente usado e conhecido do que `PUT`.
E muitas equipes usam apenas `PUT`, mesmo para atualizações parciais.
Você é **livre** para usá-los como preferir, **FastAPI** não impõe restrições.
Mas este guia te dá uma ideia de como eles são destinados a serem usados.
///
### Usando o parâmetro `exclude_unset` do Pydantic { #using-pydantics-exclude-unset-parameter }
Se você quiser receber atualizações parciais, é muito útil usar o parâmetro `exclude_unset` no método `.model_dump()` do modelo do Pydantic.
Como `item.model_dump(exclude_unset=True)`.
/// info | Informação
No Pydantic v1, o método que era chamado `.dict()` e foi descontinuado (mas ainda suportado) no Pydantic v2. Agora, deve-se usar o método `.model_dump()`.
Os exemplos aqui usam `.dict()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_dump()` a partir do Pydantic v2.
///
Isso gera um `dict` com apenas os dados definidos ao criar o modelo `item`, excluindo os valores padrão.
Então, você pode usar isso para gerar um `dict` com apenas os dados definidos (enviados na solicitação), omitindo valores padrão:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Usando o parâmetro `update` do Pydantic { #using-pydantics-update-parameter }
Agora, você pode criar uma cópia do modelo existente usando `.model_copy()`, e passar o parâmetro `update` com um `dict` contendo os dados para atualizar.
/// info | Informação
No Pydantic v1, o método era chamado `.copy()`, ele foi descontinuado (mas ainda suportado) no Pydantic v2, e renomeado para `.model_copy()`.
Os exemplos aqui usam `.copy()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_copy()` com o Pydantic v2.
///
Como `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Recapitulando as atualizações parciais { #partial-updates-recap }
Resumindo, para aplicar atualizações parciais você pode:
* (Opcionalmente) usar `PATCH` em vez de `PUT`.
* Recuperar os dados armazenados.
* Colocar esses dados em um modelo do Pydantic.
* Gerar um `dict` sem valores padrão a partir do modelo de entrada (usando `exclude_unset`).
* Dessa forma, você pode atualizar apenas os valores definidos pelo usuário, em vez de substituir os valores já armazenados com valores padrão em seu modelo.
* Criar uma cópia do modelo armazenado, atualizando seus atributos com as atualizações parciais recebidas (usando o parâmetro `update`).
* Converter o modelo copiado em algo que possa ser armazenado no seu banco de dados (por exemplo, usando o `jsonable_encoder`).
* Isso é comparável ao uso do método `.model_dump()`, mas garante (e converte) os valores para tipos de dados que possam ser convertidos em JSON, por exemplo, `datetime` para `str`.
* Salvar os dados no seu banco de dados.
* Retornar o modelo atualizado.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Dica
Você pode realmente usar essa mesma técnica com uma operação HTTP `PUT`.
Mas o exemplo aqui usa `PATCH` porque foi criado para esses casos de uso.
///
/// note | Nota
Observe que o modelo de entrada ainda é validado.
Portanto, se você quiser receber atualizações parciais que possam omitir todos os atributos, precisará ter um modelo com todos os atributos marcados como opcionais (com valores padrão ou `None`).
Para distinguir os modelos com todos os valores opcionais para **atualizações** e modelos com valores obrigatórios para **criação**, você pode usar as ideias descritas em [Modelos Adicionais](extra-models.md){.internal-link target=_blank}.
///
+172
View File
@@ -0,0 +1,172 @@
# Corpo da requisição { #request-body }
Quando você precisa enviar dados de um cliente (como de um navegador) para sua API, você os envia como um **corpo da requisição**.
O corpo da **requisição** é a informação enviada pelo cliente para sua API. O corpo da **resposta** é a informação que sua API envia para o cliente.
Sua API quase sempre precisa enviar um corpo na **resposta**. Mas os clientes não necessariamente precisam enviar **corpos de requisição** o tempo todo, às vezes eles apenas requisitam um path, talvez com alguns parâmetros de consulta, mas não enviam um corpo.
Para declarar um corpo da **requisição**, você utiliza os modelos do <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> com todos os seus poderes e benefícios.
/// info | Informação
Para enviar dados, você deve usar um dos: `POST` (o mais comum), `PUT`, `DELETE` ou `PATCH`.
Enviar um corpo em uma requisição `GET` não tem um comportamento definido nas especificações, porém é suportado pelo FastAPI, apenas para casos de uso bem complexos/extremos.
Como é desencorajado, a documentação interativa com Swagger UI não irá mostrar a documentação para o corpo da requisição para um `GET`, e proxies que intermediarem podem não suportar o corpo da requisição.
///
## Importe o `BaseModel` do Pydantic { #import-pydantics-basemodel }
Primeiro, você precisa importar `BaseModel` do `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Crie seu modelo de dados { #create-your-data-model }
Então você declara seu modelo de dados como uma classe que herda `BaseModel`.
Utilize os tipos Python padrão para todos os atributos:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Assim como quando declaramos parâmetros de consulta, quando um atributo do modelo possui um valor padrão, ele se torna opcional. Caso contrário, se torna obrigatório. Use `None` para torná-lo opcional.
Por exemplo, o modelo acima declara um JSON "`object`" (ou `dict` no Python) como esse:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...como `description` e `tax` são opcionais (com um valor padrão de `None`), esse JSON "`object`" também é válido:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Declare como um parâmetro { #declare-it-as-a-parameter }
Para adicioná-lo à sua *operação de rota*, declare-o da mesma maneira que você declarou parâmetros de rota e de consulta:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...e declare o seu tipo como o modelo que você criou, `Item`.
## Resultados { #results }
Apenas com essa declaração de tipos do Python, o **FastAPI** irá:
* Ler o corpo da requisição como um JSON.
* Converter os tipos correspondentes (se necessário).
* Validar os dados.
* Se algum dado for inválido, irá retornar um erro bem claro, indicando exatamente onde e o que estava incorreto.
* Entregar a você a informação recebida no parâmetro `item`.
* Como você o declarou na função como do tipo `Item`, você também terá o suporte do editor (completação, etc) para todos os atributos e seus tipos.
* Gerar definições de <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> para o seu modelo; você também pode usá-las em qualquer outro lugar se fizer sentido para o seu projeto.
* Esses schemas farão parte do esquema OpenAPI gerado, e serão usados pelas <abbr title="User Interfaces Interfaces de usuário">UIs</abbr> de documentação automática.
## Documentação automática { #automatic-docs }
Os JSON Schemas dos seus modelos farão parte do esquema OpenAPI gerado para sua aplicação, e aparecerão na documentação interativa da API:
<img src="/img/tutorial/body/image01.png">
E também serão utilizados na documentação da API dentro de cada *operação de rota* que precisar deles:
<img src="/img/tutorial/body/image02.png">
## Suporte do editor { #editor-support }
No seu editor, dentro da função você receberá dicas de tipos e completação em todo lugar (isso não aconteceria se você recebesse um `dict` em vez de um modelo Pydantic):
<img src="/img/tutorial/body/image03.png">
Você também poderá receber verificações de erros para operações de tipos incorretas:
<img src="/img/tutorial/body/image04.png">
Isso não é por acaso, todo o framework foi construído em volta deste design.
E foi imensamente testado na fase de design, antes de qualquer implementação, para garantir que funcionaria para todos os editores de texto.
Houveram mudanças no próprio Pydantic para que isso fosse possível.
As capturas de tela anteriores foram capturas no <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
Mas você terá o mesmo suporte do editor no <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> e na maioria dos editores Python:
<img src="/img/tutorial/body/image05.png">
/// tip | Dica
Se você utiliza o <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> como editor, você pode utilizar o <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Plugin do Pydantic para o PyCharm </a>.
Melhora o suporte do editor para seus modelos Pydantic com:
* preenchimento automático
* verificação de tipos
* refatoração
* buscas
* inspeções
///
## Use o modelo { #use-the-model }
Dentro da função, você pode acessar todos os atributos do objeto do modelo diretamente:
{* ../../docs_src/body/tutorial002_py310.py *}
/// info | Informação
No Pydantic v1 o método se chamava `.dict()`, ele foi descontinuado (mas ainda é suportado) no Pydantic v2, e renomeado para `.model_dump()`.
Os exemplos aqui usam `.dict()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_dump()` se puder usar o Pydantic v2.
///
## Corpo da requisição + parâmetros de rota { #request-body-path-parameters }
Você pode declarar parâmetros de rota e corpo da requisição ao mesmo tempo.
O **FastAPI** irá reconhecer que os parâmetros da função que combinam com parâmetros de rota devem ser **retirados da rota**, e que parâmetros da função que são declarados como modelos Pydantic sejam **retirados do corpo da requisição**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Corpo da requisição + parâmetros de rota + parâmetros de consulta { #request-body-path-query-parameters }
Você também pode declarar parâmetros de **corpo**, **rota** e **consulta**, ao mesmo tempo.
O **FastAPI** irá reconhecer cada um deles e retirar a informação do local correto.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Os parâmetros da função serão reconhecidos conforme abaixo:
* Se o parâmetro também é declarado no **path**, será utilizado como um parâmetro de rota.
* Se o parâmetro é de um **tipo único** (como `int`, `float`, `str`, `bool`, etc) será interpretado como um parâmetro de **consulta**.
* Se o parâmetro é declarado como um **modelo Pydantic**, será interpretado como o **corpo** da requisição.
/// note | Nota
O FastAPI saberá que o valor de `q` não é obrigatório por causa do valor padrão `= None`.
O `str | None` (Python 3.10+) ou o `Union` em `Union[str, None]` (Python 3.8+) não é utilizado pelo FastAPI para determinar que o valor não é obrigatório, ele saberá que não é obrigatório porque tem um valor padrão `= None`.
Mas adicionar as anotações de tipo permitirá ao seu editor oferecer um suporte melhor e detectar erros.
///
## Sem o Pydantic { #without-pydantic }
Se você não quer utilizar os modelos Pydantic, você também pode utilizar o parâmetro **Body**. Veja a documentação para [Body - Parâmetros múltiplos: Valores singulares no body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
@@ -0,0 +1,78 @@
# Modelos de Parâmetros de Cookie { #cookie-parameter-models }
Se você possui um grupo de **cookies** que estão relacionados, você pode criar um **modelo Pydantic** para declará-los. 🍪
Isso lhe permitiria **reutilizar o modelo** em **diversos lugares** e também declarar validações e metadata para todos os parâmetros de uma vez. 😎
/// note | Nota
Isso é suportado desde a versão `0.115.0` do FastAPI. 🤓
///
/// tip | Dica
Essa mesma técnica se aplica para `Query`, `Cookie`, e `Header`. 😎
///
## Cookies com Modelos Pydantic { #cookies-with-a-pydantic-model }
Declare o parâmetro de **cookie** que você precisa em um **modelo Pydantic**, e depois declare o parâmetro como um `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
O **FastAPI** irá **extrair** os dados para **cada campo** dos **cookies** recebidos na requisição e lhe fornecer o modelo Pydantic que você definiu.
## Verifique a Documentação { #check-the-docs }
Você pode ver os cookies definidos na IU da documentação em `/docs`:
<div class="screenshot">
<img src="/img/tutorial/cookie-param-models/image01.png">
</div>
/// info | Informação
Tenha em mente que, como os **navegadores lidam com cookies** de maneira especial e por baixo dos panos, eles **não** permitem facilmente que o **JavaScript** lidem com eles.
Se você for na **IU da documentação da API** em `/docs` você poderá ver a **documentação** para cookies das suas *operações de rotas*.
Mas mesmo que você **adicionar os dados** e clicar em "Executar", pelo motivo da IU da documentação trabalhar com **JavaScript**, os cookies não serão enviados, e você verá uma mensagem de **erro** como se você não tivesse escrito nenhum dado.
///
## Proibir Cookies Adicionais { #forbid-extra-cookies }
Em alguns casos especiais (provavelmente não muito comuns), você pode querer **restringir** os cookies que você deseja receber.
Agora a sua API possui o poder de controlar o seu próprio <abbr title="Isso é uma brincadeira, só por precaução. Isso não tem nada a ver com consentimentos de cookies, mas é engraçado que até a API consegue rejeitar os coitados dos cookies. Coma um biscoito. 🍪">consentimento de cookie</abbr>. 🤪🍪
Você pode utilizar a configuração do modelo Pydantic para `proibir` qualquer campo `extra`.
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
Se o cliente tentar enviar alguns **cookies extras**, eles receberão um retorno de **erro**.
Coitados dos banners de cookies com todo o seu esforço para obter o seu consentimento para a <abbr title="Isso é uma outra piada. Não preste atenção em mim. Beba um café com o seu cookie. ☕">API rejeitá-lo</abbr>. 🍪
Por exemplo, se o cliente tentar enviar um cookie `santa_tracker` com o valor de `good-list-please`, o cliente receberá uma resposta de **erro** informando que o `santa_tracker` <abbr title="O papai noel desaprova a falta de biscoitos. 🎅 Ok, chega de piadas com os cookies.">cookie não é permitido</abbr>:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Resumo { #summary }
Você consegue utilizar **modelos Pydantic** para declarar <abbr title="Coma um último biscoito antes de você ir embora. 🍪">**cookies**</abbr> no **FastAPI**. 😎
+45
View File
@@ -0,0 +1,45 @@
# Parâmetros de Cookie { #cookie-parameters }
Você pode definir parâmetros de Cookie da mesma maneira que define parâmetros com `Query` e `Path`.
## Importe `Cookie` { #import-cookie }
Primeiro importe `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Declare parâmetros de `Cookie` { #declare-cookie-parameters }
Então declare os parâmetros de cookie usando a mesma estrutura que em `Path` e `Query`.
Você pode definir o valor padrão, assim como todas as validações extras ou parâmetros de anotação:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalhes Técnicos
`Cookie` é uma classe "irmã" de `Path` e `Query`. Ela também herda da mesma classe em comum `Param`.
Mas lembre-se que quando você importa `Query`, `Path`, `Cookie` e outras de `fastapi`, elas são na verdade funções que retornam classes especiais.
///
/// info | Informação
Para declarar cookies, você precisa usar `Cookie`, pois caso contrário, os parâmetros seriam interpretados como parâmetros de consulta.
///
/// info | Informação
Tenha em mente que, como os **navegadores lidam com cookies** de maneiras especiais e nos bastidores, eles **não** permitem facilmente que o **JavaScript** os acesse.
Se você for à **interface de documentação da API** em `/docs`, poderá ver a **documentação** de cookies para suas *operações de rota*.
Mas mesmo que você **preencha os dados** e clique em "Execute", como a interface de documentação funciona com **JavaScript**, os cookies não serão enviados e você verá uma mensagem de **erro** como se você não tivesse escrito nenhum valor.
///
## Recapitulando { #recap }
Declare cookies com `Cookie`, usando o mesmo padrão comum que utiliza-se em `Query` e `Path`.
+88
View File
@@ -0,0 +1,88 @@
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS ou "Cross-Origin Resource Sharing"</a> refere-se às situações em que um frontend rodando em um navegador possui um código JavaScript que se comunica com um backend, e o backend está em uma "origem" diferente do frontend.
## Origem { #origin }
Uma origem é a combinação de protocolo (`http`, `https`), domínio (`myapp.com`, `localhost`, `localhost.tiangolo.com`), e porta (`80`, `443`, `8080`).
Então, todos estes são origens diferentes:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Mesmo se todos estiverem em `localhost`, eles usam diferentes protocolos ou portas, portanto, são "origens" diferentes.
## Passos { #steps }
Então, digamos que você tenha um frontend rodando no seu navegador em `http://localhost:8080`, e seu JavaScript esteja tentando se comunicar com um backend rodando em `http://localhost` (como não especificamos uma porta, o navegador assumirá a porta padrão `80`).
Portanto, o navegador enviará uma requisição HTTP `OPTIONS` ao backend `:80`, e se o backend enviar os cabeçalhos apropriados autorizando a comunicação a partir dessa origem diferente (`http://localhost:8080`), então o navegador `:8080` permitirá que o JavaScript no frontend envie sua requisição para o backend `:80`.
Para conseguir isso, o backend `:80` deve ter uma lista de "origens permitidas".
Neste caso, a lista terá que incluir `http://localhost:8080` para o frontend `:8080` funcionar corretamente.
## Curingas { #wildcards }
É possível declarar a lista como `"*"` (um "curinga") para dizer que tudo está permitido.
Mas isso só permitirá certos tipos de comunicação, excluindo tudo que envolva credenciais: cookies, cabeçalhos de autorização como aqueles usados com Bearer Tokens, etc.
Então, para que tudo funcione corretamente, é melhor especificar explicitamente as origens permitidas.
## Usar `CORSMiddleware` { #use-corsmiddleware }
Você pode configurá-lo em sua aplicação **FastAPI** usando o `CORSMiddleware`.
* Importe `CORSMiddleware`.
* Crie uma lista de origens permitidas (como strings).
* Adicione-a como um "middleware" à sua aplicação **FastAPI**.
Você também pode especificar se o seu backend permite:
* Credenciais (Cabeçalhos de autorização, Cookies, etc).
* Métodos HTTP específicos (`POST`, `PUT`) ou todos eles com o curinga `"*"`.
* Cabeçalhos HTTP específicos ou todos eles com o curinga `"*"`.
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
Os parâmetros padrão usados pela implementação `CORSMiddleware` são restritivos por padrão, então você precisará habilitar explicitamente as origens, métodos ou cabeçalhos específicos para que os navegadores tenham permissão para usá-los em um contexto cross domain.
Os seguintes argumentos são suportados:
* `allow_origins` - Uma lista de origens que devem ter permissão para fazer requisições de origem cruzada. Por exemplo, `['https://example.org', 'https://www.example.org']`. Você pode usar `['*']` para permitir qualquer origem.
* `allow_origin_regex` - Uma string regex para corresponder às origens que devem ter permissão para fazer requisições de origem cruzada. Por exemplo, `'https://.*\.example\.org'`.
* `allow_methods` - Uma lista de métodos HTTP que devem ser permitidos para requisições de origem cruzada. O padrão é `['GET']`. Você pode usar `['*']` para permitir todos os métodos padrão.
* `allow_headers` - Uma lista de cabeçalhos de solicitação HTTP que devem ter suporte para requisições de origem cruzada. O padrão é `[]`. Você pode usar `['*']` para permitir todos os cabeçalhos. Os cabeçalhos `Accept`, `Accept-Language`, `Content-Language` e `Content-Type` são sempre permitidos para <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">requisições CORS simples</a>.
* `allow_credentials` - Indica que os cookies devem ser suportados para requisições de origem cruzada. O padrão é `False`.
Nenhum de `allow_origins`, `allow_methods` e `allow_headers` pode ser definido como `['*']` se `allow_credentials` estiver definido como `True`. Todos eles devem ser <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards" class="external-link" rel="noopener" target="_blank">especificados explicitamente</a>.
* `expose_headers` - Indica quaisquer cabeçalhos de resposta que devem ser disponibilizados ao navegador. O padrão é `[]`.
* `max_age` - Define um tempo máximo em segundos para os navegadores armazenarem em cache as respostas CORS. O padrão é `600`.
O middleware responde a dois tipos específicos de solicitação HTTP...
### Requisições CORS pré-voo (preflight) { #cors-preflight-requests }
Estas são quaisquer solicitações `OPTIONS` com cabeçalhos `Origin` e `Access-Control-Request-Method`.
Nesse caso, o middleware interceptará a solicitação recebida e responderá com cabeçalhos CORS apropriados e uma resposta `200` ou `400` para fins informativos.
### Requisições Simples { #simple-requests }
Qualquer solicitação com um cabeçalho `Origin`. Neste caso, o middleware passará a solicitação normalmente, mas incluirá cabeçalhos CORS apropriados na resposta.
## Mais informações { #more-info }
Para mais informações sobre <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, consulte a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">documentação do CORS da Mozilla</a>.
/// note | Detalhes Técnicos
Você também pode usar `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vêm diretamente da Starlette.
///
+113
View File
@@ -0,0 +1,113 @@
# Depuração { #debugging }
Você pode conectar o depurador no seu editor, por exemplo, com o Visual Studio Code ou PyCharm.
## Chamar `uvicorn` { #call-uvicorn }
Em sua aplicação FastAPI, importe e execute `uvicorn` diretamente:
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
### Sobre `__name__ == "__main__"` { #about-name-main }
O objetivo principal de `__name__ == "__main__"` é ter algum código que seja executado quando seu arquivo for chamado com:
<div class="termy">
```console
$ python myapp.py
```
</div>
mas não é chamado quando outro arquivo o importa, como em:
```Python
from myapp import app
```
#### Mais detalhes { #more-details }
Digamos que seu arquivo se chama `myapp.py`.
Se você executá-lo com:
<div class="termy">
```console
$ python myapp.py
```
</div>
então a variável interna `__name__` no seu arquivo, criada automaticamente pelo Python, terá como valor a string `"__main__"`.
Então, a seção:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
vai executar.
---
Isso não acontecerá se você importar esse módulo (arquivo).
Então, se você tiver outro arquivo `importer.py` com:
```Python
from myapp import app
# Mais um pouco de código
```
nesse caso, a variável criada automaticamente dentro de `myapp.py` não terá a variável `__name__` com o valor `"__main__"`.
Então, a linha:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
não será executada.
/// info | Informação
Para mais informações, consulte <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">a documentação oficial do Python</a>.
///
## Execute seu código com seu depurador { #run-your-code-with-your-debugger }
Como você está executando o servidor Uvicorn diretamente do seu código, você pode chamar seu programa Python (sua aplicação FastAPI) diretamente do depurador.
---
Por exemplo, no Visual Studio Code, você pode:
* Ir para o painel "Debug".
* "Add configuration...".
* Selecionar "Python"
* Executar o depurador com a opção "`Python: Current File (Integrated Terminal)`".
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
Veja como pode parecer:
<img src="/img/tutorial/debugging/image01.png">
---
Se você usar o Pycharm, você pode:
* Abrir o menu "Executar".
* Selecionar a opção "Depurar...".
* Então um menu de contexto aparece.
* Selecionar o arquivo para depurar (neste caso, `main.py`).
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
Veja como pode parecer:
<img src="/img/tutorial/debugging/image02.png">
@@ -0,0 +1,288 @@
# Classes como Dependências { #classes-as-dependencies }
Antes de nos aprofundarmos no sistema de **Injeção de Dependência**, vamos melhorar o exemplo anterior.
## `dict` do exemplo anterior { #a-dict-from-the-previous-example }
No exemplo anterior, nós retornávamos um `dict` da nossa dependência ("injetável"):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Mas assim obtemos um `dict` como valor do parâmetro `commons` na *função de operação de rota*.
E sabemos que editores de texto não têm como oferecer muitas funcionalidades (como sugestões automáticas) para objetos do tipo `dict`, por que não há como eles saberem o tipo das chaves e dos valores.
Podemos fazer melhor...
## O que caracteriza uma dependência { #what-makes-a-dependency }
Até agora você apenas viu dependências declaradas como funções.
Mas essa não é a única forma de declarar dependências (mesmo que provavelmente seja a mais comum).
O fator principal para uma dependência é que ela deve ser "chamável"
Um objeto "chamável" em Python é qualquer coisa que o Python possa "chamar" como uma função
Então se você tiver um objeto `alguma_coisa` (que pode *não* ser uma função) que você possa "chamar" (executá-lo) dessa maneira:
```Python
something()
```
ou
```Python
something(some_argument, some_keyword_argument="foo")
```
Então esse objeto é um "chamável".
## Classes como dependências { #classes-as-dependencies_1 }
Você deve ter percebido que para criar um instância de uma classe em Python, a mesma sintaxe é utilizada.
Por exemplo:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
Nesse caso, `fluffy` é uma instância da classe `Cat`.
E para criar `fluffy`, você está "chamando" `Cat`.
Então, uma classe Python também é "chamável".
Então, no **FastAPI**, você pode utilizar uma classe Python como uma dependência.
O que o FastAPI realmente verifica, é se a dependência é algo chamável (função, classe, ou outra coisa) e os parâmetros que foram definidos.
Se você passar algo "chamável" como uma dependência do **FastAPI**, o framework irá analisar os parâmetros desse "chamável" e processá-los da mesma forma que os parâmetros de uma *função de operação de rota*. Incluindo as sub-dependências.
Isso também se aplica a objetos chamáveis que não recebem nenhum parâmetro. Da mesma forma que uma *função de operação de rota* sem parâmetros.
Então, podemos mudar o "injetável" na dependência `common_parameters` acima para a classe `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Observe o método `__init__` usado para criar uma instância da classe:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...ele possui os mesmos parâmetros que nosso `common_parameters` anterior:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Esses parâmetros são utilizados pelo **FastAPI** para "definir" a dependência.
Em ambos os casos teremos:
* Um parâmetro de consulta `q` opcional do tipo `str`.
* Um parâmetro de consulta `skip` do tipo `int`, com valor padrão `0`.
* Um parâmetro de consulta `limit` do tipo `int`, com valor padrão `100`.
Os dados serão convertidos, validados, documentados no esquema da OpenAPI e etc nos dois casos.
## Utilizando { #use-it }
Agora você pode declarar sua dependência utilizando essa classe.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
O **FastAPI** chama a classe `CommonQueryParams`. Isso cria uma "instância" dessa classe e é a instância que será passada para o parâmetro `commons` na sua função.
## Anotações de Tipo vs `Depends` { #type-annotation-vs-depends }
Perceba como escrevemos `CommonQueryParams` duas vezes no código abaixo:
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
O último `CommonQueryParams`, em:
```Python
... Depends(CommonQueryParams)
```
...é o que o **FastAPI** irá realmente usar para saber qual é a dependência.
É a partir dele que o FastAPI irá extrair os parâmetros passados e será o que o FastAPI irá realmente chamar.
---
Nesse caso, o primeiro `CommonQueryParams`, em:
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams ...
```
////
...não tem nenhum signficado especial para o **FastAPI**. O FastAPI não irá utilizá-lo para conversão dos dados, validação, etc (já que ele utiliza `Depends(CommonQueryParams)` para isso).
Na verdade você poderia escrever apenas:
//// tab | Python 3.8+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...como em:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Mas declarar o tipo é encorajado por que é a forma que o seu editor de texto sabe o que será passado como valor do parâmetro `commons`, e assim ele pode ajudar com preenchimento automático, verificações de tipo, etc:
<img src="/img/tutorial/dependencies/image02.png">
## Pegando um Atalho { #shortcut }
Mas você pode ver que temos uma repetição do código neste exemplo, escrevendo `CommonQueryParams` duas vezes:
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
O **FastAPI** nos fornece um atalho para esses casos, onde a dependência é *especificamente* uma classe que o **FastAPI** irá "chamar" para criar uma instância da própria classe.
Para esses casos específicos, você pode fazer o seguinte:
Em vez de escrever:
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...escreva:
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.8 non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Você declara a dependência como o tipo do parâmetro, e utiliza `Depends()` sem nenhum parâmetro, em vez de ter que escrever a classe *novamente* dentro de `Depends(CommonQueryParams)`.
O mesmo exemplo ficaria então dessa forma:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...e o **FastAPI** saberá o que fazer.
/// tip | Dica
Se isso parece mais confuso do que útil, não utilize, você não *precisa* disso.
É apenas um atalho. Por que o **FastAPI** se preocupa em ajudar a minimizar a repetição de código.
///
@@ -0,0 +1,69 @@
# Dependências em decoradores de operações de rota { #dependencies-in-path-operation-decorators }
Em alguns casos você não precisa necessariamente retornar o valor de uma dependência dentro de uma *função de operação de rota*.
Ou a dependência não retorna nenhum valor.
Mas você ainda precisa que ela seja executada/resolvida.
Para esses casos, em vez de declarar um parâmetro em uma *função de operação de rota* com `Depends`, você pode adicionar um argumento `dependencies` do tipo `list` ao decorador da operação de rota.
## Adicione `dependencies` ao decorador da operação de rota { #add-dependencies-to-the-path-operation-decorator }
O *decorador da operação de rota* recebe um argumento opcional `dependencies`.
Ele deve ser uma lista de `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
Essas dependências serão executadas/resolvidas da mesma forma que dependências comuns. Mas o valor delas (se existir algum) não será passado para a sua *função de operação de rota*.
/// tip | Dica
Alguns editores de texto checam parâmetros de funções não utilizados, e os mostram como erros.
Utilizando `dependencies` no *decorador da operação de rota* você pode garantir que elas serão executadas enquanto evita erros de editores/ferramentas.
Isso também pode ser útil para evitar confundir novos desenvolvedores que ao ver um parâmetro não usado no seu código podem pensar que ele é desnecessário.
///
/// info | Informação
Neste exemplo utilizamos cabeçalhos personalizados inventados `X-Key` e `X-Token`.
Mas em situações reais, como implementações de segurança, você pode obter mais vantagens em usar as [Ferramentas de segurança integradas (o próximo capítulo)](../security/index.md){.internal-link target=_blank}.
///
## Erros das dependências e valores de retorno { #dependencies-errors-and-return-values }
Você pode utilizar as mesmas *funções* de dependências que você usaria normalmente.
### Requisitos de Dependências { #dependency-requirements }
Dependências podem declarar requisitos de requisições (como cabeçalhos) ou outras subdependências:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
### Levantar exceções { #raise-exceptions }
Essas dependências podem `raise` exceções, da mesma forma que dependências comuns:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
### Valores de retorno { #return-values }
E elas também podem ou não retornar valores, eles não serão utilizados.
Então, você pode reutilizar uma dependência comum (que retorna um valor) que já seja utilizada em outro lugar, e mesmo que o valor não seja utilizado, a dependência será executada:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
## Dependências para um grupo de *operações de rota* { #dependencies-for-a-group-of-path-operations }
Mais a frente, quando você ler sobre como estruturar aplicações maiores ([Aplicações maiores - Múltiplos arquivos](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possivelmente com múltiplos arquivos, você aprenderá a declarar um único parâmetro `dependencies` para um grupo de *operações de rota*.
## Dependências globais { #global-dependencies }
No próximo passo veremos como adicionar dependências para uma aplicação `FastAPI` inteira, para que elas sejam aplicadas em toda *operação de rota*.
@@ -0,0 +1,289 @@
# Dependências com yield { #dependencies-with-yield }
O **FastAPI** possui suporte para dependências que realizam <abbr title='às vezes também chamado de "código de saída", "código de limpeza", "código de teardown", "código de fechamento", "código de saída do gerenciador de contexto", etc.'>alguns passos extras ao finalizar</abbr>.
Para fazer isso, utilize `yield` em vez de `return`, e escreva os passos extras (código) depois.
/// tip | Dica
Garanta utilizar `yield` apenas uma vez por dependência.
///
/// note | Detalhes Técnicos
Qualquer função que possa ser utilizada com:
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> ou
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
pode ser utilizada como uma dependência do **FastAPI**.
Na realidade, o FastAPI utiliza esses dois decoradores internamente.
///
## Uma dependência de banco de dados com `yield` { #a-database-dependency-with-yield }
Por exemplo, você poderia utilizar isso para criar uma sessão do banco de dados, e fechá-la após terminar.
Apenas o código anterior à declaração com `yield` e o código contendo essa declaração são executados antes de criar uma resposta:
{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
O valor gerado (yielded) é o que é injetado nas *operações de rota* e outras dependências:
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
O código após o `yield` é executado após a resposta:
{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
/// tip | Dica
Você pode usar funções assíncronas (`async`) ou funções comuns.
O **FastAPI** saberá o que fazer com cada uma, da mesma forma que as dependências comuns.
///
## Uma dependência com `yield` e `try` { #a-dependency-with-yield-and-try }
Se você utilizar um bloco `try` em uma dependência com `yield`, você irá capturar qualquer exceção que for lançada enquanto a dependência é utilizada.
Por exemplo, se algum código em um certo momento no meio, em outra dependência ou em uma *operação de rota*, fizer um "rollback" de uma transação de banco de dados ou causar qualquer outra exceção, você irá capturar a exceção em sua dependência.
Então, você pode procurar por essa exceção específica dentro da dependência com `except AlgumaExcecao`.
Da mesma forma, você pode utilizar `finally` para garantir que os passos de saída são executados, com ou sem exceções.
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
## Subdependências com `yield` { #sub-dependencies-with-yield }
Você pode ter subdependências e "árvores" de subdependências de qualquer tamanho e forma, e qualquer uma ou todas elas podem utilizar `yield`.
O **FastAPI** garantirá que o "código de saída" em cada dependência com `yield` é executado na ordem correta.
Por exemplo, `dependency_c` pode depender de `dependency_b`, e `dependency_b` depender de `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
E todas elas podem utilizar `yield`.
Neste caso, `dependency_c`, para executar seu código de saída, precisa que o valor de `dependency_b` (nomeado de `dep_b` aqui) continue disponível.
E, por outro lado, `dependency_b` precisa que o valor de `dependency_a` (nomeado de `dep_a`) esteja disponível para executar seu código de saída.
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
Da mesma forma, você pode ter algumas dependências com `yield` e outras com `return` e ter uma relação de dependência entre algumas das duas.
E você poderia ter uma única dependência que precisa de diversas outras dependências com `yield`, etc.
Você pode ter qualquer combinação de dependências que você quiser.
O **FastAPI** se encarrega de executá-las na ordem certa.
/// note | Detalhes Técnicos
Tudo isso funciona graças aos <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">gerenciadores de contexto</a> do Python.
O **FastAPI** utiliza eles internamente para alcançar isso.
///
## Dependências com `yield` e `HTTPException` { #dependencies-with-yield-and-httpexception }
Você viu que pode usar dependências com `yield` e ter blocos `try` que tentam executar algum código e depois executar algum código de saída com `finally`.
Você também pode usar `except` para capturar a exceção que foi levantada e fazer algo com ela.
Por exemplo, você pode levantar uma exceção diferente, como `HTTPException`.
/// tip | Dica
Essa é uma técnica relativamente avançada, e na maioria dos casos você não vai precisar dela, já que você pode levantar exceções (incluindo `HTTPException`) dentro do resto do código da sua aplicação, por exemplo, na *função de operação de rota*.
Mas ela existe para ser utilizada caso você precise. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
Se você quiser capturar exceções e criar uma resposta personalizada com base nisso, crie um [Manipulador de Exceções Customizado](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
## Dependências com `yield` e `except` { #dependencies-with-yield-and-except }
Se você capturar uma exceção com `except` em uma dependência que utilize `yield` e ela não for levantada novamente (ou uma nova exceção for levantada), o FastAPI não será capaz de identificar que houve uma exceção, da mesma forma que aconteceria com Python puro:
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
Neste caso, o cliente irá ver uma resposta *HTTP 500 Internal Server Error* como deveria acontecer, já que não estamos levantando nenhuma `HTTPException` ou coisa parecida, mas o servidor **não terá nenhum log** ou qualquer outra indicação de qual foi o erro. 😱
### Sempre levante (`raise`) em Dependências com `yield` e `except` { #always-raise-in-dependencies-with-yield-and-except }
Se você capturar uma exceção em uma dependência com `yield`, a menos que você esteja levantando outra `HTTPException` ou coisa parecida, **você deve relançar a exceção original**.
Você pode relançar a mesma exceção utilizando `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
Agora o cliente irá receber a mesma resposta *HTTP 500 Internal Server Error*, mas o servidor terá nosso `InternalError` personalizado nos logs. 😎
## Execução de dependências com `yield` { #execution-of-dependencies-with-yield }
A sequência de execução é mais ou menos como esse diagrama. O tempo passa do topo para baixo. E cada coluna é uma das partes interagindo ou executando código.
```mermaid
sequenceDiagram
participant client as Cliente
participant handler as Manipulador de exceções
participant dep as Dep com yield
participant operation as Operação de Rota
participant tasks as Tarefas de Background
Note over client,operation: pode lançar exceções, incluindo HTTPException
client ->> dep: Iniciar requisição
Note over dep: Executar código até o yield
opt lançar Exceção
dep -->> handler: lançar Exceção
handler -->> client: resposta de erro HTTP
end
dep ->> operation: Executar dependência, e.g. sessão de BD
opt raise
operation -->> dep: Lançar exceção (e.g. HTTPException)
opt handle
dep -->> dep: Pode capturar exceções, lançar uma nova HTTPException, lançar outras exceções
end
handler -->> client: resposta de erro HTTP
end
operation ->> client: Retornar resposta ao cliente
Note over client,operation: Resposta já foi enviada, e não pode ser modificada
opt Tarefas
operation -->> tasks: Enviar tarefas de background
end
opt Lançar outra exceção
tasks -->> tasks: Manipula exceções no código da tarefa de background
end
```
/// info | Informação
Apenas **uma resposta** será enviada para o cliente. Ela pode ser uma das respostas de erro, ou então a resposta da *operação de rota*.
Após uma dessas respostas ser enviada, nenhuma outra resposta pode ser enviada.
///
/// tip | Dica
Se você levantar qualquer exceção no código da *função de operação de rota*, ela será passada para as dependências com `yield`, incluindo `HTTPException`. Na maioria dos casos, você vai querer relançar essa mesma exceção ou uma nova a partir da dependência com `yield` para garantir que ela seja tratada adequadamente.
///
## Saída antecipada e `scope` { #early-exit-and-scope }
Normalmente, o código de saída das dependências com `yield` é executado **após a resposta** ser enviada ao cliente.
Mas se você sabe que não precisará usar a dependência depois de retornar da *função de operação de rota*, você pode usar `Depends(scope="function")` para dizer ao FastAPI que deve fechar a dependência depois que a *função de operação de rota* retornar, mas **antes** de a **resposta ser enviada**.
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
`Depends()` recebe um parâmetro `scope` que pode ser:
* `"function"`: iniciar a dependência antes da *função de operação de rota* que trata a requisição, encerrar a dependência depois que a *função de operação de rota* termina, mas **antes** de a resposta ser enviada de volta ao cliente. Assim, a função da dependência será executada **em torno** da *função de operação de rota*.
* `"request"`: iniciar a dependência antes da *função de operação de rota* que trata a requisição (semelhante a quando se usa `"function"`), mas encerrar **depois** que a resposta é enviada de volta ao cliente. Assim, a função da dependência será executada **em torno** do ciclo de **requisição** e resposta.
Se não for especificado e a dependência tiver `yield`, ela terá `scope` igual a `"request"` por padrão.
### `scope` para subdependências { #scope-for-sub-dependencies }
Quando você declara uma dependência com `scope="request"` (o padrão), qualquer subdependência também precisa ter `scope` igual a `"request"`.
Mas uma dependência com `scope` igual a `"function"` pode ter dependências com `scope` igual a `"function"` e com `scope` igual a `"request"`.
Isso porque qualquer dependência precisa conseguir executar seu código de saída antes das subdependências, pois pode ainda precisar usá-las durante seu código de saída.
```mermaid
sequenceDiagram
participant client as Cliente
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Operação de Rota
client ->> dep_req: Iniciar requisição
Note over dep_req: Executar código até o yield
dep_req ->> dep_func: Passar dependência
Note over dep_func: Executar código até o yield
dep_func ->> operation: Executar operação de rota com dependência
operation ->> dep_func: Retornar da operação de rota
Note over dep_func: Executar código após o yield
Note over dep_func: ✅ Dependência fechada
dep_func ->> client: Enviar resposta ao cliente
Note over client: Resposta enviada
Note over dep_req: Executar código após o yield
Note over dep_req: ✅ Dependência fechada
```
## Dependências com `yield`, `HTTPException`, `except` e Tarefas de Background { #dependencies-with-yield-httpexception-except-and-background-tasks }
Dependências com `yield` evoluíram ao longo do tempo para cobrir diferentes casos de uso e corrigir alguns problemas.
Se você quiser ver o que mudou em diferentes versões do FastAPI, você pode ler mais sobre isso no guia avançado, em [Dependências Avançadas - Dependências com `yield`, `HTTPException`, `except` e Tarefas de Background](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}.
## Gerenciadores de contexto { #context-managers }
### O que são "Gerenciadores de Contexto" { #what-are-context-managers }
"Gerenciadores de Contexto" são qualquer um dos objetos Python que podem ser utilizados com a declaração `with`.
Por exemplo, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">você pode utilizar `with` para ler um arquivo</a>:
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Por baixo dos panos, o código `open("./somefile.txt")` cria um objeto que é chamado de "Gerenciador de Contexto".
Quando o bloco `with` finaliza, ele se certifica de fechar o arquivo, mesmo que tenha ocorrido alguma exceção.
Quando você cria uma dependência com `yield`, o **FastAPI** irá criar um gerenciador de contexto internamente para ela, e combiná-lo com algumas outras ferramentas relacionadas.
### Utilizando gerenciadores de contexto em dependências com `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Atenção
Isso é uma ideia mais ou menos "avançada".
Se você está apenas iniciando com o **FastAPI** você pode querer pular isso por enquanto.
///
Em Python, você pode criar Gerenciadores de Contexto ao <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">criar uma classe com dois métodos: `__enter__()` e `__exit__()`</a>.
Você também pode usá-los dentro de dependências com `yield` do **FastAPI** ao utilizar
`with` ou `async with` dentro da função da dependência:
{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
/// tip | Dica
Outra forma de criar um gerenciador de contexto é utilizando:
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> ou
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
Para decorar uma função com um único `yield`.
Isso é o que o **FastAPI** usa internamente para dependências com `yield`.
Mas você não precisa usar esses decoradores para as dependências do FastAPI (e você não deveria).
O FastAPI irá fazer isso para você internamente.
///
@@ -0,0 +1,16 @@
# Dependências Globais { #global-dependencies }
Para alguns tipos de aplicação você pode querer adicionar dependências para toda a aplicação.
De forma semelhante a [adicionar `dependencies` aos *decoradores de operação de rota*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, você pode adicioná-las à aplicação `FastAPI`.
Nesse caso, elas serão aplicadas a todas as *operações de rota* da aplicação:
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
E todos os conceitos apresentados na seção sobre [adicionar `dependencies` aos *decoradores de operação de rota*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} ainda se aplicam, mas nesse caso, para todas as *operações de rota* da aplicação.
## Dependências para conjuntos de *operações de rota* { #dependencies-for-groups-of-path-operations }
Mais para a frente, quando você ler sobre como estruturar aplicações maiores ([Aplicações Maiores - Múltiplos Arquivos](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possivelmente com múltiplos arquivos, você irá aprender a declarar um único parâmetro `dependencies` para um conjunto de *operações de rota*.
+250
View File
@@ -0,0 +1,250 @@
# Dependências { #dependencies }
O **FastAPI** possui um poderoso, mas intuitivo sistema de **<abbr title="também conhecidos como: componentes, recursos, provedores, serviços, injetáveis">Injeção de Dependência</abbr>**.
Esse sistema foi pensado para ser fácil de usar, e permitir que qualquer desenvolvedor possa integrar facilmente outros componentes ao **FastAPI**.
## O que é "Injeção de Dependência" { #what-is-dependency-injection }
**"Injeção de Dependência"** no mundo da programação significa, que existe uma maneira de declarar no seu código (nesse caso, suas *funções de operação de rota*) para declarar as coisas que ele precisa para funcionar e que serão utilizadas: "dependências".
Então, esse sistema (nesse caso o **FastAPI**) se encarrega de fazer o que for preciso para fornecer essas dependências para o código ("injetando" as dependências).
Isso é bastante útil quando você precisa:
* Definir uma lógica compartilhada (mesmo formato de código repetidamente).
* Compartilhar conexões com banco de dados.
* Aplicar regras de segurança, autenticação, papéis de usuários, etc.
* E muitas outras coisas...
Tudo isso, enquanto minimizamos a repetição de código.
## Primeiros passos { #first-steps }
Vamos ver um exemplo simples. Tão simples que não será muito útil, por enquanto.
Mas dessa forma podemos focar em como o sistema de **Injeção de Dependência** funciona.
### Criando uma dependência, ou "injetável" { #create-a-dependency-or-dependable }
Primeiro vamos focar na dependência.
Ela é apenas uma função que pode receber os mesmos parâmetros de uma *função de operação de rota*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
E pronto.
**2 linhas**.
E com a mesma forma e estrutura de todas as suas *funções de operação de rota*.
Você pode pensar nela como uma *função de operação de rota* sem o "decorador" (sem a linha `@app.get("/some-path")`).
E com qualquer retorno que você desejar.
Neste caso, a dependência espera por:
* Um parâmetro de consulta opcional `q` do tipo `str`.
* Um parâmetro de consulta opcional `skip` do tipo `int`, e igual a `0` por padrão.
* Um parâmetro de consulta opcional `limit` do tipo `int`, e igual a `100` por padrão.
E então retorna um `dict` contendo esses valores.
/// info | Informação
FastAPI passou a suportar a notação `Annotated` (e começou a recomendá-la) na versão 0.95.0.
Se você utiliza uma versão anterior, ocorrerão erros ao tentar utilizar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} para pelo menos 0.95.1 antes de usar `Annotated`.
///
### Importando `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Declarando a dependência, no "dependente" { #declare-the-dependency-in-the-dependant }
Da mesma forma que você utiliza `Body`, `Query`, etc. Como parâmetros de sua *função de operação de rota*, utilize `Depends` com um novo parâmetro:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Ainda que `Depends` seja utilizado nos parâmetros da função da mesma forma que `Body`, `Query`, etc, `Depends` funciona de uma forma um pouco diferente.
Você fornece um único parâmetro para `Depends`.
Esse parâmetro deve ser algo como uma função.
Você **não chama a função** diretamente (não adicione os parênteses no final), apenas a passe como parâmetro de `Depends()`.
E essa função vai receber os parâmetros da mesma forma que uma *função de operação de rota*.
/// tip | Dica
Você verá quais outras "coisas", além de funções, podem ser usadas como dependências no próximo capítulo.
///
Sempre que uma nova requisição for realizada, o **FastAPI** se encarrega de:
* Chamar sua dependência ("injetável") com os parâmetros corretos.
* Obter o resultado da função.
* Atribuir esse resultado para o parâmetro em sua *função de operação de rota*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Assim, você escreve um código compartilhado apenas uma vez e o **FastAPI** se encarrega de chamá-lo em suas *operações de rota*.
/// check | Verifique
Perceba que você não precisa criar uma classe especial e enviar a dependência para algum outro lugar em que o **FastAPI** a "registre" ou realize qualquer operação similar.
Você apenas envia para `Depends` e o **FastAPI** sabe como fazer o resto.
///
## Compartilhando dependências `Annotated` { #share-annotated-dependencies }
Nos exemplos acima, você pode ver que existe uma pequena **duplicação de código**.
Quando você precisa utilizar a dependência `common_parameters()`, você precisa escrever o parâmetro inteiro com uma anotação de tipo e `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Mas como estamos utilizando `Annotated`, podemos guardar esse valor `Annotated` em uma variável e utilizá-la em múltiplos locais:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Dica
Isso é apenas Python padrão, essa funcionalidade é chamada de "type alias", e na verdade não é específica ao **FastAPI**.
Mas como o **FastAPI** se baseia em convenções do Python, incluindo `Annotated`, você pode incluir esse truque no seu código. 😎
///
As dependências continuarão funcionando como esperado, e a **melhor parte** é que a **informação sobre o tipo é preservada**, o que signfica que seu editor de texto ainda irá incluir **preenchimento automático**, **visualização de erros**, etc. O mesmo vale para ferramentas como `mypy`.
Isso é especialmente útil para uma **base de código grande** onde **as mesmas dependências** são utilizadas repetidamente em **muitas *operações de rota***.
## `Async` ou não, eis a questão { #to-async-or-not-to-async }
Como as dependências também serão chamadas pelo **FastAPI** (da mesma forma que *funções de operação de rota*), as mesmas regras se aplicam ao definir suas funções.
Você pode utilizar `async def` ou apenas `def`.
E você pode declarar dependências utilizando `async def` dentro de *funções de operação de rota* definidas com `def`, ou declarar dependências com `def` e utilizar dentro de *funções de operação de rota* definidas com `async def`, etc.
Não faz diferença. O **FastAPI** sabe o que fazer.
/// note | Nota
Caso você não conheça, veja em [Async: *"Com Pressa?"*](../../async.md#in-a-hurry){.internal-link target=_blank} a sessão acerca de `async` e `await` na documentação.
///
## Integrando com OpenAPI { #integrated-with-openapi }
Todas as declarações de requisições, validações e requisitos para suas dependências (e sub-dependências) serão integradas em um mesmo esquema OpenAPI.
Então, a documentação interativa também terá toda a informação sobre essas dependências:
<img src="/img/tutorial/dependencies/image01.png">
## Caso de Uso Simples { #simple-usage }
Se você parar para ver, *funções de operação de rota* são declaradas para serem usadas sempre que uma *rota* e uma *operação* se encaixam, e então o **FastAPI** se encarrega de chamar a função correspondente com os argumentos corretos, extraindo os dados da requisição.
Na verdade, todos (ou a maioria) dos frameworks web funcionam da mesma forma.
Você nunca chama essas funções diretamente. Elas são chamadas pelo framework utilizado (nesse caso, **FastAPI**).
Com o Sistema de Injeção de Dependência, você também pode informar ao **FastAPI** que sua *função de operação de rota* também "depende" em algo a mais que deve ser executado antes de sua *função de operação de rota*, e o **FastAPI** se encarrega de executar e "injetar" os resultados.
Outros termos comuns para essa mesma ideia de "injeção de dependência" são:
* recursos
* provedores
* serviços
* injetáveis
* componentes
## Plug-ins em **FastAPI** { #fastapi-plug-ins }
Integrações e "plug-ins" podem ser construídos com o sistema de **Injeção de Dependência**. Mas na verdade, **não há necessidade de criar "plug-ins"**, já que utilizando dependências é possível declarar um número infinito de integrações e interações que se tornam disponíveis para as suas *funções de operação de rota*.
E as dependências pode ser criadas de uma forma bastante simples e intuitiva que permite que você importe apenas os pacotes Python que forem necessários, e integrá-los com as funções de sua API em algumas linhas de código, *literalmente*.
Você verá exemplos disso nos próximos capítulos, acerca de bancos de dados relacionais e NoSQL, segurança, etc.
## Compatibilidade do **FastAPI** { #fastapi-compatibility }
A simplicidade do sistema de injeção de dependência do **FastAPI** faz ele compatível com:
* todos os bancos de dados relacionais
* bancos de dados NoSQL
* pacotes externos
* APIs externas
* sistemas de autenticação e autorização
* istemas de monitoramento de uso para APIs
* sistemas de injeção de dados de resposta
* etc.
## Simples e Poderoso { #simple-and-powerful }
Mesmo que o sistema hierárquico de injeção de dependência seja simples de definir e utilizar, ele ainda é bastante poderoso.
Você pode definir dependências que por sua vez definem suas próprias dependências.
No fim, uma árvore hierárquica de dependências é criadas, e o sistema de **Injeção de Dependência** toma conta de resolver todas essas dependências (e as sub-dependências delas) para você, e provê (injeta) os resultados em cada passo.
Por exemplo, vamos supor que você possua 4 endpoints na sua API (*operações de rota*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
Você poderia adicionar diferentes requisitos de permissão para cada um deles utilizando apenas dependências e sub-dependências:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integração com **OpenAPI** { #integrated-with-openapi_1 }
Todas essas dependências, ao declarar os requisitos para suas *operações de rota*, também adicionam parâmetros, validações, etc.
O **FastAPI** se encarrega de adicionar tudo isso ao esquema OpenAPI, para que seja mostrado nos sistemas de documentação interativa.
@@ -0,0 +1,105 @@
# Subdependências { #sub-dependencies }
Você pode criar dependências que possuem **subdependências**.
Elas podem ter o nível de **profundidade** que você achar necessário.
O **FastAPI** se encarrega de resolver essas dependências.
## Primeira dependência "dependable" { #first-dependency-dependable }
Você pode criar uma primeira dependência ("dependable") dessa forma:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Esse código declara um parâmetro de consulta opcional, `q`, com o tipo `str`, e então retorna esse parâmetro.
Isso é bastante simples (e não muito útil), mas irá nos ajudar a focar em como as subdependências funcionam.
## Segunda dependência, "dependable" e "dependente" { #second-dependency-dependable-and-dependant }
Então, você pode criar uma outra função para uma dependência (um "dependable") que ao mesmo tempo declara sua própria dependência (o que faz dela um "dependente" também):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Vamos focar nos parâmetros declarados:
* Mesmo que essa função seja uma dependência ("dependable") por si mesma, ela também declara uma outra dependência (ela "depende" de outra coisa).
* Ela depende do `query_extractor`, e atribui o valor retornado pela função ao parâmetro `q`.
* Ela também declara um cookie opcional `last_query`, do tipo `str`.
* Se o usuário não passou nenhuma consulta `q`, a última consulta é utilizada, que foi salva em um cookie anteriormente.
## Utilizando a dependência { #use-the-dependency }
Então podemos utilizar a dependência com:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Informação
Perceba que nós estamos declarando apenas uma dependência na *função de operação de rota*, em `query_or_cookie_extractor`.
Mas o **FastAPI** saberá que precisa solucionar `query_extractor` primeiro, para passar o resultado para `query_or_cookie_extractor` enquanto chama a função.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Utilizando a mesma dependência múltiplas vezes { #using-the-same-dependency-multiple-times }
Se uma de suas dependências é declarada várias vezes para a mesma *operação de rota*, por exemplo, múltiplas dependências com uma mesma subdependência, o **FastAPI** irá chamar essa subdependência uma única vez para cada requisição.
E o valor retornado é salvo em um <abbr title="Um utilitário/sistema para armazenar valores calculados/gerados para serem reutilizados em vez de computá-los novamente.">"cache"</abbr> e repassado para todos os "dependentes" que precisam dele em uma requisição específica, em vez de chamar a dependência múltiplas vezes para uma mesma requisição.
Em um cenário avançado onde você precise que a dependência seja calculada em cada passo (possivelmente várias vezes) de uma requisição em vez de utilizar o valor em "cache", você pode definir o parâmetro `use_cache=False` em `Depends`:
//// tab | Python 3.8+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Recapitulando { #recap }
Com exceção de todas as palavras complicadas usadas aqui, o sistema de **Injeção de Dependência** é bastante simples.
Consiste apenas de funções que parecem idênticas a *funções de operação de rota*.
Mas ainda assim, é bastante poderoso, e permite que você declare grafos (árvores) de dependências com uma profundidade arbitrária.
/// tip | Dica
Tudo isso pode não parecer muito útil com esses exemplos.
Mas você verá o quão útil isso é nos capítulos sobre **segurança**.
E você também verá a quantidade de código que você não precisara escrever.
///
+35
View File
@@ -0,0 +1,35 @@
# Codificador Compatível com JSON { #json-compatible-encoder }
Existem alguns casos em que você pode precisar converter um tipo de dados (como um modelo Pydantic) para algo compatível com JSON (como um `dict`, `list`, etc).
Por exemplo, se você precisar armazená-lo em um banco de dados.
Para isso, **FastAPI** fornece uma função `jsonable_encoder()`.
## Usando a função `jsonable_encoder` { #using-the-jsonable-encoder }
Vamos imaginar que você tenha um banco de dados `fake_db` que recebe apenas dados compatíveis com JSON.
Por exemplo, ele não recebe objetos `datetime`, pois estes objetos não são compatíveis com JSON.
Então, um objeto `datetime` teria que ser convertido em um `str` contendo os dados no <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">formato ISO</a>.
Da mesma forma, este banco de dados não receberia um modelo Pydantic (um objeto com atributos), apenas um `dict`.
Você pode usar a função `jsonable_encoder` para resolver isso.
A função recebe um objeto, como um modelo Pydantic e retorna uma versão compatível com JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
Neste exemplo, ele converteria o modelo Pydantic em um `dict`, e o `datetime` em um `str`.
O resultado de chamar a função é algo que pode ser codificado com o padrão do Python <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.
A função não retorna um grande `str` contendo os dados no formato JSON (como uma string). Mas sim, retorna uma estrutura de dados padrão do Python (por exemplo, um `dict`) com valores e subvalores compatíveis com JSON.
/// note | Nota
`jsonable_encoder` é realmente usado pelo **FastAPI** internamente para converter dados. Mas também é útil em muitos outros cenários.
///
+62
View File
@@ -0,0 +1,62 @@
# Tipos de dados extras { #extra-data-types }
Até agora, você tem usado tipos de dados comuns, tais como:
* `int`
* `float`
* `str`
* `bool`
Mas você também pode usar tipos de dados mais complexos.
E você ainda terá os mesmos recursos que viu até agora:
* Ótimo suporte do editor.
* Conversão de dados das requisições recebidas.
* Conversão de dados para os dados da resposta.
* Validação de dados.
* Anotação e documentação automáticas.
## Outros tipos de dados { #other-data-types }
Aqui estão alguns dos tipos de dados adicionais que você pode usar:
* `UUID`:
* Um "Identificador Universalmente Único" padrão, comumente usado como ID em muitos bancos de dados e sistemas.
* Em requisições e respostas será representado como uma `str`.
* `datetime.datetime`:
* O `datetime.datetime` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* O `datetime.date` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `2008-09-15`.
* `datetime.time`:
* O `datetime.time` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `14:23:55.003`.
* `datetime.timedelta`:
* O `datetime.timedelta` do Python.
* Em requisições e respostas será representado como um `float` de segundos totais.
* O Pydantic também permite representá-lo como uma "codificação ISO 8601 diferença de tempo", <a href="https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers" class="external-link" target="_blank">cheque a documentação para mais informações</a>.
* `frozenset`:
* Em requisições e respostas, será tratado da mesma forma que um `set`:
* Nas requisições, uma lista será lida, eliminando duplicadas e convertendo-a em um `set`.
* Nas respostas, o `set` será convertido para uma `list`.
* O esquema gerado vai especificar que os valores do `set` são unicos (usando o `uniqueItems` do JSON Schema).
* `bytes`:
* O `bytes` padrão do Python.
* Em requisições e respostas será representado como uma `str`.
* O esquema gerado vai especificar que é uma `str` com o "formato" `binary`.
* `Decimal`:
* O `Decimal` padrão do Python.
* Em requisições e respostas será representado como um `float`.
* Você pode checar todos os tipos de dados válidos do Pydantic aqui: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Tipos de dados do Pydantic</a>.
## Exemplo { #example }
Aqui está um exemplo de *operação de rota* com parâmetros utilizando-se de alguns dos tipos acima.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Note que os parâmetros dentro da função tem seu tipo de dados natural, e você pode, por exemplo, realizar manipulações normais de data, como:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
+219
View File
@@ -0,0 +1,219 @@
# Modelos Adicionais { #extra-models }
Continuando com o exemplo anterior, será comum ter mais de um modelo relacionado.
Isso é especialmente o caso para modelos de usuários, porque:
* O **modelo de entrada** precisa ser capaz de ter uma senha.
* O **modelo de saída** não deve ter uma senha.
* O **modelo de banco de dados** provavelmente precisaria ter uma senha com hash.
/// danger | Cuidado
Nunca armazene senhas em texto simples dos usuários. Sempre armazene uma "hash segura" que você pode verificar depois.
Se não souber, você aprenderá o que é uma "senha hash" nos [capítulos de segurança](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}.
///
## Múltiplos modelos { #multiple-models }
Aqui está uma ideia geral de como os modelos poderiam parecer com seus campos de senha e os lugares onde são usados:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
/// info | Informação
No Pydantic v1 o método se chamava `.dict()`, ele foi descontinuado (mas ainda é suportado) no Pydantic v2 e renomeado para `.model_dump()`.
Os exemplos aqui usam `.dict()` por compatibilidade com o Pydantic v1, mas você deve usar `.model_dump()` se puder usar o Pydantic v2.
///
### Sobre `**user_in.dict()` { #about-user-in-dict }
#### O `.dict()` do Pydantic { #pydantics-dict }
`user_in` é um modelo Pydantic da classe `UserIn`.
Os modelos Pydantic possuem um método `.dict()` que retorna um `dict` com os dados do modelo.
Então, se criarmos um objeto Pydantic `user_in` como:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
e depois chamarmos:
```Python
user_dict = user_in.dict()
```
agora temos um `dict` com os dados na variável `user_dict` (é um `dict` em vez de um objeto de modelo Pydantic).
E se chamarmos:
```Python
print(user_dict)
```
teríamos um `dict` Python com:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Desembrulhando um `dict` { #unpacking-a-dict }
Se tomarmos um `dict` como `user_dict` e passarmos para uma função (ou classe) com `**user_dict`, o Python irá "desembrulhá-lo". Ele passará as chaves e valores do `user_dict` diretamente como argumentos chave-valor.
Então, continuando com o `user_dict` acima, escrevendo:
```Python
UserInDB(**user_dict)
```
Resultaria em algo equivalente a:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Ou mais exatamente, usando `user_dict` diretamente, com qualquer conteúdo que ele possa ter no futuro:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Um modelo Pydantic a partir do conteúdo de outro { #a-pydantic-model-from-the-contents-of-another }
Como no exemplo acima, obtivemos o `user_dict` a partir do `user_in.dict()`, este código:
```Python
user_dict = user_in.dict()
UserInDB(**user_dict)
```
seria equivalente a:
```Python
UserInDB(**user_in.dict())
```
...porque `user_in.dict()` é um `dict`, e depois fazemos o Python "desembrulhá-lo" passando-o para `UserInDB` precedido por `**`.
Então, obtemos um modelo Pydantic a partir dos dados em outro modelo Pydantic.
#### Desembrulhando um `dict` e palavras-chave extras { #unpacking-a-dict-and-extra-keywords }
E, então, adicionando o argumento de palavra-chave extra `hashed_password=hashed_password`, como em:
```Python
UserInDB(**user_in.dict(), hashed_password=hashed_password)
```
...acaba sendo como:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Atenção
As funções adicionais de suporte `fake_password_hasher` e `fake_save_user` servem apenas para demonstrar um fluxo possível dos dados, mas é claro que elas não fornecem segurança real.
///
## Reduzir duplicação { #reduce-duplication }
Reduzir a duplicação de código é uma das ideias principais no **FastAPI**.
A duplicação de código aumenta as chances de bugs, problemas de segurança, problemas de desincronização de código (quando você atualiza em um lugar, mas não em outros), etc.
E esses modelos estão compartilhando muitos dos dados e duplicando nomes e tipos de atributos.
Nós poderíamos fazer melhor.
Podemos declarar um modelo `UserBase` que serve como base para nossos outros modelos. E então podemos fazer subclasses desse modelo que herdam seus atributos (declarações de tipo, validação, etc.).
Toda conversão de dados, validação, documentação, etc. ainda funcionará normalmente.
Dessa forma, podemos declarar apenas as diferenças entre os modelos (com `password` em texto claro, com `hashed_password` e sem senha):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` ou `anyOf` { #union-or-anyof }
Você pode declarar uma resposta como o `Union` de dois ou mais tipos, o que significa que a resposta seria qualquer um deles.
Isso será definido no OpenAPI com `anyOf`.
Para fazer isso, use a anotação de tipo padrão do Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
/// note | Nota
Ao definir um <a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a>, inclua o tipo mais específico primeiro, seguido pelo tipo menos específico. No exemplo abaixo, o tipo mais específico `PlaneItem` vem antes de `CarItem` em `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` no Python 3.10 { #union-in-python-3-10 }
Neste exemplo, passamos `Union[PlaneItem, CarItem]` como o valor do argumento `response_model`.
Dado que estamos passando-o como um **valor para um argumento** em vez de colocá-lo em uma **anotação de tipo**, precisamos usar `Union` mesmo no Python 3.10.
Se estivesse em uma anotação de tipo, poderíamos ter usado a barra vertical, como:
```Python
some_variable: PlaneItem | CarItem
```
Mas se colocarmos isso na atribuição `response_model=PlaneItem | CarItem`, teríamos um erro, pois o Python tentaria executar uma **operação inválida** entre `PlaneItem` e `CarItem` em vez de interpretar isso como uma anotação de tipo.
## Lista de modelos { #list-of-models }
Da mesma forma, você pode declarar respostas de listas de objetos.
Para isso, use o padrão Python `typing.List` (ou simplesmente `list` no Python 3.9 e superior):
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
## Resposta com `dict` arbitrário { #response-with-arbitrary-dict }
Você também pode declarar uma resposta usando um simples `dict` arbitrário, declarando apenas o tipo das chaves e valores, sem usar um modelo Pydantic.
Isso é útil se você não souber os nomes de campo / atributo válidos (que seriam necessários para um modelo Pydantic) antecipadamente.
Neste caso, você pode usar `typing.Dict` (ou simplesmente `dict` no Python 3.9 e superior):
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
## Recapitulação { #recap }
Use vários modelos Pydantic e herde livremente para cada caso.
Não é necessário ter um único modelo de dados por entidade se essa entidade precisar ter diferentes "estados". No caso da "entidade" de usuário com um estado que inclui `password`, `password_hash` e sem senha.
+323
View File
@@ -0,0 +1,323 @@
# Primeiros Passos { #first-steps }
O arquivo FastAPI mais simples pode se parecer com:
{* ../../docs_src/first_steps/tutorial001.py *}
Copie o conteúdo para um arquivo `main.py`.
Execute o servidor:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
Na saída, há uma linha com algo como:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Essa linha mostra a URL onde a sua aplicação está sendo servida, na sua máquina local.
### Confira { #check-it }
Abra o seu navegador em <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Você verá essa resposta em JSON:
```JSON
{"message": "Hello World"}
```
### Documentação Interativa de APIs { #interactive-api-docs }
Agora vá para <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Você verá a documentação interativa automática da API (fornecida por <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Documentação Alternativa de APIs { #alternative-api-docs }
E agora, vá para <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Você verá a documentação alternativa automática (fornecida por <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI { #openapi }
O **FastAPI** gera um "*schema*" com toda a sua API usando o padrão **OpenAPI** para definir APIs.
#### "*Schema*" { #schema }
Um "*schema*" é uma definição ou descrição de algo. Não o código que o implementa, mas apenas uma descrição abstrata.
#### API "*schema*" { #api-schema }
Nesse caso, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> é uma especificação que determina como definir um *schema* da sua API.
Esta definição de *schema* inclui os paths da sua API, os parâmetros possíveis que eles usam, etc.
#### "*Schema*" de dados { #data-schema }
O termo "*schema*" também pode se referir à forma de alguns dados, como um conteúdo JSON.
Nesse caso, significaria os atributos JSON e os tipos de dados que eles possuem, etc.
#### OpenAPI e JSON Schema { #openapi-and-json-schema }
OpenAPI define um *schema* de API para sua API. E esse *schema* inclui definições (ou "*schemas*") dos dados enviados e recebidos por sua API usando **JSON Schema**, o padrão para *schemas* de dados JSON.
#### Verifique o `openapi.json` { #check-the-openapi-json }
Se você está curioso(a) sobre a aparência do *schema* bruto OpenAPI, o FastAPI gera automaticamente um JSON (*schema*) com as descrições de toda a sua API.
Você pode ver isso diretamente em: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Ele mostrará um JSON começando com algo como:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Para que serve o OpenAPI { #what-is-openapi-for }
O *schema* OpenAPI é o que possibilita os dois sistemas de documentação interativos mostrados.
E existem dezenas de alternativas, todas baseadas em OpenAPI. Você pode facilmente adicionar qualquer uma dessas alternativas à sua aplicação criada com **FastAPI**.
Você também pode usá-lo para gerar código automaticamente para clientes que se comunicam com sua API. Por exemplo, aplicativos front-end, móveis ou IoT.
## Recapitulando, passo a passo { #recap-step-by-step }
### Passo 1: importe `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
`FastAPI` é uma classe Python que fornece todas as funcionalidades para sua API.
/// note | Detalhes Técnicos
`FastAPI` é uma classe que herda diretamente de `Starlette`.
Você pode usar todas as funcionalidades do <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> com `FastAPI` também.
///
### Passo 2: crie uma "instância" de `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
Aqui, a variável `app` será uma "instância" da classe `FastAPI`.
Este será o principal ponto de interação para criar toda a sua API.
### Passo 3: crie uma operação de rota { #step-3-create-a-path-operation }
#### Path { #path }
"Path" aqui se refere à última parte da URL, começando do primeiro `/`.
Então, em uma URL como:
```
https://example.com/items/foo
```
...o path seria:
```
/items/foo
```
/// info | Informação
Um "path" também é comumente chamado de "endpoint" ou de "rota".
///
Ao construir uma API, o "path" é a principal forma de separar "preocupações" e "recursos".
#### Operação { #operation }
"Operação" aqui se refere a um dos "métodos" HTTP.
Um dos:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...e os mais exóticos:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
No protocolo HTTP, você pode se comunicar com cada path usando um (ou mais) desses "métodos".
---
Ao construir APIs, você normalmente usa esses métodos HTTP para executar uma ação específica.
Normalmente você usa:
* `POST`: para criar dados.
* `GET`: para ler dados.
* `PUT`: para atualizar dados.
* `DELETE`: para deletar dados.
Portanto, no OpenAPI, cada um dos métodos HTTP é chamado de "operação".
Vamos chamá-los de "**operações**" também.
#### Defina um decorador de operação de rota { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
O `@app.get("/")` diz ao **FastAPI** que a função logo abaixo é responsável por tratar as requisições que vão para:
* o path `/`
* usando uma <abbr title="um método HTTP GET">operação <code>get</code></abbr>
/// info | Informações sobre `@decorator`
Essa sintaxe `@alguma_coisa` em Python é chamada de "decorador".
Você o coloca em cima de uma função. Como um chapéu decorativo (acho que é daí que vem o termo).
Um "decorador" pega a função abaixo e faz algo com ela.
Em nosso caso, este decorador informa ao **FastAPI** que a função abaixo corresponde ao **path** `/` com uma **operação** `get`.
É o "**decorador de operação de rota**".
///
Você também pode usar as outras operações:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
E os mais exóticos:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Dica
Você está livre para usar cada operação (método HTTP) como desejar.
O **FastAPI** não impõe nenhum significado específico.
As informações aqui são apresentadas como uma orientação, não uma exigência.
Por exemplo, ao usar GraphQL, você normalmente executa todas as ações usando apenas operações `POST`.
///
### Passo 4: defina a função de operação de rota { #step-4-define-the-path-operation-function }
Esta é a nossa "**função de operação de rota**":
* **path**: é `/`.
* **operação**: é `get`.
* **função**: é a função abaixo do "decorador" (abaixo do `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
Esta é uma função Python.
Ela será chamada pelo **FastAPI** sempre que receber uma requisição para a URL "`/`" usando uma operação `GET`.
Neste caso, é uma função `async`.
---
Você também pode defini-la como uma função normal em vez de `async def`:
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
/// note | Nota
Se você não sabe a diferença, verifique o [Async: *"Com pressa?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
///
### Passo 5: retorne o conteúdo { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
Você pode retornar um `dict`, `list` e valores singulares como `str`, `int`, etc.
Você também pode devolver modelos Pydantic (você verá mais sobre isso mais tarde).
Existem muitos outros objetos e modelos que serão convertidos automaticamente para JSON (incluindo ORMs, etc). Tente usar seus favoritos, é altamente provável que já sejam compatíveis.
## Recapitulando { #recap }
* Importe `FastAPI`.
* Crie uma instância do `app`.
* Escreva um **decorador de operação de rota** usando decoradores como `@app.get("/")`.
* Defina uma **função de operação de rota**; por exemplo, `def root(): ...`.
* Execute o servidor de desenvolvimento usando o comando `fastapi dev`.
+254
View File
@@ -0,0 +1,254 @@
# Manipulação de erros { #handling-errors }
Há diversas situações em que você precisa notificar um erro a um cliente que está utilizando a sua API.
Esse cliente pode ser um browser com um frontend, o código de outra pessoa, um dispositivo IoT, etc.
Pode ser que você precise comunicar ao cliente que:
* O cliente não tem direitos para realizar aquela operação.
* O cliente não tem acesso aquele recurso.
* O item que o cliente está tentando acessar não existe.
* etc.
Nesses casos, você normalmente retornaria um **HTTP status code** próximo ao status code na faixa do status code **400** (do 400 ao 499).
Isso é bastante similar ao caso do HTTP status code 200 (do 200 ao 299). Esses "200" status codes significam que, de algum modo, houve sucesso na requisição.
Os status codes na faixa dos 400 significam que houve um erro por parte do cliente.
Você se lembra de todos aqueles erros (e piadas) a respeito do "**404 Not Found**"?
## Use o `HTTPException` { #use-httpexception }
Para retornar ao cliente *responses* HTTP com erros, use o `HTTPException`.
### Import `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
### Lance o `HTTPException` no seu código. { #raise-an-httpexception-in-your-code }
`HTTPException`, ao fundo, nada mais é do que a conjunção entre uma exceção comum do Python e informações adicionais relevantes para APIs.
E porque é uma exceção do Python, você não **retorna** (return) o `HTTPException`, você lança o (raise) no seu código.
Isso também significa que, se você está escrevendo uma função de utilidade, a qual você está chamando dentro da sua função de operações de caminhos, e você lança o `HTTPException` dentro da função de utilidade, o resto do seu código não será executado dentro da função de operações de caminhos. Ao contrário, o `HTTPException` irá finalizar a requisição no mesmo instante e enviará o erro HTTP oriundo do `HTTPException` para o cliente.
O benefício de lançar uma exceção em vez de retornar um valor ficará mais evidente na seção sobre Dependências e Segurança.
Neste exemplo, quando o cliente pede, na requisição, por um item cujo ID não existe, a exceção com o status code `404` é lançada:
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
### A response resultante { #the-resulting-response }
Se o cliente faz uma requisição para `http://example.com/items/foo` (um `item_id` `"foo"`), esse cliente receberá um HTTP status code 200, e uma resposta JSON:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Mas se o cliente faz uma requisição para `http://example.com/items/bar` (ou seja, um não existente `item_id "bar"`), esse cliente receberá um HTTP status code 404 (o erro "não encontrado" — *not found error*), e uma resposta JSON:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Dica
Quando você lançar um `HTTPException`, você pode passar qualquer valor convertível em JSON como parâmetro de `detail`, e não apenas `str`.
Você pode passar um `dict` ou um `list`, etc.
Esses tipos de dados são manipulados automaticamente pelo **FastAPI** e convertidos em JSON.
///
## Adicione headers customizados { #add-custom-headers }
Há certas situações em que é bastante útil poder adicionar headers customizados no HTTP error. Exemplo disso seria adicionar headers customizados para tipos de segurança.
Você provavelmente não precisará utilizar esses headers diretamente no seu código.
Mas caso você precise, para um cenário mais complexo, você pode adicionar headers customizados:
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
## Instale manipuladores de exceções customizados { #install-custom-exception-handlers }
Você pode adicionar manipuladores de exceção customizados com <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">a mesma seção de utilidade de exceções presentes no Starlette</a>
Digamos que você tenha uma exceção customizada `UnicornException` que você (ou uma biblioteca que você use) precise lançar (`raise`).
Nesse cenário, se você precisa manipular essa exceção de modo global com o FastAPI, você pode adicionar um manipulador de exceção customizada com `@app.exception_handler()`.
{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
Nesse cenário, se você fizer uma requisição para `/unicorns/yolo`, a *operação de caminho* vai lançar (`raise`) o `UnicornException`.
Essa exceção será manipulada, contudo, pelo `unicorn_exception_handler`.
Dessa forma você receberá um erro "limpo", com o HTTP status code `418` e um JSON com o conteúdo:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Detalhes Técnicos
Você também pode usar `from starlette.requests import Request` and `from starlette.responses import JSONResponse`.
**FastAPI** disponibiliza o mesmo `starlette.responses` através do `fastapi.responses` por conveniência ao desenvolvedor. Contudo, a maior parte das respostas disponíveis vem diretamente do Starlette. O mesmo acontece com o `Request`.
///
## Sobrescreva os manipuladores de exceções padrão { #override-the-default-exception-handlers }
**FastAPI** tem alguns manipuladores padrão de exceções.
Esses manipuladores são os responsáveis por retornar o JSON padrão de respostas quando você lança (`raise`) o `HTTPException` e quando a requisição tem dados invalidos.
Você pode sobrescrever esses manipuladores de exceção com os seus próprios manipuladores.
### Sobrescreva exceções de validação da requisição { #override-request-validation-exceptions }
Quando a requisição contém dados inválidos, **FastAPI** internamente lança para o `RequestValidationError`.
E também inclui um manipulador de exceções padrão para ele.
Para sobrescrevê-lo, importe o `RequestValidationError` e use-o com o `@app.exception_handler(RequestValidationError)` para decorar o manipulador de exceções.
O manipulador de exceções receberá um `Request` e a exceção.
{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
Se você for ao `/items/foo`, em vez de receber o JSON padrão com o erro:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
você receberá a versão em texto:
```
1 validation error
path -> item_id
value is not a valid integer (type=type_error.integer)
```
#### `RequestValidationError` vs `ValidationError` { #requestvalidationerror-vs-validationerror }
/// warning | Atenção
Você pode pular estes detalhes técnicos caso eles não sejam importantes para você neste momento.
///
`RequestValidationError` é uma subclasse do <a href="https://docs.pydantic.dev/latest/concepts/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a> existente no Pydantic.
**FastAPI** faz uso dele para que você veja o erro no seu log, caso você utilize um modelo de Pydantic em `response_model`, e seus dados tenham erro.
Contudo, o cliente ou usuário não terão acesso a ele. Ao contrário, o cliente receberá um "Internal Server Error" com o HTTP status code `500`.
E assim deve ser porque seria um bug no seu código ter o `ValidationError` do Pydantic na sua *response*, ou em qualquer outro lugar do seu código (que não na requisição do cliente).
E enquanto você conserta o bug, os clientes / usuários não deveriam ter acesso às informações internas do erro, porque, desse modo, haveria exposição de uma vulnerabilidade de segurança.
### Sobrescreva o manipulador de erro `HTTPException` { #override-the-httpexception-error-handler }
Do mesmo modo, você pode sobreescrever o `HTTPException`.
Por exemplo, você pode querer retornar uma *response* em *plain text* ao invés de um JSON para os seguintes erros:
{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
/// note | Detalhes Técnicos
Você pode usar `from starlette.responses import PlainTextResponse`.
**FastAPI** disponibiliza o mesmo `starlette.responses` como `fastapi.responses`, como conveniência a você, desenvolvedor. Contudo, a maior parte das respostas disponíveis vem diretamente do Starlette.
///
### Use o body do `RequestValidationError`. { #use-the-requestvalidationerror-body }
O `RequestValidationError` contém o `body` que ele recebeu de dados inválidos.
Você pode utilizá-lo enquanto desenvolve seu app para conectar o *body* e debugá-lo, e assim retorná-lo ao usuário, etc.
{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
Tente enviar um item inválido como este:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Você receberá uma *response* informando-o de que os dados são inválidos, e contendo o *body* recebido:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### O `HTTPException` do FastAPI vs o `HTTPException` do Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
O **FastAPI** tem o seu próprio `HTTPException`.
E a classe de erro `HTTPException` do **FastAPI** herda da classe de erro do `HTTPException` do Starlette.
A única diferença é que o `HTTPException` do **FastAPI** aceita qualquer dado que possa ser convertido em JSON para o campo `detail`, enquanto o `HTTPException` do Starlette aceita apenas strings para esse campo.
Portanto, você pode continuar lançando o `HTTPException` do **FastAPI** normalmente no seu código.
Porém, quando você registrar um manipulador de exceção, você deve registrá-lo através do `HTTPException` do Starlette.
Dessa forma, se qualquer parte do código interno, extensão ou plug-in do Starlette lançar um `HTTPException` do Starlette, o seu manipulador poderá capturar e tratá-lo.
Neste exemplo, para poder ter ambos os `HTTPException` no mesmo código, a exceção do Starlette é renomeada para `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Reutilize os manipuladores de exceção do **FastAPI** { #reuse-fastapis-exception-handlers }
Se você quer usar a exceção em conjunto com o mesmo manipulador de exceção *default* do **FastAPI**, você pode importar e re-usar esses manipuladores de exceção do `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
Nesse exemplo você apenas imprime (`print`) o erro com uma mensagem expressiva. Mesmo assim, dá para pegar a ideia. Você pode usar a exceção e então apenas re-usar o manipulador de exceção *default*.
@@ -0,0 +1,72 @@
# Modelos de Parâmetros do Cabeçalho { #header-parameter-models }
Se você possui um grupo de **parâmetros de cabeçalho** relacionados, você pode criar um **modelo do Pydantic** para declará-los.
Isso vai lhe permitir **reusar o modelo** em **múltiplos lugares** e também declarar validações e metadados para todos os parâmetros de uma vez. 😎
/// note | Nota
Isso é possível desde a versão `0.115.0` do FastAPI. 🤓
///
## Parâmetros do Cabeçalho com um Modelo Pydantic { #header-parameters-with-a-pydantic-model }
Declare os **parâmetros de cabeçalho** que você precisa em um **modelo do Pydantic**, e então declare o parâmetro como `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
O **FastAPI** irá **extrair** os dados de **cada campo** a partir dos **cabeçalhos** da requisição e te retornará o modelo do Pydantic que você definiu.
## Checando a documentação { #check-the-docs }
Você pode ver os headers necessários na interface gráfica da documentação em `/docs`:
<div class="screenshot">
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## Proibindo Cabeçalhos adicionais { #forbid-extra-headers }
Em alguns casos de uso especiais (provavelmente não muito comuns), você pode querer **restringir** os cabeçalhos que você quer receber.
Você pode usar a configuração dos modelos do Pydantic para proibir (`forbid`) quaisquer campos `extra`:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Se um cliente tentar enviar alguns **cabeçalhos extra**, eles irão receber uma resposta de **erro**.
Por exemplo, se o cliente tentar enviar um cabeçalho `tool` com o valor `plumbus`, ele irá receber uma resposta de **erro** informando que o parâmetro do cabeçalho `tool` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Desativar conversão de underscores { #disable-convert-underscores }
Da mesma forma que com parâmetros de cabeçalho normais, quando você tem caracteres de sublinhado nos nomes dos parâmetros, eles são **automaticamente convertidos em hifens**.
Por exemplo, se você tem um parâmetro de cabeçalho `save_data` no código, o cabeçalho HTTP esperado será `save-data`, e ele aparecerá assim na documentação.
Se por algum motivo você precisar desativar essa conversão automática, também poderá fazê-lo para modelos do Pydantic para parâmetros de cabeçalho.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Atenção
Antes de definir `convert_underscores` como `False`, tenha em mente que alguns proxies e servidores HTTP não permitem o uso de cabeçalhos com sublinhados.
///
## Resumo { #summary }
Você pode utilizar **modelos do Pydantic** para declarar **cabeçalhos** no **FastAPI**. 😎
+91
View File
@@ -0,0 +1,91 @@
# Parâmetros de Cabeçalho { #header-parameters }
Você pode definir parâmetros de Cabeçalho da mesma maneira que define paramêtros com `Query`, `Path` e `Cookie`.
## Importe `Header` { #import-header }
Primeiro importe `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Declare parâmetros de `Header` { #declare-header-parameters }
Então declare os paramêtros de cabeçalho usando a mesma estrutura que em `Path`, `Query` e `Cookie`.
O primeiro valor é o valor padrão, você pode passar todas as validações adicionais ou parâmetros de anotação:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalhes Técnicos
`Header` é uma classe "irmã" de `Path`, `Query` e `Cookie`. Ela também herda da mesma classe em comum `Param`.
Mas lembre-se que quando você importa `Query`, `Path`, `Header`, e outras de `fastapi`, elas são na verdade funções que retornam classes especiais.
///
/// info | Informação
Para declarar headers, você precisa usar `Header`, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta.
///
## Conversão automática { #automatic-conversion }
`Header` tem algumas funcionalidades a mais em relação a `Path`, `Query` e `Cookie`.
A maioria dos cabeçalhos padrão são separados pelo caractere "hífen", também conhecido como "sinal de menos" (`-`).
Mas uma variável como `user-agent` é inválida em Python.
Portanto, por padrão, `Header` converterá os caracteres de nomes de parâmetros de sublinhado (`_`) para hífen (`-`) para extrair e documentar os cabeçalhos.
Além disso, os cabeçalhos HTTP não diferenciam maiúsculas de minúsculas, portanto, você pode declará-los com o estilo padrão do Python (também conhecido como "snake_case").
Portanto, você pode usar `user_agent` como faria normalmente no código Python, em vez de precisar colocar as primeiras letras em maiúsculas como `User_Agent` ou algo semelhante.
Se por algum motivo você precisar desabilitar a conversão automática de sublinhados para hífens, defina o parâmetro `convert_underscores` de `Header` para `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Atenção
Antes de definir `convert_underscores` como `False`, lembre-se de que alguns proxies e servidores HTTP não permitem o uso de cabeçalhos com sublinhados.
///
## Cabeçalhos duplicados { #duplicate-headers }
É possível receber cabeçalhos duplicados. Isso significa, o mesmo cabeçalho com vários valores.
Você pode definir esses casos usando uma lista na declaração de tipo.
Você receberá todos os valores do cabeçalho duplicado como uma `list` Python.
Por exemplo, para declarar um cabeçalho de `X-Token` que pode aparecer mais de uma vez, você pode escrever:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Se você se comunicar com essa *operação de rota* enviando dois cabeçalhos HTTP como:
```
X-Token: foo
X-Token: bar
```
A resposta seria como:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Recapitulando { #recap }
Declare cabeçalhos com `Header`, usando o mesmo padrão comum que utiliza-se em `Query`, `Path` e `Cookie`.
E não se preocupe com sublinhados em suas variáveis, **FastAPI** cuidará da conversão deles.
+95
View File
@@ -0,0 +1,95 @@
# Tutorial - Guia de Usuário { #tutorial-user-guide }
Esse tutorial mostra como usar o **FastAPI** com a maior parte de seus recursos, passo a passo.
Cada seção constrói, gradualmente, sobre as anteriores, mas sua estrutura são tópicos separados, para que você possa ir a qualquer um específico e resolver suas necessidades específicas de API.
Ele também foi construído para servir como uma referência futura, então você pode voltar e ver exatamente o que você precisa.
## Rode o código { #run-the-code }
Todos os blocos de código podem ser copiados e utilizados diretamente (eles são, na verdade, arquivos Python testados).
Para rodar qualquer um dos exemplos, copie o código para um arquivo `main.py`, e inicie o `fastapi dev` com:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
É **ALTAMENTE recomendado** que você escreva ou copie o código, edite-o e rode-o localmente.
Usá-lo em seu editor é o que realmente te mostra os benefícios do FastAPI, ver quão pouco código você tem que escrever, todas as conferências de tipo, preenchimento automático, etc.
---
## Instale o FastAPI { #install-fastapi }
O primeiro passo é instalar o FastAPI.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então **instalar o FastAPI**:
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
</div>
/// note | Nota
Quando você instala com `pip install "fastapi[standard]"`, ele vem com algumas dependências opcionais padrão, incluindo `fastapi-cloud-cli`, que permite fazer deploy na <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
Se você não quiser ter essas dependências opcionais, pode instalar `pip install fastapi` em vez disso.
Se você quiser instalar as dependências padrão, mas sem o `fastapi-cloud-cli`, você pode instalar com `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
## Guia Avançado de Usuário { #advanced-user-guide }
Há também um **Guia Avançado de Usuário** que você pode ler após esse **Tutorial - Guia de Usuário**.
O **Guia Avançado de Usuário** constrói sobre esse, usa os mesmos conceitos e te ensina algumas funcionalidades extras.
Mas você deveria ler primeiro o **Tutorial - Guia de Usuário** (que você está lendo agora).
Ele foi projetado para que você possa construir uma aplicação completa com apenas o **Tutorial - Guia de Usuário**, e então estendê-la de diferentes formas, dependendo das suas necessidades, usando algumas ideias adicionais do **Guia Avançado de Usuário**.
+120
View File
@@ -0,0 +1,120 @@
# Metadados e Urls de Documentos { #metadata-and-docs-urls }
Você pode personalizar várias configurações de metadados na sua aplicação **FastAPI**.
## Metadados para API { #metadata-for-api }
Você pode definir os seguintes campos que são usados na especificação OpenAPI e nas interfaces automáticas de documentação da API:
| Parâmetro | Tipo | Descrição |
|------------|------|-------------|
| `title` | `str` | O título da API. |
| `summary` | `str` | Um breve resumo da API. <small>Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
| `description` | `str` | Uma breve descrição da API. Pode usar Markdown. |
| `version` | `string` | A versão da API. Esta é a versão da sua aplicação, não do OpenAPI. Por exemplo, `2.5.0`. |
| `terms_of_service` | `str` | Uma URL para os Termos de Serviço da API. Se fornecido, deve ser uma URL. |
| `contact` | `dict` | As informações de contato da API exposta. Pode conter vários campos. <details><summary>Campos de <code>contact</code></summary><table><thead><tr><th>Parâmetro</th><th>Tipo</th><th>Descrição</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>O nome identificador da pessoa/organização de contato.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>A URL que aponta para as informações de contato. DEVE estar no formato de uma URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>O endereço de e-mail da pessoa/organização de contato. DEVE estar no formato de um endereço de e-mail.</td></tr></tbody></table></details> |
| `license_info` | `dict` | As informações de licença para a API exposta. Ela pode conter vários campos. <details><summary>Campos de <code>license_info</code></summary><table><thead><tr><th>Parâmetro</th><th>Tipo</th><th>Descrição</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>OBRIGATÓRIO</strong> (se um <code>license_info</code> for definido). O nome da licença usada para a API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Uma expressão de licença <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> para a API. O campo <code>identifier</code> é mutuamente exclusivo do campo <code>url</code>. <small>Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>Uma URL para a licença usada para a API. DEVE estar no formato de uma URL.</td></tr></tbody></table></details> |
Você pode defini-los da seguinte maneira:
{* ../../docs_src/metadata/tutorial001.py hl[3:16,19:32] *}
/// tip | Dica
Você pode escrever Markdown no campo `description` e ele será renderizado na saída.
///
Com essa configuração, a documentação automática da API se pareceria com:
<img src="/img/tutorial/metadata/image01.png">
## Identificador de Licença { #license-identifier }
Desde o OpenAPI 3.1.0 e FastAPI 0.99.0, você também pode definir o license_info com um identifier em vez de uma url.
Por exemplo:
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
## Metadados para tags { #metadata-for-tags }
Você também pode adicionar metadados adicionais para as diferentes tags usadas para agrupar suas operações de rota com o parâmetro `openapi_tags`.
Ele recebe uma lista contendo um dicionário para cada tag.
Cada dicionário pode conter:
* `name` (**obrigatório**): uma `str` com o mesmo nome da tag que você usa no parâmetro `tags` nas suas *operações de rota* e `APIRouter`s.
* `description`: uma `str` com uma breve descrição da tag. Pode conter Markdown e será exibido na interface de documentação.
* `externalDocs`: um `dict` descrevendo a documentação externa com:
* `description`: uma `str` com uma breve descrição da documentação externa.
* `url` (**obrigatório**): uma `str` com a URL da documentação externa.
### Criar Metadados para tags { #create-metadata-for-tags }
Vamos tentar isso em um exemplo com tags para `users` e `items`.
Crie metadados para suas tags e passe-os para o parâmetro `openapi_tags`:
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
Observe que você pode usar Markdown dentro das descrições. Por exemplo, "login" será exibido em negrito (**login**) e "fancy" será exibido em itálico (_fancy_).
/// tip | Dica
Você não precisa adicionar metadados para todas as tags que você usa.
///
### Use suas tags { #use-your-tags }
Use o parâmetro `tags` com suas *operações de rota* (e `APIRouter`s) para atribuí-los a diferentes tags:
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
/// info | Informação
Leia mais sobre tags em [Configuração de operação de rota](path-operation-configuration.md#tags){.internal-link target=_blank}.
///
### Cheque os documentos { #check-the-docs }
Agora, se você verificar a documentação, ela exibirá todos os metadados adicionais:
<img src="/img/tutorial/metadata/image02.png">
### Ordem das tags { #order-of-tags }
A ordem de cada dicionário de metadados de tag também define a ordem exibida na interface de documentação.
Por exemplo, embora `users` apareça após `items` em ordem alfabética, ele é exibido antes deles, porque adicionamos seus metadados como o primeiro dicionário na lista.
## URL da OpenAPI { #openapi-url }
Por padrão, o esquema OpenAPI é servido em `/openapi.json`.
Mas você pode configurá-lo com o parâmetro `openapi_url`.
Por exemplo, para defini-lo para ser servido em `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
Se você quiser desativar completamente o esquema OpenAPI, pode definir `openapi_url=None`, o que também desativará as interfaces de documentação que o utilizam.
## URLs da Documentação { #docs-urls }
Você pode configurar as duas interfaces de documentação incluídas:
* **Swagger UI**: acessível em `/docs`.
* Você pode definir sua URL com o parâmetro `docs_url`.
* Você pode desativá-la definindo `docs_url=None`.
* **ReDoc**: acessível em `/redoc`.
* Você pode definir sua URL com o parâmetro `redoc_url`.
* Você pode desativá-la definindo `redoc_url=None`.
Por exemplo, para definir o Swagger UI para ser servido em `/documentation` e desativar o ReDoc:
{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+95
View File
@@ -0,0 +1,95 @@
# Middleware { #middleware }
Você pode adicionar middleware à suas aplicações **FastAPI**.
Um "middleware" é uma função que manipula cada **requisição** antes de ser processada por qualquer *operação de rota* específica. E também cada **resposta** antes de retorná-la.
* Ele pega cada **requisição** que chega ao seu aplicativo.
* Ele pode então fazer algo com essa **requisição** ou executar qualquer código necessário.
* Então ele passa a **requisição** para ser processada pelo resto do aplicativo (por alguma *operação de rota*).
* Ele então pega a **resposta** gerada pelo aplicativo (por alguma *operação de rota*).
* Ele pode fazer algo com essa **resposta** ou executar qualquer código necessário.
* Então ele retorna a **resposta**.
/// note | Detalhes Técnicos
Se você tiver dependências com `yield`, o código de saída será executado *depois* do middleware.
Se houver alguma tarefa em segundo plano (abordada na seção [Tarefas em segundo plano](background-tasks.md){.internal-link target=_blank}, que você verá mais adiante), ela será executada *depois* de todo o middleware.
///
## Criar um middleware { #create-a-middleware }
Para criar um middleware, use o decorador `@app.middleware("http")` logo acima de uma função.
A função middleware recebe:
* A `request`.
* Uma função `call_next` que receberá o `request` como um parâmetro.
* Esta função passará a `request` para a *operação de rota* correspondente.
* Então ela retorna a `response` gerada pela *operação de rota* correspondente.
* Você pode então modificar ainda mais o `response` antes de retorná-lo.
{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
/// tip | Dica
Tenha em mente que cabeçalhos proprietários personalizados podem ser adicionados <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando o prefixo `X-`</a>.
Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando o parâmetro `expose_headers` documentado em <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Documentos CORS da Starlette</a>.
///
/// note | Detalhes Técnicos
Você também pode usar `from starlette.requests import Request`.
**FastAPI** fornece isso como uma conveniência para você, o desenvolvedor. Mas vem diretamente da Starlette.
///
### Antes e depois da `response` { #before-and-after-the-response }
Você pode adicionar código para ser executado com a `request`, antes que qualquer *operação de rota* o receba.
E também depois que a `response` é gerada, antes de retorná-la.
Por exemplo, você pode adicionar um cabeçalho personalizado `X-Process-Time` contendo o tempo em segundos que levou para processar a solicitação e gerar uma resposta:
{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
/// tip | Dica
Aqui usamos <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> em vez de `time.time()` porque ele pode ser mais preciso para esses casos de uso. 🤓
///
## Ordem de execução de múltiplos middlewares { #multiple-middleware-execution-order }
Quando você adiciona múltiplos middlewares usando o decorador `@app.middleware()` ou o método `app.add_middleware()`, cada novo middleware envolve a aplicação, formando uma pilha. O último middleware adicionado é o mais externo, e o primeiro é o mais interno.
No caminho da requisição, o middleware mais externo roda primeiro.
No caminho da resposta, ele roda por último.
Por exemplo:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Isso resulta na seguinte ordem de execução:
* **Requisição**: MiddlewareB → MiddlewareA → rota
* **Resposta**: rota → MiddlewareA → MiddlewareB
Esse comportamento de empilhamento garante que os middlewares sejam executados em uma ordem previsível e controlável.
## Outros middlewares { #other-middlewares }
Mais tarde, você pode ler mais sobre outros middlewares no [Guia do usuário avançado: Middleware avançado](../advanced/middleware.md){.internal-link target=_blank}.
Você lerá sobre como manipular <abbr title="Cross-Origin Resource Sharing">CORS</abbr> com um middleware na próxima seção.
@@ -0,0 +1,107 @@
# Configuração da Operação de Rota { #path-operation-configuration }
Existem vários parâmetros que você pode passar para o seu *decorador de operação de rota* para configurá-lo.
/// warning | Atenção
Observe que esses parâmetros são passados diretamente para o *decorador de operação de rota*, não para a sua *função de operação de rota*.
///
## Código de Status da Resposta { #response-status-code }
Você pode definir o `status_code` (HTTP) para ser usado na resposta da sua *operação de rota*.
Você pode passar diretamente o código `int`, como `404`.
Mas se você não se lembrar o que cada código numérico significa, pode usar as constantes de atalho em `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Esse código de status será usado na resposta e será adicionado ao esquema OpenAPI.
/// note | Detalhes Técnicos
Você também poderia usar `from starlette import status`.
**FastAPI** fornece o mesmo `starlette.status` como `fastapi.status` apenas como uma conveniência para você, o desenvolvedor. Mas vem diretamente do Starlette.
///
## Tags { #tags }
Você pode adicionar tags para sua *operação de rota*, passe o parâmetro `tags` com uma `list` de `str` (comumente apenas um `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Eles serão adicionados ao esquema OpenAPI e usados pelas interfaces de documentação automática:
<img src="/img/tutorial/path-operation-configuration/image01.png">
### Tags com Enums { #tags-with-enums }
Se você tem uma grande aplicação, você pode acabar acumulando **várias tags**, e você gostaria de ter certeza de que você sempre usa a **mesma tag** para *operações de rota* relacionadas.
Nestes casos, pode fazer sentido armazenar as tags em um `Enum`.
**FastAPI** suporta isso da mesma maneira que com strings simples:
{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
## Resumo e descrição { #summary-and-description }
Você pode adicionar um `summary` e uma `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
## Descrição do docstring { #description-from-docstring }
Como as descrições tendem a ser longas e cobrir várias linhas, você pode declarar a descrição da *operação de rota* na <abbr title="uma string de várias linhas como a primeira expressão dentro de uma função (não atribuída a nenhuma variável) usada para documentação">docstring</abbr> da função e o **FastAPI** irá lê-la de lá.
Você pode escrever <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> na docstring, ele será interpretado e exibido corretamente (levando em conta a indentação da docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Ela será usada nas documentações interativas:
<img src="/img/tutorial/path-operation-configuration/image02.png">
## Descrição da resposta { #response-description }
Você pode especificar a descrição da resposta com o parâmetro `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
/// info | Informação
Note que `response_description` se refere especificamente à resposta, a `description` se refere à *operação de rota* em geral.
///
/// check | Verifique
OpenAPI especifica que cada *operação de rota* requer uma descrição de resposta.
Então, se você não fornecer uma, o **FastAPI** irá gerar automaticamente uma de "Resposta bem-sucedida".
///
<img src="/img/tutorial/path-operation-configuration/image03.png">
## Descontinuar uma *operação de rota* { #deprecate-a-path-operation }
Se você precisar marcar uma *operação de rota* como <abbr title="obsoleta, recomendada não usá-la">descontinuada</abbr>, mas sem removê-la, passe o parâmetro `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
Ela será claramente marcada como descontinuada nas documentações interativas:
<img src="/img/tutorial/path-operation-configuration/image04.png">
Verifique como *operações de rota* descontinuadas e não descontinuadas se parecem:
<img src="/img/tutorial/path-operation-configuration/image05.png">
## Resumindo { #recap }
Você pode configurar e adicionar metadados para suas *operações de rota* facilmente passando parâmetros para os *decoradores de operação de rota*.
@@ -0,0 +1,154 @@
# Parâmetros de path e validações numéricas { #path-parameters-and-numeric-validations }
Da mesma forma que você pode declarar mais validações e metadados para parâmetros de consulta com `Query`, você pode declarar o mesmo tipo de validações e metadados para parâmetros de path com `Path`.
## Importe `Path` { #import-path }
Primeiro, importe `Path` de `fastapi`, e importe `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Informação
O FastAPI adicionou suporte a `Annotated` (e passou a recomendá-lo) na versão 0.95.0.
Se você tiver uma versão mais antiga, verá erros ao tentar usar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} para pelo menos 0.95.1 antes de usar `Annotated`.
///
## Declare metadados { #declare-metadata }
Você pode declarar todos os mesmos parâmetros que em `Query`.
Por exemplo, para declarar um valor de metadado `title` para o parâmetro de path `item_id` você pode digitar:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Nota
Um parâmetro de path é sempre obrigatório, pois precisa fazer parte do path. Mesmo que você o declare como `None` ou defina um valor padrão, isso não afetaria nada, ele ainda seria sempre obrigatório.
///
## Ordene os parâmetros de acordo com sua necessidade { #order-the-parameters-as-you-need }
/// tip | Dica
Isso provavelmente não é tão importante ou necessário se você usar `Annotated`.
///
Vamos supor que você queira declarar o parâmetro de consulta `q` como uma `str` obrigatória.
E você não precisa declarar mais nada para esse parâmetro, então você realmente não precisa usar `Query`.
Mas você ainda precisa usar `Path` para o parâmetro de path `item_id`. E você não quer usar `Annotated` por algum motivo.
O Python vai reclamar se você colocar um valor com “padrão” antes de um valor que não tem “padrão”.
Mas você pode reordená-los e colocar primeiro o valor sem padrão (o parâmetro de consulta `q`).
Isso não faz diferença para o **FastAPI**. Ele vai detectar os parâmetros pelos seus nomes, tipos e declarações de padrão (`Query`, `Path`, etc.), sem se importar com a ordem.
Então, você pode declarar sua função assim:
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
Mas tenha em mente que, se você usar `Annotated`, você não terá esse problema, não fará diferença, pois você não está usando valores padrão de parâmetros de função para `Query()` ou `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
## Ordene os parâmetros de acordo com sua necessidade, truques { #order-the-parameters-as-you-need-tricks }
/// tip | Dica
Isso provavelmente não é tão importante ou necessário se você usar `Annotated`.
///
Aqui vai um pequeno truque que pode ser útil, mas você não vai precisar dele com frequência.
Se você quiser:
* declarar o parâmetro de consulta `q` sem um `Query` nem qualquer valor padrão
* declarar o parâmetro de path `item_id` usando `Path`
* tê-los em uma ordem diferente
* não usar `Annotated`
...o Python tem uma pequena sintaxe especial para isso.
Passe `*`, como o primeiro parâmetro da função.
O Python não fará nada com esse `*`, mas saberá que todos os parâmetros seguintes devem ser chamados como argumentos nomeados (pares chave-valor), também conhecidos como <abbr title="Do inglês: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Mesmo que eles não tenham um valor padrão.
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
### Melhor com `Annotated` { #better-with-annotated }
Tenha em mente que, se você usar `Annotated`, como você não está usando valores padrão de parâmetros de função, você não terá esse problema e provavelmente não precisará usar `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
## Validações numéricas: maior que ou igual { #number-validations-greater-than-or-equal }
Com `Query` e `Path` (e outras que você verá depois) você pode declarar restrições numéricas.
Aqui, com `ge=1`, `item_id` precisará ser um número inteiro “`g`reater than or `e`qual” a `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
## Validações numéricas: maior que e menor que ou igual { #number-validations-greater-than-and-less-than-or-equal }
O mesmo se aplica a:
* `gt`: maior que (`g`reater `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
## Validações numéricas: floats, maior que e menor que { #number-validations-floats-greater-than-and-less-than }
Validações numéricas também funcionam para valores `float`.
Aqui é onde se torna importante poder declarar <abbr title="greater than maior que"><code>gt</code></abbr> e não apenas <abbr title="greater than or equal maior que ou igual"><code>ge</code></abbr>. Com isso você pode exigir, por exemplo, que um valor seja maior que `0`, mesmo que seja menor que `1`.
Assim, `0.5` seria um valor válido. Mas `0.0` ou `0` não seriam.
E o mesmo para <abbr title="less than menor que"><code>lt</code></abbr>.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
## Recapitulando { #recap }
Com `Query`, `Path` (e outras que você ainda não viu) você pode declarar metadados e validações de string do mesmo modo que em [Parâmetros de consulta e validações de string](query-params-str-validations.md){.internal-link target=_blank}.
E você também pode declarar validações numéricas:
* `gt`: maior que (`g`reater `t`han)
* `ge`: maior que ou igual (`g`reater than or `e`qual)
* `lt`: menor que (`l`ess `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
/// info | Informação
`Query`, `Path` e outras classes que você verá depois são subclasses de uma classe comum `Param`.
Todas elas compartilham os mesmos parâmetros para validação adicional e metadados que você viu.
///
/// note | Detalhes Técnicos
Quando você importa `Query`, `Path` e outras de `fastapi`, elas são na verdade funções.
Que, quando chamadas, retornam instâncias de classes de mesmo nome.
Então, você importa `Query`, que é uma função. E quando você a chama, ela retorna uma instância de uma classe também chamada `Query`.
Essas funções existem (em vez de usar diretamente as classes) para que seu editor não marque erros sobre seus tipos.
Dessa forma, você pode usar seu editor e ferramentas de codificação normais sem precisar adicionar configurações personalizadas para desconsiderar esses erros.
///
+241
View File
@@ -0,0 +1,241 @@
# Parâmetros de path { #path-parameters }
Você pode declarar "parâmetros" ou "variáveis" de path com a mesma sintaxe usada por strings de formatação do Python:
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
O valor do parâmetro de path `item_id` será passado para a sua função como o argumento `item_id`.
Então, se você executar este exemplo e acessar <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, você verá uma resposta:
```JSON
{"item_id":"foo"}
```
## Parâmetros de path com tipos { #path-parameters-with-types }
Você pode declarar o tipo de um parâmetro de path na função, usando as anotações de tipo padrão do Python:
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
Neste caso, `item_id` é declarado como um `int`.
/// check | Verifique
Isso fornecerá suporte do editor dentro da sua função, com verificações de erros, preenchimento automático, etc.
///
## Dados <abbr title="também conhecido como: serialização, parsing, marshalling">conversão</abbr> { #data-conversion }
Se você executar este exemplo e abrir seu navegador em <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, você verá uma resposta:
```JSON
{"item_id":3}
```
/// check | Verifique
Perceba que o valor que sua função recebeu (e retornou) é `3`, como um `int` do Python, não uma string `"3"`.
Então, com essa declaração de tipo, o **FastAPI** fornece <abbr title="convertendo a string que vem de um request HTTP em dados Python">"parsing"</abbr> automático do request.
///
## Validação de dados { #data-validation }
Mas se você for no navegador para <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, verá um bom erro HTTP:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
porque o parâmetro de path `item_id` tinha o valor `"foo"`, que não é um `int`.
O mesmo erro apareceria se você fornecesse um `float` em vez de um `int`, como em: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
/// check | Verifique
Então, com a mesma declaração de tipo do Python, o **FastAPI** fornece validação de dados.
Observe que o erro também declara claramente exatamente o ponto onde a validação não passou.
Isso é incrivelmente útil ao desenvolver e depurar código que interage com sua API.
///
## Documentação { #documentation }
E quando você abrir seu navegador em <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, você verá documentação automática, interativa, da API como:
<img src="/img/tutorial/path-params/image01.png">
/// check | Verifique
Novamente, apenas com a mesma declaração de tipo do Python, o **FastAPI** fornece documentação automática e interativa (integrando o Swagger UI).
Observe que o parâmetro de path está declarado como um inteiro.
///
## Benefícios baseados em padrões, documentação alternativa { #standards-based-benefits-alternative-documentation }
E como o schema gerado é do padrão <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, existem muitas ferramentas compatíveis.
Por causa disso, o próprio **FastAPI** fornece uma documentação alternativa da API (usando ReDoc), que você pode acessar em <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
<img src="/img/tutorial/path-params/image02.png">
Da mesma forma, existem muitas ferramentas compatíveis. Incluindo ferramentas de geração de código para muitas linguagens.
## Pydantic { #pydantic }
Toda a validação de dados é realizada nos bastidores pelo <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, então você recebe todos os benefícios disso. E você sabe que está em boas mãos.
Você pode usar as mesmas declarações de tipo com `str`, `float`, `bool` e muitos outros tipos de dados complexos.
Vários deles são explorados nos próximos capítulos do tutorial.
## A ordem importa { #order-matters }
Ao criar *operações de rota*, você pode encontrar situações em que tem um path fixo.
Como `/users/me`, digamos que seja para obter dados sobre o usuário atual.
E então você também pode ter um path `/users/{user_id}` para obter dados sobre um usuário específico por algum ID de usuário.
Como as *operações de rota* são avaliadas em ordem, você precisa garantir que o path para `/users/me` seja declarado antes do de `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Caso contrário, o path para `/users/{user_id}` também corresponderia a `/users/me`, "achando" que está recebendo um parâmetro `user_id` com o valor `"me"`.
Da mesma forma, você não pode redefinir uma operação de rota:
{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
A primeira sempre será usada, já que o path corresponde primeiro.
## Valores predefinidos { #predefined-values }
Se você tem uma *operação de rota* que recebe um *parâmetro de path*, mas quer que os valores válidos possíveis do *parâmetro de path* sejam predefinidos, você pode usar um <abbr title="Enumeration">`Enum`</abbr> padrão do Python.
### Crie uma classe `Enum` { #create-an-enum-class }
Importe `Enum` e crie uma subclasse que herde de `str` e de `Enum`.
Ao herdar de `str`, a documentação da API saberá que os valores devem ser do tipo `string` e poderá renderizá-los corretamente.
Em seguida, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis:
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// info | Informação
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerations (ou enums) estão disponíveis no Python</a> desde a versão 3.4.
///
/// tip | Dica
Se você está se perguntando, "AlexNet", "ResNet" e "LeNet" são apenas nomes de <abbr title="Tecnicamente, arquiteturas de modelos de Deep Learning">modelos</abbr> de Aprendizado de Máquina.
///
### Declare um parâmetro de path { #declare-a-path-parameter }
Em seguida, crie um *parâmetro de path* com anotação de tipo usando a classe enum que você criou (`ModelName`):
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Verifique a documentação { #check-the-docs }
Como os valores disponíveis para o *parâmetro de path* são predefinidos, a documentação interativa pode mostrá-los de forma agradável:
<img src="/img/tutorial/path-params/image03.png">
### Trabalhando com *enumerações* do Python { #working-with-python-enumerations }
O valor do *parâmetro de path* será um *membro de enumeração*.
#### Compare membros de enumeração { #compare-enumeration-members }
Você pode compará-lo com o *membro de enumeração* no seu enum `ModelName` criado:
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Obtenha o valor da enumeração { #get-the-enumeration-value }
Você pode obter o valor real (um `str` neste caso) usando `model_name.value`, ou, em geral, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
/// tip | Dica
Você também pode acessar o valor `"lenet"` com `ModelName.lenet.value`.
///
#### Retorne membros de enumeração { #return-enumeration-members }
Você pode retornar *membros de enum* da sua *operação de rota*, até mesmo aninhados em um corpo JSON (por exemplo, um `dict`).
Eles serão convertidos para seus valores correspondentes (strings neste caso) antes de serem retornados ao cliente:
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
No seu cliente, você receberá uma resposta JSON como:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parâmetros de path que contêm paths { #path-parameters-containing-paths }
Digamos que você tenha uma *operação de rota* com um path `/files/{file_path}`.
Mas você precisa que o próprio `file_path` contenha um *path*, como `home/johndoe/myfile.txt`.
Então, a URL para esse arquivo seria algo como: `/files/home/johndoe/myfile.txt`.
### Suporte do OpenAPI { #openapi-support }
O OpenAPI não oferece suporte a uma maneira de declarar um *parâmetro de path* que contenha um *path* dentro, pois isso poderia levar a cenários difíceis de testar e definir.
Ainda assim, você pode fazer isso no **FastAPI**, usando uma das ferramentas internas do Starlette.
E a documentação continuará funcionando, embora não adicione nenhuma informação dizendo que o parâmetro deve conter um path.
### Conversor de path { #path-convertor }
Usando uma opção diretamente do Starlette você pode declarar um *parâmetro de path* contendo um *path* usando uma URL como:
```
/files/{file_path:path}
```
Nesse caso, o nome do parâmetro é `file_path`, e a última parte, `:path`, diz que o parâmetro deve corresponder a qualquer *path*.
Então, você pode usá-lo com:
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
/// tip | Dica
Você pode precisar que o parâmetro contenha `/home/johndoe/myfile.txt`, com uma barra inicial (`/`).
Nesse caso, a URL seria: `/files//home/johndoe/myfile.txt`, com uma barra dupla (`//`) entre `files` e `home`.
///
## Recapitulação { #recap }
Com o **FastAPI**, ao usar declarações de tipo do Python curtas, intuitivas e padrão, você obtém:
- Suporte no editor: verificações de erro, autocompletar, etc.
- "<abbr title="convertendo a string que vem de um request HTTP em dados Python">Parsing</abbr>" de dados
- Validação de dados
- Anotação da API e documentação automática
E você só precisa declará-los uma vez.
Essa é provavelmente a principal vantagem visível do **FastAPI** em comparação com frameworks alternativos (além do desempenho bruto).
@@ -0,0 +1,69 @@
# Modelos de Parâmetros de Consulta { #query-parameter-models }
Se você possui um grupo de **parâmetros de consultas** que são relacionados, você pode criar um **modelo Pydantic** para declará-los.
Isso permitiria que você **reutilizasse o modelo** em **diversos lugares**, e também declarasse validações e metadados de todos os parâmetros de uma única vez. 😎
/// note | Nota
Isso é suportado desde o FastAPI versão `0.115.0`. 🤓
///
## Parâmetros de Consulta com um Modelo Pydantic { #query-parameters-with-a-pydantic-model }
Declare os **parâmetros de consulta** que você precisa em um **modelo Pydantic**, e então declare o parâmetro como `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
O **FastAPI** **extrairá** os dados para **cada campo** dos **parâmetros de consulta** presentes na requisição, e fornecerá o modelo Pydantic que você definiu.
## Verifique os Documentos { #check-the-docs }
Você pode ver os parâmetros de consulta nos documentos de IU em `/docs`:
<div class="screenshot">
<img src="/img/tutorial/query-param-models/image01.png">
</div>
## Restrinja Parâmetros de Consulta Extras { #forbid-extra-query-parameters }
Em alguns casos especiais (provavelmente não muito comuns), você queira **restrinjir** os parâmetros de consulta que deseja receber.
Você pode usar a configuração do modelo Pydantic para `forbid` (proibir) qualquer campo `extra`:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Caso um cliente tente enviar alguns dados **extras** nos **parâmetros de consulta**, eles receberão um retorno de **erro**.
Por exemplo, se o cliente tentar enviar um parâmetro de consulta `tool` com o valor `plumbus`, como:
```http
https://example.com/items/?limit=10&tool=plumbus
```
Eles receberão um retorno de **erro** informando-os que o parâmentro de consulta `tool` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Resumo { #summary }
Você pode utilizar **modelos Pydantic** para declarar **parâmetros de consulta** no **FastAPI**. 😎
/// tip | Dica
Alerta de spoiler: você também pode utilizar modelos Pydantic para declarar cookies e cabeçalhos, mas você irá ler sobre isso mais a frente no tutorial. 🤫
///
@@ -0,0 +1,488 @@
# Parâmetros de consulta e validações de string { #query-parameters-and-string-validations }
O **FastAPI** permite declarar informações adicionais e validações para os seus parâmetros.
Vamos usar esta aplicação como exemplo:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
O parâmetro de consulta `q` é do tipo `str | None`, isso significa que é do tipo `str`, mas também pode ser `None`, e de fato, o valor padrão é `None`, então o FastAPI saberá que não é obrigatório.
/// note | Nota
O FastAPI saberá que o valor de `q` não é obrigatório por causa do valor padrão `= None`.
Ter `str | None` permitirá que seu editor lhe ofereça melhor suporte e detecte erros.
///
## Validação adicional { #additional-validation }
Vamos impor que, embora `q` seja opcional, sempre que for fornecido, **seu comprimento não exceda 50 caracteres**.
### Importe `Query` e `Annotated` { #import-query-and-annotated }
Para isso, primeiro importe:
* `Query` de `fastapi`
* `Annotated` de `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Informação
O FastAPI adicionou suporte a `Annotated` (e passou a recomendá-lo) na versão 0.95.0.
Se você tiver uma versão mais antiga, terá erros ao tentar usar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} para pelo menos 0.95.1 antes de usar `Annotated`.
///
## Use `Annotated` no tipo do parâmetro `q` { #use-annotated-in-the-type-for-the-q-parameter }
Lembra que eu disse antes que `Annotated` pode ser usado para adicionar metadados aos seus parâmetros na [Introdução aos tipos do Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
Agora é a hora de usá-lo com FastAPI. 🚀
Tínhamos esta anotação de tipo:
//// tab | Python 3.10+
```Python
q: str | None = None
```
////
//// tab | Python 3.8+
```Python
q: Union[str, None] = None
```
////
O que faremos é envolver isso com `Annotated`, para que fique assim:
//// tab | Python 3.10+
```Python
q: Annotated[str | None] = None
```
////
//// tab | Python 3.8+
```Python
q: Annotated[Union[str, None]] = None
```
////
Ambas as versões significam a mesma coisa, `q` é um parâmetro que pode ser `str` ou `None`, e por padrão é `None`.
Agora vamos pular para a parte divertida. 🎉
## Adicione `Query` ao `Annotated` no parâmetro `q` { #add-query-to-annotated-in-the-q-parameter }
Agora que temos esse `Annotated` onde podemos colocar mais informações (neste caso, uma validação adicional), adicione `Query` dentro de `Annotated` e defina o parâmetro `max_length` como `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Perceba que o valor padrão continua sendo `None`, então o parâmetro ainda é opcional.
Mas agora, com `Query(max_length=50)` dentro de `Annotated`, estamos dizendo ao FastAPI que queremos **validação adicional** para este valor, queremos que tenha no máximo 50 caracteres. 😎
/// tip | Dica
Aqui estamos usando `Query()` porque este é um **parâmetro de consulta**. Mais adiante veremos outros como `Path()`, `Body()`, `Header()` e `Cookie()`, que também aceitam os mesmos argumentos que `Query()`.
///
Agora o FastAPI vai:
* **Validar** os dados garantindo que o comprimento máximo seja de 50 caracteres
* Mostrar um **erro claro** para o cliente quando os dados não forem válidos
* **Documentar** o parâmetro na *operação de rota* do esquema OpenAPI (então ele aparecerá na **UI de docs automática**)
## Alternativa (antiga): `Query` como valor padrão { #alternative-old-query-as-the-default-value }
Versões anteriores do FastAPI (antes de <abbr title="antes de 2023-03">0.95.0</abbr>) exigiam que você usasse `Query` como valor padrão do seu parâmetro, em vez de colocá-lo em `Annotated`. É muito provável que você veja código assim por aí, então vou te explicar.
/// tip | Dica
Para código novo e sempre que possível, use `Annotated` como explicado acima. Há múltiplas vantagens (explicadas abaixo) e nenhuma desvantagem. 🍰
///
É assim que você usaria `Query()` como valor padrão do parâmetro da sua função, definindo o parâmetro `max_length` como 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Como neste caso (sem usar `Annotated`) temos que substituir o valor padrão `None` na função por `Query()`, agora precisamos definir o valor padrão com o parâmetro `Query(default=None)`, ele serve ao mesmo propósito de definir esse valor padrão (pelo menos para o FastAPI).
Então:
```Python
q: str | None = Query(default=None)
```
...torna o parâmetro opcional, com um valor padrão de `None`, o mesmo que:
```Python
q: str | None = None
```
Mas a versão com `Query` o declara explicitamente como sendo um parâmetro de consulta.
Então, podemos passar mais parâmetros para `Query`. Neste caso, o parâmetro `max_length` que se aplica a strings:
```Python
q: str | None = Query(default=None, max_length=50)
```
Isso validará os dados, mostrará um erro claro quando os dados não forem válidos e documentará o parâmetro na *operação de rota* do esquema OpenAPI.
### `Query` como valor padrão ou em `Annotated` { #query-as-the-default-value-or-in-annotated }
Tenha em mente que, ao usar `Query` dentro de `Annotated`, você não pode usar o parâmetro `default` de `Query`.
Em vez disso, use o valor padrão real do parâmetro da função. Caso contrário, haveria inconsistência.
Por exemplo, isto não é permitido:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...porque não está claro se o valor padrão deveria ser `"rick"` ou `"morty"`.
Então, você usaria (preferencialmente):
```Python
q: Annotated[str, Query()] = "rick"
```
...ou em bases de código mais antigas você encontrará:
```Python
q: str = Query(default="rick")
```
### Vantagens de `Annotated` { #advantages-of-annotated }
**Usar `Annotated` é recomendado** em vez do valor padrão nos parâmetros da função, é **melhor** por vários motivos. 🤓
O valor **padrão** do **parâmetro da função** é o **valor padrão real**, isso é mais intuitivo com Python em geral. 😌
Você poderia **chamar** essa mesma função em **outros lugares** sem FastAPI, e ela **funcionaria como esperado**. Se houver um parâmetro **obrigatório** (sem valor padrão), seu **editor** vai avisar com um erro, e o **Python** também reclamará se você executá-la sem passar o parâmetro obrigatório.
Quando você não usa `Annotated` e em vez disso usa o estilo de **valor padrão (antigo)**, se você chamar essa função sem FastAPI em **outros lugares**, terá que **lembrar** de passar os argumentos para a função para que funcione corretamente, caso contrário os valores serão diferentes do esperado (por exemplo, `QueryInfo` ou algo parecido em vez de `str`). E seu editor não vai avisar, e o Python também não vai reclamar ao executar a função, apenas quando as operações internas falharem.
Como `Annotated` pode ter mais de uma anotação de metadados, você agora pode até usar a mesma função com outras ferramentas, como o <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
## Adicione mais validações { #add-more-validations }
Você também pode adicionar um parâmetro `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Adicione expressões regulares { #add-regular-expressions }
Você pode definir um `pattern` de <abbr title="Uma expressão regular, regex ou regexp é uma sequência de caracteres que define um padrão de busca para strings.">expressão regular</abbr> que o parâmetro deve corresponder:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Esse padrão específico de expressão regular verifica se o valor recebido no parâmetro:
* `^`: começa com os caracteres seguintes, não tem caracteres antes.
* `fixedquery`: tem exatamente o valor `fixedquery`.
* `$`: termina ali, não tem mais caracteres depois de `fixedquery`.
Se você se sentir perdido com essas ideias de **"expressão regular"**, não se preocupe. Esse é um assunto difícil para muitas pessoas. Você ainda pode fazer muitas coisas sem precisar de expressões regulares por enquanto.
Agora você sabe que, sempre que precisar delas, pode usá-las no **FastAPI**.
### Pydantic v1 `regex` em vez de `pattern` { #pydantic-v1-regex-instead-of-pattern }
Antes da versão 2 do Pydantic e antes do FastAPI 0.100.0, o parâmetro se chamava `regex` em vez de `pattern`, mas agora está descontinuado.
Você ainda pode ver algum código usando isso:
//// tab | Pydantic v1
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
////
Mas saiba que isso está descontinuado e deve ser atualizado para usar o novo parâmetro `pattern`. 🤓
## Valores padrão { #default-values }
Você pode, claro, usar valores padrão diferentes de `None`.
Digamos que você queira declarar o parâmetro de consulta `q` com `min_length` de `3` e ter um valor padrão de `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
/// note | Nota
Ter um valor padrão de qualquer tipo, incluindo `None`, torna o parâmetro opcional (não obrigatório).
///
## Parâmetros obrigatórios { #required-parameters }
Quando não precisamos declarar mais validações ou metadados, podemos tornar o parâmetro de consulta `q` obrigatório simplesmente não declarando um valor padrão, assim:
```Python
q: str
```
em vez de:
```Python
q: str | None = None
```
Mas agora estamos declarando com `Query`, por exemplo assim:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Então, quando você precisa declarar um valor como obrigatório usando `Query`, você pode simplesmente não declarar um valor padrão:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
### Obrigatório, pode ser `None` { #required-can-be-none }
Você pode declarar que um parâmetro pode aceitar `None`, mas que ainda assim é obrigatório. Isso forçaria os clientes a enviarem um valor, mesmo que o valor seja `None`.
Para isso, você pode declarar que `None` é um tipo válido, mas simplesmente não declarar um valor padrão:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Lista de parâmetros de consulta / múltiplos valores { #query-parameter-list-multiple-values }
Quando você define explicitamente um parâmetro de consulta com `Query`, você também pode declará-lo para receber uma lista de valores, ou seja, receber múltiplos valores.
Por exemplo, para declarar um parâmetro de consulta `q` que pode aparecer várias vezes na URL, você pode escrever:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Então, com uma URL como:
```
http://localhost:8000/items/?q=foo&q=bar
```
você receberá os múltiplos valores do *parâmetro de consulta* `q` (`foo` e `bar`) em uma `list` Python dentro da sua *função de operação de rota*, no *parâmetro da função* `q`.
Assim, a resposta para essa URL seria:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Dica
Para declarar um parâmetro de consulta com tipo `list`, como no exemplo acima, você precisa usar explicitamente `Query`, caso contrário seria interpretado como um corpo da requisição.
///
A documentação interativa da API será atualizada de acordo, permitindo múltiplos valores:
<img src="/img/tutorial/query-params-str-validations/image02.png">
### Lista de parâmetros de consulta / múltiplos valores com valores padrão { #query-parameter-list-multiple-values-with-defaults }
Você também pode definir uma `list` de valores padrão caso nenhum seja fornecido:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
Se você for até:
```
http://localhost:8000/items/
```
o valor padrão de `q` será: `["foo", "bar"]` e sua resposta será:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Usando apenas `list` { #using-just-list }
Você também pode usar `list` diretamente em vez de `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
/// note | Nota
Tenha em mente que, neste caso, o FastAPI não verificará o conteúdo da lista.
Por exemplo, `list[int]` verificaria (e documentaria) que os conteúdos da lista são inteiros. Mas `list` sozinho não.
///
## Declare mais metadados { #declare-more-metadata }
Você pode adicionar mais informações sobre o parâmetro.
Essas informações serão incluídas no OpenAPI gerado e usadas pelas interfaces de documentação e por ferramentas externas.
/// note | Nota
Tenha em mente que ferramentas diferentes podem ter níveis diferentes de suporte ao OpenAPI.
Algumas delas podem ainda não mostrar todas as informações extras declaradas, embora na maioria dos casos o recurso ausente já esteja planejado para desenvolvimento.
///
Você pode adicionar um `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
E uma `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Parâmetros com alias { #alias-parameters }
Imagine que você queira que o parâmetro seja `item-query`.
Assim:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Mas `item-query` não é um nome de variável Python válido.
O mais próximo seria `item_query`.
Mas você ainda precisa que seja exatamente `item-query`...
Então você pode declarar um `alias`, e esse alias será usado para encontrar o valor do parâmetro:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Descontinuando parâmetros { #deprecating-parameters }
Agora digamos que você não gosta mais desse parâmetro.
Você tem que deixá-lo por um tempo, pois há clientes usando-o, mas quer que a documentação mostre claramente que ele está <abbr title="obsoleto, recomenda-se não usá-lo">descontinuado</abbr>.
Então passe o parâmetro `deprecated=True` para `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
A documentação vai mostrar assim:
<img src="/img/tutorial/query-params-str-validations/image01.png">
## Excluir parâmetros do OpenAPI { #exclude-parameters-from-openapi }
Para excluir um parâmetro de consulta do OpenAPI gerado (e portanto, dos sistemas de documentação automáticos), defina o parâmetro `include_in_schema` de `Query` como `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Validação personalizada { #custom-validation }
Podem existir casos em que você precise fazer alguma **validação personalizada** que não pode ser feita com os parâmetros mostrados acima.
Nesses casos, você pode usar uma **função validadora personalizada** que é aplicada após a validação normal (por exemplo, depois de validar que o valor é uma `str`).
Você pode fazer isso usando o <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` do Pydantic</a> dentro de `Annotated`.
/// tip | Dica
O Pydantic também tem <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> e outros. 🤓
///
Por exemplo, este validador personalizado verifica se o ID do item começa com `isbn-` para um número de livro <abbr title="ISBN significa Número Padrão Internacional de Livro">ISBN</abbr> ou com `imdb-` para um ID de URL de filme <abbr title="IMDB (Internet Movie Database) é um site com informações sobre filmes">IMDB</abbr>:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Informação
Isso está disponível com a versão 2 do Pydantic ou superior. 😎
///
/// tip | Dica
Se você precisar fazer qualquer tipo de validação que exija comunicação com algum **componente externo**, como um banco de dados ou outra API, você deve usar **Dependências do FastAPI** em vez disso; você aprenderá sobre elas mais adiante.
Esses validadores personalizados são para coisas que podem ser verificadas **apenas** com os **mesmos dados** fornecidos na requisição.
///
### Entenda esse código { #understand-that-code }
O ponto importante é apenas usar **`AfterValidator` com uma função dentro de `Annotated`**. Sinta-se à vontade para pular esta parte. 🤸
---
Mas se você está curioso sobre este exemplo específico e ainda entretido, aqui vão alguns detalhes extras.
#### String com `value.startswith()` { #string-with-value-startswith }
Percebeu? Uma string usando `value.startswith()` pode receber uma tupla, e verificará cada valor na tupla:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Um item aleatório { #a-random-item }
Com `data.items()` obtemos um <abbr title="Algo que podemos iterar com um laço for, como uma list, set, etc.">objeto iterável</abbr> com tuplas contendo a chave e o valor de cada item do dicionário.
Convertimos esse objeto iterável em uma `list` adequada com `list(data.items())`.
Em seguida, com `random.choice()` podemos obter um **valor aleatório** da lista, então obtemos uma tupla com `(id, name)`. Será algo como `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Depois **atribuímos esses dois valores** da tupla às variáveis `id` e `name`.
Assim, se o usuário não fornecer um ID de item, ele ainda receberá uma sugestão aleatória.
...fazemos tudo isso em **uma única linha simples**. 🤯 Você não ama Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Recapitulando { #recap }
Você pode declarar validações adicionais e metadados para seus parâmetros.
Validações e metadados genéricos:
* `alias`
* `title`
* `description`
* `deprecated`
Validações específicas para strings:
* `min_length`
* `max_length`
* `pattern`
Validações personalizadas usando `AfterValidator`.
Nestes exemplos você viu como declarar validações para valores `str`.
Veja os próximos capítulos para aprender a declarar validações para outros tipos, como números.
+187
View File
@@ -0,0 +1,187 @@
# Parâmetros de Consulta { #query-parameters }
Quando você declara outros parâmetros na função que não fazem parte dos parâmetros da rota, esses parâmetros são automaticamente interpretados como parâmetros de "consulta".
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
A consulta é o conjunto de pares chave-valor que vai depois de `?` na URL, separado pelo caractere `&`.
Por exemplo, na URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...os parâmetros da consulta são:
* `skip`: com o valor `0`
* `limit`: com o valor `10`
Como eles são parte da URL, eles são "naturalmente" strings.
Mas quando você declara eles com os tipos do Python (no exemplo acima, como `int`), eles são convertidos para aquele tipo e validados em relação a ele.
Todo o processo que era aplicado para parâmetros de rota também é aplicado para parâmetros de consulta:
* Suporte do editor (obviamente)
* <abbr title="convertendo uma string que vem de um request HTTP em um dado Python">"Parsing"</abbr> de dados
* Validação de dados
* Documentação automática
## Valores padrão { #defaults }
Como os parâmetros de consulta não são uma parte fixa da rota, eles podem ser opcionais e podem ter valores padrão.
No exemplo acima eles tem valores padrão de `skip=0` e `limit=10`.
Então, se você for até a URL:
```
http://127.0.0.1:8000/items/
```
Seria o mesmo que ir para:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Mas, se por exemplo você for para:
```
http://127.0.0.1:8000/items/?skip=20
```
Os valores dos parâmetros na sua função serão:
* `skip=20`: Por que você definiu isso na URL
* `limit=10`: Por que esse era o valor padrão
## Parâmetros opcionais { #optional-parameters }
Da mesma forma, você pode declarar parâmetros de consulta opcionais, definindo o valor padrão para `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
Nesse caso, o parâmetro da função `q` será opcional, e `None` será o padrão.
/// check | Verifique
Você também pode notar que o **FastAPI** é esperto o suficiente para perceber que o parâmetro da rota `item_id` é um parâmetro da rota, e `q` não é, portanto, `q` é o parâmetro de consulta.
///
## Conversão dos tipos de parâmetros de consulta { #query-parameter-type-conversion }
Você também pode declarar tipos `bool`, e eles serão convertidos:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Nesse caso, se você for para:
```
http://127.0.0.1:8000/items/foo?short=1
```
ou
```
http://127.0.0.1:8000/items/foo?short=True
```
ou
```
http://127.0.0.1:8000/items/foo?short=true
```
ou
```
http://127.0.0.1:8000/items/foo?short=on
```
ou
```
http://127.0.0.1:8000/items/foo?short=yes
```
ou qualquer outra variação (tudo em maiúscula, primeira letra em maiúscula, etc), a sua função vai ver o parâmetro `short` com um valor `bool` de `True`. Caso contrário `False`.
## Múltiplos parâmetros de rota e consulta { #multiple-path-and-query-parameters }
Você pode declarar múltiplos parâmetros de rota e parâmetros de consulta ao mesmo tempo, o **FastAPI** vai saber o quê é o quê.
E você não precisa declarar eles em nenhuma ordem específica.
Eles serão detectados pelo nome:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Parâmetros de consulta obrigatórios { #required-query-parameters }
Quando você declara um valor padrão para parâmetros que não são de rota (até agora, nós vimos apenas parâmetros de consulta), então eles não são obrigatórios.
Caso você não queira adicionar um valor específico mas queira apenas torná-lo opcional, defina o valor padrão como `None`.
Porém, quando você quiser fazer com que o parâmetro de consulta seja obrigatório, você pode simplesmente não declarar nenhum valor como padrão.
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
Aqui o parâmetro de consulta `needy` é um valor obrigatório, do tipo `str`.
Se você abrir no seu navegador a URL:
```
http://127.0.0.1:8000/items/foo-item
```
... sem adicionar o parâmetro obrigatório `needy`, você verá um erro como:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Como `needy` é um parâmetro obrigatório, você precisaria defini-lo na URL:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...isso deve funcionar:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
E claro, você pode definir alguns parâmetros como obrigatórios, alguns possuindo um valor padrão, e outros sendo totalmente opcionais:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
Nesse caso, existem 3 parâmetros de consulta:
* `needy`, um `str` obrigatório.
* `skip`, um `int` com o valor padrão `0`.
* `limit`, um `int` opcional.
/// tip | Dica
Você também poderia usar `Enum` da mesma forma que com [Path Parameters](path-params.md#predefined-values){.internal-link target=_blank}.
///
+176
View File
@@ -0,0 +1,176 @@
# Arquivos de Requisição { #request-files }
Você pode definir arquivos para serem enviados pelo cliente usando `File`.
/// info | Informação
Para receber arquivos enviados, primeiro instale o <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Garanta que você criou um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, o ativou e então o instalou, por exemplo:
```console
$ pip install python-multipart
```
Isso é necessário, visto que os arquivos enviados são enviados como "dados de formulário".
///
## Importe `File` { #import-file }
Importe `File` e `UploadFile` de `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
## Definir Parâmetros `File` { #define-file-parameters }
Crie parâmetros de arquivo da mesma forma que você faria para `Body` ou `Form`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
/// info | Informação
`File` é uma classe que herda diretamente de `Form`.
Mas lembre-se que quando você importa `Query`, `Path`, `File` e outros de `fastapi`, eles são, na verdade, funções que retornam classes especiais.
///
/// tip | Dica
Para declarar corpos de arquivos, você precisa usar `File`, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta ou parâmetros de corpo (JSON).
///
Os arquivos serão enviados como "dados de formulário".
Se você declarar o tipo do parâmetro da função da sua *operação de rota* como `bytes`, o **FastAPI** lerá o arquivo para você e você receberá o conteúdo como `bytes`.
Mantenha em mente que isso significa que todo o conteúdo será armazenado na memória. Isso funcionará bem para arquivos pequenos.
Mas há muitos casos em que você pode se beneficiar do uso de `UploadFile`.
## Parâmetros de Arquivo com `UploadFile` { #file-parameters-with-uploadfile }
Defina um parâmetro de arquivo com um tipo de `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
Utilizar `UploadFile` tem várias vantagens sobre `bytes`:
* Você não precisa utilizar o `File()` no valor padrão do parâmetro.
* Ele utiliza um arquivo "spooled":
* Um arquivo armazenado na memória até um limite máximo de tamanho, e após passar esse limite, ele será armazenado no disco.
* Isso significa que funcionará bem para arquivos grandes como imagens, vídeos, binários grandes, etc., sem consumir toda a memória.
* Você pode receber metadados do arquivo enviado.
* Ele tem uma <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> interface `assíncrona`.
* Ele expõe um objeto python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> que você pode passar diretamente para outras bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
### `UploadFile` { #uploadfile }
`UploadFile` tem os seguintes atributos:
* `filename`: Uma `str` com o nome do arquivo original que foi enviado (por exemplo, `myimage.jpg`).
* `content_type`: Uma `str` com o tipo de conteúdo (MIME type / media type) (por exemplo, `image/jpeg`).
* `file`: Um <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (um <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> objeto). Este é o objeto de arquivo Python que você pode passar diretamente para outras funções ou bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
`UploadFile` tem os seguintes métodos `assíncronos`. Todos eles chamam os métodos de arquivo correspondentes por baixo dos panos (usando o `SpooledTemporaryFile` interno).
* `write(data)`: Escreve `data` (`str` ou `bytes`) no arquivo.
* `read(size)`: Lê `size` (`int`) bytes/caracteres do arquivo.
* `seek(offset)`: Vai para o byte na posição `offset` (`int`) no arquivo.
* Por exemplo, `await myfile.seek(0)` irá para o início do arquivo.
* Isso é especialmente útil se você executar `await myfile.read()` uma vez e precisar ler o conteúdo novamente.
* `close()`: Fecha o arquivo.
Como todos esses métodos são métodos `assíncronos`, você precisa "aguardar" por eles.
Por exemplo, dentro de uma função de *operação de rota* `assíncrona`, você pode obter o conteúdo com:
```Python
contents = await myfile.read()
```
Se você estiver dentro de uma função de *operação de rota* normal `def`, você pode acessar o `UploadFile.file` diretamente, por exemplo:
```Python
contents = myfile.file.read()
```
/// note | Detalhes Técnicos do `async`
Quando você usa os métodos `async`, o **FastAPI** executa os métodos de arquivo em um threadpool e aguarda por eles.
///
/// note | Detalhes Técnicos do Starlette
O `UploadFile` do **FastAPI** herda diretamente do `UploadFile` do **Starlette**, mas adiciona algumas partes necessárias para torná-lo compatível com o **Pydantic** e as outras partes do FastAPI.
///
## O que é "Form Data" { #what-is-form-data }
O jeito que os formulários HTML (`<form></form>`) enviam os dados para o servidor normalmente usa uma codificação "especial" para esses dados, a qual é diferente do JSON.
**FastAPI** se certificará de ler esses dados do lugar certo, ao invés de JSON.
/// note | Detalhes Técnicos
Dados de formulários normalmente são codificados usando o "media type" `application/x-www-form-urlencoded` quando não incluem arquivos.
Mas quando o formulário inclui arquivos, ele é codificado como `multipart/form-data`. Se você usar `File`, o **FastAPI** saberá que tem que pegar os arquivos da parte correta do corpo da requisição.
Se você quiser ler mais sobre essas codificações e campos de formulário, vá para a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network Rede de Desenvolvedores da Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
///
/// warning | Atenção
Você pode declarar múltiplos parâmetros `File` e `Form` em uma *operação de rota*, mas você não pode declarar campos `Body` que você espera receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
///
## Upload de Arquivo Opcional { #optional-file-upload }
Você pode tornar um arquivo opcional usando anotações de tipo padrão e definindo um valor padrão de `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` com Metadados Adicionais { #uploadfile-with-additional-metadata }
Você também pode usar `File()` com `UploadFile`, por exemplo, para definir metadados adicionais:
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
## Uploads de Múltiplos Arquivos { #multiple-file-uploads }
É possível realizar o upload de vários arquivos ao mesmo tempo.
Eles serão associados ao mesmo "campo de formulário" enviado usando "dados de formulário".
Para usar isso, declare uma lista de `bytes` ou `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
Você receberá, tal como declarado, uma `list` de `bytes` ou `UploadFile`.
/// note | Detalhes Técnicos
Você pode também pode usar `from starlette.responses import HTMLResponse`.
**FastAPI** providencia o mesmo `starlette.responses` que `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
///
### Uploads de Múltiplos Arquivos com Metadados Adicionais { #multiple-file-uploads-with-additional-metadata }
Da mesma forma de antes, você pode usar `File()` para definir parâmetros adicionais, mesmo para `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
## Recapitulando { #recap }
Utilize `File`, `bytes` e `UploadFile` para declarar arquivos a serem enviados na requisição, enviados como dados de formulário.
@@ -0,0 +1,78 @@
# Modelos de Formulários { #form-models }
Você pode utilizar **Modelos Pydantic** para declarar **campos de formulários** no FastAPI.
/// info | Informação
Para utilizar formulários, instale primeiramente o <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo, e então instalar. Por exemplo:
```console
$ pip install python-multipart
```
///
/// note | Nota
Isto é suportado desde a versão `0.113.0` do FastAPI. 🤓
///
## Modelos Pydantic para Formulários { #pydantic-models-for-forms }
Você precisa apenas declarar um **modelo Pydantic** com os campos que deseja receber como **campos de formulários**, e então declarar o parâmetro como um `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
O **FastAPI** irá **extrair** as informações para **cada campo** dos **dados do formulário** na requisição e dar para você o modelo Pydantic que você definiu.
## Confira os Documentos { #check-the-docs }
Você pode verificar na UI de documentação em `/docs`:
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## Proibir Campos Extras de Formulários { #forbid-extra-form-fields }
Em alguns casos de uso especiais (provavelmente não muito comum), você pode desejar **restringir** os campos do formulário para aceitar apenas os declarados no modelo Pydantic. E **proibir** qualquer campo **extra**.
/// note | Nota
Isso é suportado deste a versão `0.114.0` do FastAPI. 🤓
///
Você pode utilizar a configuração de modelo do Pydantic para `proibir` qualquer campo `extra`:
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
Caso um cliente tente enviar informações adicionais, ele receberá um retorno de **erro**.
Por exemplo, se o cliente tentar enviar os campos de formulário:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Ele receberá um retorno de erro informando-o que o campo `extra` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Resumo { #summary }
Você pode utilizar modelos Pydantic para declarar campos de formulários no FastAPI. 😎
@@ -0,0 +1,41 @@
# Formulários e Arquivos da Requisição { #request-forms-and-files }
Você pode definir arquivos e campos de formulário ao mesmo tempo usando `File` e `Form`.
/// info | Informação
Para receber arquivos carregados e/ou dados de formulário, primeiro instale <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então instalar, por exemplo:
```console
$ pip install python-multipart
```
///
## Importe `File` e `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
## Defina parâmetros de `File` e `Form` { #define-file-and-form-parameters }
Crie parâmetros de arquivo e formulário da mesma forma que você faria para `Body` ou `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
Os arquivos e campos de formulário serão carregados como dados de formulário e você receberá os arquivos e campos de formulário.
E você pode declarar alguns dos arquivos como `bytes` e alguns como `UploadFile`.
/// warning | Atenção
Você pode declarar vários parâmetros `File` e `Form` em uma *operação de caminho*, mas não é possível declarar campos `Body` para receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
///
## Recapitulando { #recap }
Usar `File` e `Form` juntos quando precisar receber dados e arquivos na mesma requisição.

Some files were not shown because too many files have changed in this diff Show More