diff --git a/09/index.html b/09/index.html index b766eebe..42df774b 100644 --- a/09/index.html +++ b/09/index.html @@ -1400,7 +1400,7 @@
Adotar essa abordagem evita a exposição das variáveis de ambiente no arquivo de configuração. Esta não foi a abordagem padrão no curso devido à complexidade adicional e à intenção de evitar confusões. Dependendo do ambiente estabelecido pela equipe de DevOps/SRE em um projeto real, essa gestão pode variar entre variáveis de ambiente, arquivos .env
ou soluções mais avançadas como Vault.{:target="_blank"}.
Adotar essa abordagem evita a exposição das variáveis de ambiente no arquivo de configuração. Esta não foi a abordagem padrão no curso devido à complexidade adicional e à intenção de evitar confusões. Dependendo do ambiente estabelecido pela equipe de DevOps/SRE em um projeto real, essa gestão pode variar entre variáveis de ambiente, arquivos .env
ou soluções mais avançadas como Vault.
Se optar por utilizar um arquivo .env
com as configurações do PostgreSQL, configure o Pydantic para ignorar variáveis de ambiente que não são necessárias, adicionando extra='ignore'
a chamada de SettingsConfigDic
:
fast_zero/settings.py | |
---|---|
1 2 diff --git a/12/index.html b/12/index.html index bc18093c..d3b0b5f9 100644 --- a/12/index.html +++ b/12/index.html @@ -818,13 +818,13 @@ 🦖 Quem vai ministrar essas aulas?-Prazer! Eu me chamo Eduardo. Mas as pessoas me conhecem na internet como @dunossauro. +Prazer! Eu me chamo Eduardo. Mas as pessoas me conhecem na internet como @dunossauro.
@@ -1068,7 +1068,7 @@ F.A.Q.Última atualização: - October 24, 2023 + November 27, 2023 diff --git a/search/search_index.json b/search/search_index.json index dc9bfa4e..43174448 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["pt"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"FastAPI do Zero!","text":""},{"location":"#fastapi-do-zero","title":"FastAPI do ZERO","text":"Esse material ainda est\u00e1 em fase de desenvolvimento. Caso encontre algum erro, ficarei extremamente feliz que voc\u00ea me notifique ou envie um Pull Request! Problemas j\u00e1 conhecidos Construindo um Projeto com Bancos de Dados, Testes e Deploy Boas-vindas \u00e0 sua jornada de aprendizado com o framework FastAPI! Neste curso, o foco \u00e9 proporcionar um entendimento pr\u00e1tico das habilidades essenciais para o desenvolvimento eficiente de APIs. Exploraremos temas como integra\u00e7\u00e3o com bancos de dados e implementa\u00e7\u00e3o de testes, oferecendo uma base s\u00f3lida para quem busca trabalhar com essa ferramenta. A abordagem \u00e9 direta e informativa, visando nos equipar com o conhecimento necess\u00e1rio para come\u00e7ar a criar nossos pr\u00f3prios projetos. "},{"location":"#o-que-e-fastapi","title":"O que \u00e9 FastAPI?","text":"FastAPI \u00e9 um framework Python moderno, projetado para simplicidade, velocidade e efici\u00eancia. A combina\u00e7\u00e3o de alto desempenho com anota\u00e7\u00f5es de tipo Python facilita o desenvolvimento de APIs RESTful. "},{"location":"#sobre-o-curso","title":"Sobre o curso","text":"Este curso foi desenvolvido para oferecer uma experi\u00eancia pr\u00e1tica no uso do FastAPI, uma das ferramentas mais modernas para constru\u00e7\u00e3o de APIs. Ao longo do curso, o objetivo \u00e9 que voc\u00ea obtenha uma compreens\u00e3o das funcionalidades do FastAPI e de boas pr\u00e1ticas associadas a ele. O projeto central do curso ser\u00e1 a constru\u00e7\u00e3o de um gerenciador de tarefas (uma lista de tarefas), come\u00e7ando do zero. Esse projeto incluir\u00e1 a implementa\u00e7\u00e3o da autentica\u00e7\u00e3o do usu\u00e1rio e das opera\u00e7\u00f5es CRUD completas. Para a constru\u00e7\u00e3o do projeto, ser\u00e3o utilizadas as vers\u00f5es mais recentes das ferramentas, dispon\u00edveis em 2023, como a vers\u00e3o 0.100 do FastAPI, a vers\u00e3o 2.0 do Pydantic, a vers\u00e3o 2.0 do SQLAlchemy ORM, al\u00e9m do Python 3.11 e do Alembic para gerenciamento de migra\u00e7\u00f5es. Al\u00e9m da constru\u00e7\u00e3o do projeto, o curso tamb\u00e9m incluir\u00e1 a pr\u00e1tica de testes, utilizando o pytest. Essa abordagem tem como objetivo garantir que as APIs desenvolvidas sejam n\u00e3o apenas funcionais, mas tamb\u00e9m robustas e confi\u00e1veis. "},{"location":"#o-que-voce-vai-aprender","title":"O que voc\u00ea vai aprender?","text":"Aqui est\u00e1 uma vis\u00e3o geral dos t\u00f3picos que vamos abordar neste curso:
SIM! Esse curso foi todo desenvolvido de forma aberta e com a ajuda financeira de pessoas incr\u00edveis. Caso voc\u00ea sinta vontade de contribuir, voc\u00ea pode me pagar um caf\u00e9 por pix (pix.dunossauro@gmail.com) ou apoiar a campanha recorrente de financiamento coletivo da live de python que \u00e9 o que paga as contas aqui de casa. "},{"location":"#onde-o-curso-sera-disponibilizado","title":"Onde o curso ser\u00e1 disponibilizado?","text":"Esse material est\u00e1 em fase de desenvolvimento e todas as aulas estar\u00e3o dispon\u00edveis no meu canal do YouTube. Voc\u00ea pode conferir outros materiais dispon\u00edveis por l\u00e1 enquanto os v\u00eddeos n\u00e3o saem, ou se inscrever para ser notificado quando os v\u00eddeos sa\u00edrem! http://youtube.com/@dunossauro Aqui estar\u00e1 listada a playlist quando dispon\u00edvel! "},{"location":"#pre-requisitos","title":"Pr\u00e9-requisitos","text":"Para aproveitar ao m\u00e1ximo este curso, \u00e9 recomendado que voc\u00ea tenha algum conhecimento pr\u00e9vio de Python. Al\u00e9m disso, algum entendimento b\u00e1sico de desenvolvimento web e APIs RESTful ser\u00e1 \u00fatil, mas n\u00e3o essencial, pois a abordagem deste curso \u00e9 pr\u00e1tica e centrada em um projeto concreto. Atrav\u00e9s de exemplos reais e instru\u00e7\u00f5es passo a passo, voc\u00ea ter\u00e1 a oportunidade de acompanhar o processo de constru\u00e7\u00e3o de uma aplica\u00e7\u00e3o real. Mesmo que os conceitos de desenvolvimento web sejam novos para voc\u00ea, a \u00eanfase na aplica\u00e7\u00e3o pr\u00e1tica e a estrutura detalhada do curso facilitar\u00e3o o entendimento e a aplica\u00e7\u00e3o dessas habilidades at\u00e9 o fim do processo. Caso esteja iniciando seus estudos em Python!Caso voc\u00ea ainda n\u00e3o se sinta uma pessoa preparada, ou caiu aqui sem saber exatamente o que esperar. Temos um pequeno curso introdut\u00f3rio. Destinado aos primeiros passos com python. Link direto Tamb\u00e9m temos uma live focada em dicas para iniciar os estudos em python Link direto Ou ent\u00e3o a leitura do livro Pense em python "},{"location":"#aulas","title":"Aulas","text":"
Prazer! Eu me chamo Eduardo. Mas as pessoas me conhecem na internet como @dunossauro. Eu sou um programador Python muito empolgado e curioso. Toco um projeto pessoal chamado Live de Python h\u00e1 pouco mais de 6 anos. Onde conversamos sobre tudo e mais um pouco quando o assunto \u00e9 Python. Esse projeto que estamos desenvolvendo \u00e9 um peda\u00e7o, um projeto, de um grande curso de FastAPI que estou montando. Espero que voc\u00ea se divirta ao m\u00e1ximo com a parte pr\u00e1tica enquanto escrevo em mais detalhes todo o potencial te\u00f3rico que lan\u00e7arei no futuro! Caso queira saber mais sobre esse projeto completo. "},{"location":"#licenca","title":"\ud83d\udcd6 Licen\u00e7a","text":"Todo esse curso foi escrito e produzido por Eduardo Mendes (@dunossauro). Todo esse material \u00e9 gratuito e est\u00e1 sob licen\u00e7a Creative Commons BY-NC-SA. O que quer dizer que:
Pontos de aten\u00e7\u00e3o:
Toda essa p\u00e1gina foi feita usando as seguintes bibliotecas:
Para os slides:
O versionamento de tudo est\u00e1 sendo feito no reposit\u00f3rio do curso Github "},{"location":"#deploy","title":"\ud83d\ude80 Deploy","text":"Os deploys das p\u00e1ginas est\u00e1ticas geradas pelo MkDocs est\u00e3o sendo feitos no Netlify "},{"location":"#conclusao","title":"Conclus\u00e3o","text":"Neste curso, a inten\u00e7\u00e3o \u00e9 fornecer uma compreens\u00e3o completa do framework FastAPI, utilizando-o para construir uma aplica\u00e7\u00e3o de gerenciamento de tarefas. O aprendizado ser\u00e1 focado na pr\u00e1tica, e cada conceito ser\u00e1 acompanhado por exemplos e exerc\u00edcios relevantes. A jornada come\u00e7ar\u00e1 com a configura\u00e7\u00e3o do ambiente de desenvolvimento e introdu\u00e7\u00e3o ao FastAPI. Ao longo das aulas, abordaremos t\u00f3picos como autentica\u00e7\u00e3o, opera\u00e7\u00f5es CRUD, testes com pytest e deploy. A \u00eanfase ser\u00e1 colocada na aplica\u00e7\u00e3o de boas pr\u00e1ticas e no entendimento das ferramentas e tecnologias atualizadas, incluindo as vers\u00f5es mais recentes do FastAPI, Pydantic, SQLAlchemy ORM, Python e Alembic. Este conte\u00fado foi pensado para auxiliar na compreens\u00e3o de como criar uma API eficiente e confi\u00e1vel, dando aten\u00e7\u00e3o a aspectos importantes como testes e integra\u00e7\u00e3o com banco de dados. "},{"location":"#faq","title":"F.A.Q.","text":"Perguntas frequentes que me fizeram durante os v\u00eddeos
Objetivos dessa aula:
Aula Slides C\u00f3digo Nesta aula pr\u00e1tica, vamos come\u00e7ar nossa jornada na constru\u00e7\u00e3o de uma API com FastAPI. Esse \u00e9 um moderno e r\u00e1pido (altamente perform\u00e1tico) framework web para constru\u00e7\u00e3o de APIs com Python 3.7+ baseado em Python type hints. Partiremos do b\u00e1sico, configurando nosso ambiente de desenvolvimento. Discutiremos desde a escolha e instala\u00e7\u00e3o da vers\u00e3o correta do Python at\u00e9 a instala\u00e7\u00e3o e configura\u00e7\u00e3o do Poetry, um gerenciador de pacotes e depend\u00eancias para Python. Al\u00e9m disso, instalaremos e configuraremos uma s\u00e9rie de ferramentas de desenvolvimento \u00fateis, como Ruff, Blue, Isort, pytest e Taskipy. Depois de configurado o nosso ambiente, criaremos nosso primeiro programa \"Hello, World!\" com FastAPI. Isso nos permitir\u00e1 confirmar que tudo est\u00e1 funcionando corretamente. E, finalmente, exploraremos uma parte crucial do Desenvolvimento Orientado por Testes (TDD), escrevendo nosso primeiro teste com Pytest. "},{"location":"01/#ambiente-de-desenvolvimento","title":"Ambiente de Desenvolvimento","text":"Para iniciar essa aula voc\u00ea vai precisar de algumas ferramentas.
Se voc\u00ea precisar reconstruir o ambiente usado nesse curso, \u00e9 recomendado que voc\u00ea use o pyenv. Caso tenha problemas durante a instala\u00e7\u00e3o. O pyenv conta com dois assistentes simplificados para sua configura\u00e7\u00e3o. Para windows, use o pyenv-windows. Para GNU/Linux e MacOS, use o pyenv-installer. Navegue at\u00e9 o diret\u00f3rio onde far\u00e1 os exerc\u00edcios e executar\u00e1 os c\u00f3digos de exemplo no seu terminal e digite os seguintes comandos: $ Execu\u00e7\u00e3o no terminal! Certifique que a vers\u00e3o do python 3.11 esteja instalada: $ Execu\u00e7\u00e3o no terminal! A resposta esperada \u00e9 que o Ap\u00f3s instalar o Python, o pr\u00f3ximo passo \u00e9 instalar o Poetry, um gerenciador de pacotes e depend\u00eancias para Python. O Poetry facilita a cria\u00e7\u00e3o, o gerenciamento e a distribui\u00e7\u00e3o de pacotes Python. Caso esse seja seu primeiro contato com o PoetryTemos uma live de python explicando somente ele Link direto Para instalar o Poetry, voc\u00ea pode seguir as instru\u00e7\u00f5es presentes na documenta\u00e7\u00e3o oficial do Poetry para o seu sistema operacional. Alternativamente, se voc\u00ea optou por usar o pipx, pode instalar o Poetry com o seguinte comando: $ Execu\u00e7\u00e3o no terminal! "},{"location":"01/#criacao-do-projeto-fastapi-e-instalacao-das-dependencias","title":"Cria\u00e7\u00e3o do Projeto FastAPI e Instala\u00e7\u00e3o das Depend\u00eancias","text":"Agora que temos o Python e o Poetry prontos, podemos come\u00e7ar a criar nosso projeto FastAPI. Vamos inicialmente criar um novo diret\u00f3rio para nosso projeto e navegar para ele: $ Execu\u00e7\u00e3o no terminal! Ele criar\u00e1 uma estrutura como essa: Para que a vers\u00e3o que instalamos com pyenv seja usada em nosso projeto criado com poetry, devemos dizer ao pyenv qual vers\u00e3o do python ser\u00e1 usada nesse diret\u00f3rio: $ Execu\u00e7\u00e3o no terminal! Em conjunto com essa instru\u00e7\u00e3o, devemos dizer ao poetry que usaremos essa vers\u00e3o em nosso projeto. Para isso vamos alterar o arquivo de configura\u00e7\u00e3o do projeto o Desta forma, temos uma vers\u00e3o do python selecionada para esse projeto e uma garantia que o poetry usar\u00e1 essa vers\u00e3o para a cria\u00e7\u00e3o do nosso ambiente virtual. Em seguida, inicializaremos um novo projeto Python com Poetry e instalaremos as depend\u00eancias necess\u00e1rias - FastAPI e Uvicorn: $ Execu\u00e7\u00e3o no terminal! "},{"location":"01/#primeira-execucao-de-um-hello-world","title":"Primeira Execu\u00e7\u00e3o de um \"Hello, World!\"","text":"Para garantir que tudo est\u00e1 configurado corretamente, vamos criar um pequeno programa \"Hello, World!\" com FastAPI. Em um novo arquivo chamado Agora, podemos iniciar nosso servidor FastAPI com o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Acesse http://localhost:8000 no seu navegador, e voc\u00ea deve ver a mensagem \"Hello, World!\" em formato JSON. "},{"location":"01/#instalando-as-ferramentas-de-desenvolvimento","title":"Instalando as ferramentas de desenvolvimento","text":"As ferramentas de desenvolvimento escolhidas podem variar de acordo com a prefer\u00eancia pessoal. Nesta aula, utilizaremos algumas que s\u00e3o particularmente \u00fateis para demonstrar certos conceitos:
Para instalar as depend\u00eancias, podemos usar um grupo do poetry focado nelas, para n\u00e3o serem usadas em produ\u00e7\u00e3o: $ Execu\u00e7\u00e3o no terminal! O HTTPX foi inclu\u00eddo, pois ele \u00e9 uma depend\u00eancia do cliente de testes do FastAPI. "},{"location":"01/#configurando-as-ferramentas-de-desenvolvimento","title":"Configurando as ferramentas de desenvolvimento","text":"Ap\u00f3s a instala\u00e7\u00e3o das depend\u00eancias, vamos precisar configurar todas as ferramentas de desenvolvimento no arquivo Come\u00e7ando pelo ruff, vamos definir o comprimento de linha para 79 caracteres (conforme sugerido na PEP 8) e em seguida, informaremos que o diret\u00f3rio de ambiente virtual e o de migra\u00e7\u00f5es de banco de dados dever\u00e3o ser ignorados: pyproject.toml "},{"location":"01/#isort","title":"isort","text":"Para evitar conflitos de formata\u00e7\u00e3o entre o isort e o blue, definiremos o black como perfil de formata\u00e7\u00e3o a ser seguido, j\u00e1 que o blue \u00e9 um fork dele. Como o black utiliza 88 caracteres por linha, vamos alterar para 79 que \u00e9 o padr\u00e3o que o blue segue e que tamb\u00e9m estamos seguindo: pyproject.toml "},{"location":"01/#pytest","title":"pytest","text":"Configuraremos o pytest para reconhecer o caminho base para execu\u00e7\u00e3o dos testes na raiz do projeto "},{"location":"01/#blue","title":"blue","text":"Configuraremos o blue para excluir o caminho das migra\u00e7\u00f5es quando essas forem utilizadas: pyproject.toml "},{"location":"01/#taskipy","title":"Taskipy","text":"Para simplificar a execu\u00e7\u00e3o de certos comandos, vamos criar algumas tarefas com o Taskipy. pyproject.toml Os comandos definidos fazem o seguinte:
Para executar um comando, \u00e9 bem mais simples, precisando somente passar a palavra O meu est\u00e1 exatamente assim: pyproject.toml "},{"location":"01/#os-efeitos-dessas-configuracoes-de-desenvolvimento","title":"Os efeitos dessas configura\u00e7\u00f5es de desenvolvimento","text":"Caso voc\u00ea tenha copiado o c\u00f3digo que usamos para definir Dessa forma, veremos que cometemos algumas infra\u00e7\u00f5es na formata\u00e7\u00e3o da PEP-8. O blue nos informar\u00e1 que dever\u00edamos ter adicionado duas linhas antes de uma defini\u00e7\u00e3o de fun\u00e7\u00e3o: Para corrigir isso, podemos usar o nosso comando de formata\u00e7\u00e3o de c\u00f3digo: ComandoResultado $ Execu\u00e7\u00e3o no terminal! fast_zero/app.py "},{"location":"01/#introducao-ao-pytest-testando-o-hello-world","title":"Introdu\u00e7\u00e3o ao Pytest: Testando o \"Hello, World!\"","text":"Antes de entendermos a din\u00e2mica dos testes, precisamos entender o efeito que eles t\u00eam no nosso c\u00f3digo. Um bom lugar para come\u00e7ar isso \u00e9 analisando a cobertura. Vamos executar os testes. $ Execu\u00e7\u00e3o no terminal! Teremos uma resposta como essa: $ Execu\u00e7\u00e3o no terminal! As primeiras duas linhas s\u00e3o referentes ao comando do Temos uma live de Python explicando os conceitos b\u00e1sicos da biblioteca Link direto A parte importante dessa Mensagem est\u00e1 na tabela gerada pelo Por n\u00e3o ter encontrado nenhum teste, o pytest retornou um \"erro\". Isso significa que nossa tarefa Isso gera um relat\u00f3rio de cobertura de testes em formato HTML. Podemos abrir esse arquivo em nosso navegador e entender exatamente quais linhas do c\u00f3digo n\u00e3o est\u00e3o sendo testadas. Se clicarmos no arquivo Isto significa que precisamos testar todo esse arquivo. "},{"location":"01/#escrevendo-o-teste","title":"Escrevendo o teste","text":"Agora, vamos escrever nosso primeiro teste com Pytest. Para testar o FastAPI, precisamos de um cliente de teste. Isso pode ser obtido no m\u00f3dulo S\u00f3 o fato de termos definido um cliente, j\u00e1 nos mostra uma cobertura bastante diferente: $ Execu\u00e7\u00e3o no terminal! Devido ao fato de n\u00e3o ter coletado nenhum teste, o pytest ainda retornou um \"erro\". Para ver a cobertura, precisaremos executar novamente o No navegador, podemos ver que a \u00fanica linha n\u00e3o \"testada\" \u00e9 aquela onde temos a l\u00f3gica do endpoint: No verde vemos o que foi executado quando chamamos o teste, no vermelho o que n\u00e3o foi. Para resolver isso, temos que criar um teste de fato, fazendo uma chamada para nossa API usando o cliente de teste que definimos: tests/test_app.py Esse teste faz uma requisi\u00e7\u00e3o GET no endpoint Dessa forma, temos um teste que coletou 1 item (1 teste). Esse teste foi aprovado e a cobertura n\u00e3o deixou de abranger nenhuma linha de c\u00f3digo. Como conseguimos coletar um item, o Agora que escrevemos nosso teste de forma intuitiva, podemos entender o que cada passo do teste faz. Essa compreens\u00e3o \u00e9 vital, pois pode nos ajudar a escrever testes no futuro com mais confian\u00e7a e efic\u00e1cia. Para desvendar o m\u00e9todo por tr\u00e1s da nossa abordagem, vamos explorar a estrat\u00e9gia conhecida como AAA, que divide o teste em tr\u00eas fases distintas: Arrange, Act, Assert. Caso fazer testes ainda seja complicado para voc\u00eaTemos uma live de python focada em ensinar os primeiros passos no mundo dos testes. Link direto Vamos pegar esse teste que fizemos e entender os passos que fizemos para conseguir testar esse endpoint: tests/test_app.py Com base nesse c\u00f3digo, podemos observar as tr\u00eas fases: "},{"location":"01/#fase-1-organizar-arrange","title":"Fase 1 - Organizar (Arrange)","text":"Nesta primeira etapa, estamos preparando o ambiente para o teste. No exemplo, a linha com o coment\u00e1rio Aqui \u00e9 a etapa onde acontece a a\u00e7\u00e3o principal do teste, que consiste em chamar o Sistema Sob Teste (SUT). No nosso caso, o SUT \u00e9 a rota Esta \u00e9 a etapa de verificar se tudo correu como esperado. \u00c9 f\u00e1cil notar onde estamos fazendo a verifica\u00e7\u00e3o, pois essa linha sempre tem a palavra reservada Agora que compreendemos o que cada linha de teste faz em espec\u00edfico, podemos nos orientar de forma clara nos testes que escreveremos no futuro. Cada uma das linhas usadas tem uma raz\u00e3o de estar no teste, e conhecer essa estrutura n\u00e3o s\u00f3 nos d\u00e1 uma compreens\u00e3o mais profunda do que estamos fazendo, mas tamb\u00e9m nos d\u00e1 confian\u00e7a para explorar e escrever testes mais complexos. "},{"location":"01/#criando-nosso-repositorio-no-git","title":"Criando nosso reposit\u00f3rio no git","text":"Antes de concluirmos a aula, precisamos criar nosso reposit\u00f3rio no git e criar nosso arquivo "},{"location":"01/#conclusao","title":"Conclus\u00e3o","text":"Pronto! Agora temos um ambiente de desenvolvimento totalmente configurado para come\u00e7ar a trabalhar com FastAPI e j\u00e1 fizemos nossa primeira imers\u00e3o no Desenvolvimento Orientado por Testes. Na pr\u00f3xima aula, vamos aprofundar na estrutura\u00e7\u00e3o da nossa aplica\u00e7\u00e3o FastAPI. At\u00e9 l\u00e1! "},{"location":"02/","title":"Estruturando o Projeto e Criando Rotas CRUD","text":""},{"location":"02/#estruturando-o-projeto-e-criando-rotas-crud","title":"Estruturando o Projeto e Criando Rotas CRUD","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Boas-vindas de volta \u00e0 nossa s\u00e9rie de cursos \"FastAPI do Zero: Criando um Projeto com Bancos de Dados, Testes e Deploy\". Hoje, na Aula 3, avan\u00e7aremos na estrutura\u00e7\u00e3o do nosso projeto FastAPI e implementar todas as rotas CRUD (Criar, Ler, Atualizar e Deletar) para o nosso recurso de usu\u00e1rio. "},{"location":"02/#o-que-e-uma-api","title":"O que \u00e9 uma API?","text":"Acr\u00f4nimo de Application Programming Interface (Interface de Programa\u00e7\u00e3o de Aplica\u00e7\u00f5es), uma API \u00e9 um conjunto de regras e protocolos que permitem a comunica\u00e7\u00e3o entre diferentes softwares. As APIs servem como uma ponte entre diferentes programas, permitindo que eles se comuniquem e compartilhem informa\u00e7\u00f5es de maneira eficiente e segura. No mundo moderno, as APIs geralmente comunicam usando o formato de dados JSON (JavaScript Object Notation), que \u00e9 uma maneira leve e eficiente de transmitir dados entre a API e o cliente. As APIs s\u00e3o fundamentais no mundo da programa\u00e7\u00e3o moderna, ao permitirem a intera\u00e7\u00e3o entre diferentes sistemas, independentemente de como foram projetados ou em que linguagem foram escritos. "},{"location":"02/#o-que-e-http","title":"O que \u00e9 HTTP?","text":"HTTP, ou Hypertext Transfer Protocol (Protocolo de Transfer\u00eancia de Hipertexto), \u00e9 o protocolo fundamental na web para a transfer\u00eancia de dados e comunica\u00e7\u00e3o entre clientes e servidores. No contexto das APIs, o HTTP \u00e9 o protocolo que permite a comunica\u00e7\u00e3o entre o cliente (geralmente o navegador de um usu\u00e1rio, mas pode ser qualquer coisa que saiba como fazer solicita\u00e7\u00f5es HTTP) e o servidor onde a API est\u00e1 hospedada. As informa\u00e7\u00f5es entre o cliente e o servidor s\u00e3o trocadas na forma de JSON, tornando-se uma linguagem universal para a troca de informa\u00e7\u00f5es na web. O HTTP \u00e9 baseado no modelo de requisi\u00e7\u00e3o-resposta: o cliente faz uma requisi\u00e7\u00e3o para o servidor, e o servidor responde a essa requisi\u00e7\u00e3o. Essas requisi\u00e7\u00f5es e respostas s\u00e3o formatadas de acordo com as regras do protocolo HTTP. A seguir, vamos explorar como os verbos HTTP, os c\u00f3digos de resposta e os c\u00f3digos de erro s\u00e3o utilizados para gerenciar a comunica\u00e7\u00e3o entre o cliente e a API. "},{"location":"02/#compreendendo-os-verbos-http-codigos-de-resposta-e-codigos-de-erro","title":"Compreendendo os Verbos HTTP, C\u00f3digos de Resposta e C\u00f3digos de Erro","text":"Quando trabalhamos com APIs REST, o uso apropriado dos verbos HTTP, c\u00f3digos de resposta e c\u00f3digos de erro \u00e9 crucial para criar uma API clara e consistente. "},{"location":"02/#verbos-http","title":"Verbos HTTP","text":"Os verbos HTTP indicam a a\u00e7\u00e3o desejada a ser executada em um determinado recurso. Os verbos mais comuns s\u00e3o:
Os c\u00f3digos de resposta HTTP informam ao cliente sobre o resultado de sua solicita\u00e7\u00e3o. Aqui est\u00e3o alguns dos c\u00f3digos de resposta mais comuns:
Os c\u00f3digos de erro HTTP indicam que houve um problema com a solicita\u00e7\u00e3o. Alguns c\u00f3digos de erro comuns incluem:
Ao trabalhar com APIs REST, \u00e9 importante lidar corretamente com esses c\u00f3digos de resposta e erro para proporcionar uma boa experi\u00eancia para os usu\u00e1rios da API. "},{"location":"02/#como-acontece-a-comunicacao-web-entre-cliente-e-servidor","title":"Como acontece a comunica\u00e7\u00e3o web entre cliente e servidor","text":"A comunica\u00e7\u00e3o entre cliente e servidor na web \u00e9 um processo que ocorre em v\u00e1rias etapas e \u00e9 governado por protocolos de comunica\u00e7\u00e3o espec\u00edficos. O protocolo mais comum \u00e9 o HTTP (Hypertext Transfer Protocol). Essa forma de comunica\u00e7\u00e3o \u00e9 geralmente descrita como stateless, o que significa que cada requisi\u00e7\u00e3o \u00e9 processada de forma independente, sem qualquer conhecimento das requisi\u00e7\u00f5es anteriores. A informa\u00e7\u00e3o \u00e9 trocada na forma de mensagens HTTP, que cont\u00eam dados e informa\u00e7\u00f5es sobre como esses dados devem ser processados. Um aspecto fundamental dessa comunica\u00e7\u00e3o \u00e9 a troca de dados na forma de objetos JSON, que s\u00e3o uma maneira eficiente e flex\u00edvel de representar dados estruturados. Este diagrama representa a sequ\u00eancia b\u00e1sica de uma comunica\u00e7\u00e3o cliente-servidor usando HTTP e JSON:
Essa \u00e9 uma vis\u00e3o geral simplificada do processo. Na pr\u00e1tica, a comunica\u00e7\u00e3o entre cliente e servidor pode envolver muitas outras nuances, como autentica\u00e7\u00e3o, redirecionamento, cookies e muito mais. "},{"location":"02/#pydantic-e-a-validacao-de-dados","title":"Pydantic e a valida\u00e7\u00e3o de dados","text":"Antes de mergulharmos no c\u00f3digo, vamos entender alguns conceitos importantes. Caso esse seja seu primeiro contato com PydanticTemos uma live de python exclusiva sobre esse assunto Link direto O Pydantic \u00e9 uma biblioteca Python que oferece valida\u00e7\u00e3o de dados e configura\u00e7\u00f5es usando anota\u00e7\u00f5es de tipos Python. Ela \u00e9 utilizada extensivamente com o FastAPI para lidar com a valida\u00e7\u00e3o e serializa\u00e7\u00e3o/desserializa\u00e7\u00e3o de dados. O Pydantic tem um papel crucial ao trabalhar com JSON, pois permite a valida\u00e7\u00e3o dos dados recebidos neste formato, assim como sua convers\u00e3o para formatos nativos do Python e vice-versa. O uso do Pydantic nos permite definir modelos de dados, ou \"esquemas\", com campos anotados com tipos de dados. O Pydantic garante que as inst\u00e2ncias desses modelos sempre estejam em conformidade com o esquema definido. Esquemas: No contexto da programa\u00e7\u00e3o, um esquema \u00e9 uma representa\u00e7\u00e3o estrutural de um objeto ou entidade. Por exemplo, no nosso caso, um usu\u00e1rio pode ser representado por um esquema que cont\u00e9m campos para nome de usu\u00e1rio, e-mail e senha. Esquemas s\u00e3o \u00fateis porque permitem definir a estrutura de um objeto de uma maneira clara e reutiliz\u00e1vel. Valida\u00e7\u00e3o de dados: Este \u00e9 o processo de verificar se os dados recebidos est\u00e3o em conformidade com as regras e restri\u00e7\u00f5es definidas. Por exemplo, se esperamos que o campo \"email\" contenha um endere\u00e7o de e-mail v\u00e1lido, a valida\u00e7\u00e3o de dados garantir\u00e1 que os dados inseridos nesse campo de fato correspondam a um formato de e-mail v\u00e1lido. Vamos considerar um exemplo onde recebemos o seguinte objeto JSON, representando um novo usu\u00e1rio que quer se registrar em nosso servi\u00e7o: Para lidar com esta entrada de dados, devemos definir um esquema Pydantic que corresponda \u00e0 estrutura deste objeto JSON. Usamos anota\u00e7\u00f5es de tipos Python para definir o tipo de dado de cada campo: Neste exemplo, o campo Ao usar este esquema, qualquer tentativa de criar um usu\u00e1rio com dados que n\u00e3o correspondam a este formato (por exemplo, um email que n\u00e3o \u00e9 v\u00e1lido, ou um campo de nome de usu\u00e1rio que n\u00e3o \u00e9 uma string) resultar\u00e1 em um erro de valida\u00e7\u00e3o. "},{"location":"02/#suporte-a-emails","title":"Suporte a emails","text":"Para que o Pydantic suporte a valida\u00e7\u00e3o de emails, \u00e9 necess\u00e1rio instalar o Ademais, se tentarmos criar um usu\u00e1rio com um email inv\u00e1lido, o Pydantic ir\u00e1 automaticamente validar o campo e retornar um erro \u00fatil. Isso nos poupa muito trabalho de valida\u00e7\u00e3o manual e ajuda a manter nossa API robusta e confi\u00e1vel. "},{"location":"02/#implementando-as-rotas-crud","title":"Implementando as Rotas CRUD","text":"CRUD \u00e9 um acr\u00f4nimo que representa as quatro opera\u00e7\u00f5es b\u00e1sicas que voc\u00ea pode realizar em qualquer banco de dados persistente:
Os c\u00f3digos de status HTTP s\u00e3o usados para indicar o resultado de cada opera\u00e7\u00e3o CRUD. Por exemplo, uma solicita\u00e7\u00e3o POST bem-sucedida (create) retorna o status HTTP 201 (Criado), enquanto uma solicita\u00e7\u00e3o GET bem-sucedida (read) retorna o status HTTP 200 (OK). \u00c9 importante notar que, ao trabalhar com FastAPI e Pydantic, nossos esquemas desempenham um papel vital na opera\u00e7\u00e3o de \"Create\" (criar). Ao usar a opera\u00e7\u00e3o POST para adicionar um novo registro ao nosso banco de dados, vamos aproveitar a valida\u00e7\u00e3o de dados do Pydantic para garantir que o novo registro esteja em conformidade com o esquema do nosso modelo de dados. Se os dados enviados na solicita\u00e7\u00e3o POST n\u00e3o passarem na valida\u00e7\u00e3o do Pydantic, nossa API retornar\u00e1 um c\u00f3digo de status HTTP 422 (Unprocessable Entity), indicando que os dados fornecidos s\u00e3o inv\u00e1lidos ou incompletos. Agora que temos uma compreens\u00e3o clara do que \u00e9 o CRUD, como se relaciona com os verbos HTTP, os c\u00f3digos de status e a valida\u00e7\u00e3o do Pydantic, podemos passar para a implementa\u00e7\u00e3o dessas opera\u00e7\u00f5es em nossa API FastAPI. Na nossa API, vamos criar rotas correspondentes para cada opera\u00e7\u00e3o CRUD, come\u00e7ando com a opera\u00e7\u00e3o \"create\" (criar), que ser\u00e1 implementada pela rota POST. "},{"location":"02/#implementando-a-rota-post","title":"Implementando a Rota POST","text":"A rota POST \u00e9 usada para criar um novo usu\u00e1rio em nosso sistema. Lembrando, o verbo HTTP POST est\u00e1 relacionado \u00e0 opera\u00e7\u00e3o \"Create\" do CRUD. Se tudo ocorrer como esperado e um novo usu\u00e1rio for criado com sucesso, a rota deve retornar o status HTTP 201 (Criado). Para a cria\u00e7\u00e3o dessa rota, vamos usar de base o JSON que criamos anteriormente. Para que a pessoa se cadastre na nossa plataforma, ela precisa enviar os dados de nome de usu\u00e1rio, email e senha: Para isso, vamos criar um esquema Pydantic equivalente em um arquivo de esquemas: Agora vamos criar nosso endpoint que esperar\u00e1 receber esse esquema Pydantic e retornar\u00e1 201, caso o JSON enviado seja v\u00e1lido: fast_zero/app.py Com esse endpoint criado, podemos executar a nossa aplica\u00e7\u00e3o: $ Execu\u00e7\u00e3o no terminal! E acessar a p\u00e1gina http://localhost:8000/docs. Isso nos mostrar\u00e1 as defini\u00e7\u00f5es do nosso endpoint usando o Swagger. Dessa forma, podemos testar de forma simplificada a nossa API, enviando o JSON e realizando alguns testes. Entretanto, precisamos prestar aten\u00e7\u00e3o a um detalhe: nosso modelo retorna a senha do usu\u00e1rio, o que \u00e9 uma p\u00e9ssima pr\u00e1tica de seguran\u00e7a. Para evitar isso, podemos criar um novo modelo que ser\u00e1 usado somente para resposta. Dessa forma, n\u00e3o expomos os dados que n\u00e3o queremos na API: fast_zero/schemas.py Precisamos tamb\u00e9m dizer ao FastAPI que esse ser\u00e1 o modelo de resposta, e converter nosso Note que somente adicionando o Agora, se fizermos de novo a chamada no Swagger, receberemos o mesmo objeto, mas sem expor a senha. Caso nunca tenha usado o SwaggerTemos uma live focada em OpenAPI, que s\u00e3o as especifica\u00e7\u00f5es do Swagger Link direto "},{"location":"02/#criando-um-banco-de-dados-falso","title":"Criando um banco de dados falso","text":"Finalmente, para brincar com essas rotas, podemos criar uma lista provis\u00f3ria para simular um banco de dados. Assim, podemos adicionar nossos dados e entender como o FastAPI funciona. Para isso, adicionamos uma lista provis\u00f3ria para o \"banco\" e alteramos nosso endpoint para inserir nossos modelos do Pydantic nessa lista: fast_zero/app.py Se queremos uma simula\u00e7\u00e3o de banco de dados, precisamos ter um Dessa forma, nada muda. No entanto, podemos prosseguir com a constru\u00e7\u00e3o dos outros endpoints. E lembre-se, \u00e9 importante testar esse endpoint para garantir que tudo esteja funcionando corretamente. "},{"location":"02/#implementando-o-teste-da-rota-post","title":"Implementando o teste da rota POST","text":"Antes de criar o teste de fato, vamos execut\u00e1-los para ver como anda a nossa cobertura: $ Execu\u00e7\u00e3o no terminal! Vemos que temos 3 Miss. Possivelmente das linhas que acabamos de escrever. Ent\u00e3o, vamos escrever nosso teste. Esse teste para a rota POST precisa verificar se a cria\u00e7\u00e3o de um novo usu\u00e1rio funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o POST com um novo usu\u00e1rio para a rota /users/. Em seguida, verificamos se a resposta tem o status HTTP 201 (Criado) e se a resposta cont\u00e9m o novo usu\u00e1rio criado. tests/test_app.py Ao executar o teste: $ Execu\u00e7\u00e3o no terminal! "},{"location":"02/#nao-se-repita-dry","title":"N\u00e3o se repita (DRY)","text":"Voc\u00ea deve ter notado que a linha Para solucionar essa repeti\u00e7\u00e3o, podemos usar uma funcionalidade do pytest chamada Fixture. Uma fixture \u00e9 como uma fun\u00e7\u00e3o que prepara dados ou estado necess\u00e1rios para o teste. Pode ser pensada como uma forma de n\u00e3o repetir a fase de Arrange de um teste, simplificando a chamada e n\u00e3o repetindo c\u00f3digo. Se fixtures s\u00e3o uma novidade para voc\u00eaExiste uma live de Python onde discutimos especificamente sobre fixtures Link direto Neste caso, vamos criar uma fixture que retorna nosso Agora, em vez de repetir a cria\u00e7\u00e3o do Com essa simples mudan\u00e7a, conseguimos tornar nosso c\u00f3digo mais limpo e f\u00e1cil de manter, seguindo o princ\u00edpio DRY. Vemos que estamos no caminho certo. Agora que a rota POST est\u00e1 implementada, vamos seguir para a pr\u00f3xima opera\u00e7\u00e3o CRUD: Read. "},{"location":"02/#implementando-a-rota-get","title":"Implementando a Rota GET","text":"A rota GET \u00e9 usada para recuperar informa\u00e7\u00f5es de um ou mais usu\u00e1rios do nosso sistema. No contexto do CRUD, o verbo HTTP GET est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Read\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). Para estruturar a resposta dessa rota, podemos criar um novo modelo chamado Com esse modelo definido, podemos criar nosso endpoint GET. Este endpoint retornar\u00e1 uma inst\u00e2ncia de Com essa implementa\u00e7\u00e3o, nossa API agora pode retornar uma lista de usu\u00e1rios. No entanto, nosso trabalho ainda n\u00e3o acabou. A pr\u00f3xima etapa \u00e9 escrever testes para garantir que nossa rota GET est\u00e1 funcionando corretamente. Isso nos ajudar\u00e1 a identificar e corrigir quaisquer problemas antes de prosseguirmos com a implementa\u00e7\u00e3o de outras rotas. "},{"location":"02/#implementando-o-teste-da-rota-de-get","title":"Implementando o teste da rota de GET","text":"Nosso teste da rota GET tem que verificar se a recupera\u00e7\u00e3o dos usu\u00e1rios est\u00e1 funcionando corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o GET para a rota /users/. Em seguida, verificamos se a resposta tem o status HTTP 200 (OK) e se a resposta cont\u00e9m a lista de usu\u00e1rios. tests/test_app.py Com as rotas POST e GET implementadas, agora podemos criar e recuperar usu\u00e1rios. Vamos implementar a pr\u00f3xima opera\u00e7\u00e3o CRUD: Update. "},{"location":"02/#implementando-a-rota-put","title":"Implementando a Rota PUT","text":"A rota PUT \u00e9 usada para atualizar as informa\u00e7\u00f5es de um usu\u00e1rio existente. No contexto do CRUD, o verbo HTTP PUT est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Update\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). No entanto, se o usu\u00e1rio solicitado n\u00e3o for encontrado, dever\u00edamos retornar o status HTTP 404 (N\u00e3o Encontrado). fast_zero/app.py "},{"location":"02/#implementando-o-teste-da-rota-de-put","title":"Implementando o teste da rota de PUT","text":"Nosso teste da rota PUT precisa verificar se a atualiza\u00e7\u00e3o de um usu\u00e1rio existente funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o PUT com as novas informa\u00e7\u00f5es do usu\u00e1rio para a rota Com as rotas POST, GET e PUT implementadas, agora podemos criar, recuperar e atualizar usu\u00e1rios. A \u00faltima opera\u00e7\u00e3o CRUD que precisamos implementar \u00e9 Delete. "},{"location":"02/#implementando-a-rota-delete","title":"Implementando a Rota DELETE","text":"A rota DELETE \u00e9 usada para excluir um usu\u00e1rio do nosso sistema. No contexto do CRUD, o verbo HTTP DELETE est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Delete\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). No entanto, se o usu\u00e1rio solicitado n\u00e3o for encontrado, dever\u00edamos retornar o status HTTP 404 (N\u00e3o Encontrado). Para transmitir uma mensagem de sucesso ou falha na opera\u00e7\u00e3o de exclus\u00e3o, podemos criar um modelo chamado Agora podemos criar nosso endpoint DELETE. Este endpoint receber\u00e1 o ID do usu\u00e1rio que queremos excluir. Note que, estamos lan\u00e7ando uma exce\u00e7\u00e3o HTTP quando o ID do usu\u00e1rio est\u00e1 fora do range da nossa lista (simula\u00e7\u00e3o do nosso banco de dados). Quando conseguimos excluir o usu\u00e1rio com sucesso, retornamos a mensagem de sucesso em um modelo do tipo Com a implementa\u00e7\u00e3o da rota DELETE conclu\u00edda, \u00e9 fundamental garantirmos que essa rota est\u00e1 funcionando conforme o esperado. Para isso, precisamos escrever testes para essa rota. "},{"location":"02/#implementando-o-teste-da-rota-de-delete","title":"Implementando o teste da rota de DELETE","text":"Nosso teste da rota DELETE precisa verificar se a exclus\u00e3o de um usu\u00e1rio existente funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o DELETE para a rota /users/{user_id}. Em seguida, verificamos se a resposta tem o status HTTP 200 (OK) e se a resposta cont\u00e9m uma mensagem informando que o usu\u00e1rio foi exclu\u00eddo. tests/test_app.py "},{"location":"02/#checando-tudo-antes-do-commit","title":"Checando tudo antes do commit","text":"Antes de fazermos o commit, \u00e9 uma boa pr\u00e1tica checarmos todo o c\u00f3digo, e podemos fazer isso com as a\u00e7\u00f5es que criamos com o taskipy. $ Execu\u00e7\u00e3o no terminal! "},{"location":"02/#commit","title":"Commit","text":"Ap\u00f3s toda essa jornada de aprendizado, constru\u00e7\u00e3o e teste de rotas, chegou a hora de registrar nosso progresso utilizando o git. Fazer commits regulares \u00e9 uma boa pr\u00e1tica, pois mant\u00e9m um hist\u00f3rico detalhado das altera\u00e7\u00f5es e facilita a volta a uma vers\u00e3o anterior do c\u00f3digo, se necess\u00e1rio. Primeiramente, vamos verificar as altera\u00e7\u00f5es feitas no projeto com o comando Em seguida, vamos adicionar todas as altera\u00e7\u00f5es para o pr\u00f3ximo commit. O comando Agora, estamos prontos para fazer o commit. Com o comando Por fim, enviamos nossas altera\u00e7\u00f5es para o reposit\u00f3rio remoto com E pronto! As altera\u00e7\u00f5es est\u00e3o seguras no hist\u00f3rico do git, e podemos continuar com o pr\u00f3ximo passo do projeto. "},{"location":"02/#conclusao","title":"Conclus\u00e3o","text":"Com a implementa\u00e7\u00e3o bem-sucedida das rotas CRUD, demos um passo significativo na constru\u00e7\u00e3o de uma API robusta e funcional com FastAPI. Agora podemos manipular usu\u00e1rios - criar, ler, atualizar e excluir - o que \u00e9 fundamental para muitos sistemas de informa\u00e7\u00e3o. O papel dos testes em cada etapa n\u00e3o pode ser subestimado. Testes n\u00e3o apenas nos ajudam a assegurar que nosso c\u00f3digo est\u00e1 funcionando como esperado, mas tamb\u00e9m nos permitem refinar nossas solu\u00e7\u00f5es e detectar problemas potenciais antes que eles afetem a funcionalidade geral do nosso sistema. Nunca subestime a import\u00e2ncia de executar seus testes sempre que fizer uma altera\u00e7\u00e3o em seu c\u00f3digo! At\u00e9 aqui, no entanto, trabalhamos com um \"banco de dados\" provis\u00f3rio, na forma de uma lista Python, que \u00e9 vol\u00e1til e n\u00e3o persiste os dados de uma execu\u00e7\u00e3o do aplicativo para outra. Para nosso aplicativo ser \u00fatil em um cen\u00e1rio do mundo real, precisamos armazenar nossos dados de forma mais duradoura. \u00c9 a\u00ed que os bancos de dados entram. No pr\u00f3ximo t\u00f3pico, vamos explorar uma das partes mais cr\u00edticas de qualquer aplicativo - a conex\u00e3o e intera\u00e7\u00e3o com um banco de dados. Vamos aprender a integrar nosso aplicativo FastAPI com um banco de dados real, permitindo a persist\u00eancia de nossos dados de usu\u00e1rio entre as sess\u00f5es do aplicativo. "},{"location":"03/","title":"Configurando o banco de dados e gerenciando migra\u00e7\u00f5es com Alembic","text":""},{"location":"03/#configurando-o-banco-de-dados-e-gerenciando-migracoes-com-alembic","title":"Configurando o Banco de Dados e Gerenciando Migra\u00e7\u00f5es com Alembic","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Ol\u00e1 a todos! Se voc\u00ea est\u00e1 chegando agora, recomendamos verificar as aulas anteriores de nosso curso \"FastAPI do Zero: Criando um Projeto com Bancos de Dados, Testes e Deploy\". Hoje, vamos mergulhar no SQLAlchemy e no Alembic, e come\u00e7aremos a configurar nosso banco de dados. Antes de mergulharmos na instala\u00e7\u00e3o e configura\u00e7\u00e3o, vamos esclarecer alguns conceitos. "},{"location":"03/#o-que-e-um-orm-e-por-que-usamos-um","title":"O que \u00e9 um ORM e por que usamos um?","text":"ORM significa Mapeamento Objeto-Relacional. \u00c9 uma t\u00e9cnica de programa\u00e7\u00e3o que vincula (ou mapeia) objetos a registros de banco de dados. Em outras palavras, um ORM permite que voc\u00ea interaja com seu banco de dados, como se voc\u00ea estivesse trabalhando com objetos Python. O SQLAlchemy \u00e9 um exemplo de ORM. Ele permite que voc\u00ea trabalhe com bancos de dados SQL de maneira mais natural aos programadores Python. Em vez de escrever consultas SQL cruas, voc\u00ea pode usar m\u00e9todos e atributos Python para manipular seus registros de banco de dados. Mas por que usar\u00edamos um ORM? Aqui est\u00e3o algumas raz\u00f5es:
Uma boa pr\u00e1tica no desenvolvimento de aplica\u00e7\u00f5es \u00e9 separar as configura\u00e7\u00f5es do c\u00f3digo. Configura\u00e7\u00f5es, como credenciais de banco de dados, s\u00e3o propensas a mudan\u00e7as entre ambientes diferentes (como desenvolvimento, teste e produ\u00e7\u00e3o). Mistur\u00e1-las com o c\u00f3digo pode tornar o processo de mudan\u00e7a entre esses ambientes complicado e propenso a erros. Caso queira saber mais sobre 12 fatoresTemos uma live focada nesse assunto com a participa\u00e7\u00e3o especial do Bruno Rocha Link direto Al\u00e9m disso, expor credenciais de banco de dados e outras informa\u00e7\u00f5es sens\u00edveis no c\u00f3digo-fonte \u00e9 uma pr\u00e1tica de seguran\u00e7a ruim. Se esse c\u00f3digo fosse comprometido, essas informa\u00e7\u00f5es poderiam ser usadas para acessar e manipular seus recursos. Por isso, usaremos o Isso est\u00e1 alinhado com a metodologia dos 12 fatores, um conjunto de melhores pr\u00e1ticas para desenvolvimento de aplica\u00e7\u00f5es modernas. O terceiro fator, \"Config\", afirma que as configura\u00e7\u00f5es que variam entre os ambientes devem ser armazenadas no ambiente e n\u00e3o no c\u00f3digo. Agora que entendemos melhor esses conceitos, vamos come\u00e7ar instalando as bibliotecas que vamos usar. O primeiro passo \u00e9 instalar o SQLAlchemy, um ORM que nos permite trabalhar com bancos de dados SQL de maneira Pythonic. Al\u00e9m disso, o Alembic, que \u00e9 uma ferramenta de migra\u00e7\u00e3o de banco de dados, funciona muito bem com o SQLAlchemy e nos ajudar\u00e1 a gerenciar as altera\u00e7\u00f5es do esquema do nosso banco de dados. $ Execu\u00e7\u00e3o no terminal! Al\u00e9m disso, para evitar a escrita de configura\u00e7\u00f5es do banco de dados diretamente no c\u00f3digo-fonte, usaremos o Agora estamos prontos para mergulhar na configura\u00e7\u00e3o do nosso banco de dados! Vamos em frente. "},{"location":"03/#o-basico-sobre-sqlalchemy","title":"O b\u00e1sico sobre SQLAlchemy","text":"SQLAlchemy \u00e9 uma biblioteca Python vers\u00e1til, concebida para intermediar a intera\u00e7\u00e3o entre Python e bancos de dados relacionais, como MySQL, PostgreSQL e SQLite. A biblioteca \u00e9 constitu\u00edda por duas partes principais: o Core e o ORM (Object Relational Mapper).
Al\u00e9m do Core e do ORM, o SQLAlchemy conta com outros componentes cruciais que ser\u00e3o foco desta aula, a Engine e a Session: "},{"location":"03/#engine","title":"Engine","text":"A 'Engine' do SQLAlchemy \u00e9 o ponto de contato com o banco de dados, estabelecendo e gerenciando as conex\u00f5es. Ela \u00e9 instanciada atrav\u00e9s da fun\u00e7\u00e3o Quanto \u00e0 persist\u00eancia de dados e consultas ao banco de dados utilizando o ORM, a Session \u00e9 a principal interface. Ela atua como um intermedi\u00e1rio entre o aplicativo Python e o banco de dados, mediada pela Engine. A Session \u00e9 encarregada de todas as transa\u00e7\u00f5es, fornecendo uma API para conduzi-las. Agora que conhecemos a Engine e a Session, vamos explorar a defini\u00e7\u00e3o de modelos de dados. "},{"location":"03/#definindo-os-modelos-de-dados-com-sqlalchemy","title":"Definindo os Modelos de Dados com SQLAlchemy","text":"Os modelos de dados definem a estrutura de como os dados ser\u00e3o armazenados no banco de dados. No ORM do SQLAlchemy, esses modelos s\u00e3o definidos como classes Python que herdam de uma classe base comum. A classe base \u00e9 criada a partir de Cada classe que herda da classe base \u00e9 automaticamente mapeada para uma tabela no banco de dados. Adicionalmente, a classe base inclui um objeto de metadados que \u00e9 uma cole\u00e7\u00e3o de todas as tabelas que foram declaradas. Este objeto \u00e9 utilizado para gerenciar opera\u00e7\u00f5es como cria\u00e7\u00e3o, modifica\u00e7\u00e3o e exclus\u00e3o de tabelas. Vamos agora definir nosso modelo Inclua o seguinte c\u00f3digo no arquivo Aqui, Antes de prosseguirmos, uma boa pr\u00e1tica seria criar um teste para validar se toda a estrutura do banco de dados funciona. Vamos criar um arquivo para validar isso: A partir daqui, voc\u00ea pode prosseguir com a estrutura\u00e7\u00e3o do conte\u00fado desse arquivo para definir os testes necess\u00e1rios para validar o seu modelo de usu\u00e1rio e sua intera\u00e7\u00e3o com o banco de dados. "},{"location":"03/#antes-de-escrever-os-testes","title":"Antes de Escrever os Testes","text":"A essa altura, se estiv\u00e9ssemos buscando apenas cobertura, poder\u00edamos simplesmente testar utilizando o modelo, e isso seria suficiente. No entanto, queremos verificar se toda a nossa intera\u00e7\u00e3o com o banco de dados ocorrer\u00e1 com sucesso. Isso inclui saber se os tipos de dados na tabela foram mapeados corretamente, se \u00e9 poss\u00edvel interagir com o banco de dados, se o ORM est\u00e1 estruturado adequadamente com a classe base. Precisamos garantir que todo esse esquema funcione. Neste diagrama, vemos a rela\u00e7\u00e3o completa entre o aplicativo Python e o banco de dados. A conex\u00e3o \u00e9 estabelecida atrav\u00e9s do SQLAlchemy ORM, que fornece uma Session para interagir com os Modelos. Esses modelos s\u00e3o mapeados para as tabelas no banco de dados, enquanto a Engine se conecta com o banco de dados e depende de Metadata para manter as informa\u00e7\u00f5es das tabelas. Portanto, criaremos uma fixture para que possamos usar todo esse esquema sempre que necess\u00e1rio. "},{"location":"03/#criando-uma-fixture-para-interacoes-com-o-banco-de-dados","title":"Criando uma Fixture para intera\u00e7\u00f5es com o Banco de Dados","text":"Para testar o banco, temos que fazer diversos passos, e isso pode tornar nosso teste bastante grande. Uma fixture pode ajudar a isolar toda essa configura\u00e7\u00e3o do banco de dados fora do teste. Assim, evitamos repetir o mesmo c\u00f3digo em todos os testes e ainda garantimos que cada teste tenha sua pr\u00f3pria vers\u00e3o limpa do banco de dados. Vamos criar uma fixture para a conex\u00e3o com o banco de dados chamada Aqui, estamos utilizando o SQLite como o banco de dados em mem\u00f3ria para os testes. Essa \u00e9 uma pr\u00e1tica comum em testes unit\u00e1rios, pois a utiliza\u00e7\u00e3o de um banco de dados em mem\u00f3ria \u00e9 mais r\u00e1pida do que um banco de dados persistido em disco. Com o SQLite em mem\u00f3ria, podemos criar e destruir bancos de dados facilmente, o que \u00e9 \u00fatil para isolar os testes e garantir que os dados de um teste n\u00e3o afetem outros testes. Al\u00e9m disso, n\u00e3o precisamos nos preocupar com a limpeza dos dados ap\u00f3s a execu\u00e7\u00e3o dos testes, j\u00e1 que o banco de dados em mem\u00f3ria \u00e9 descartado quando o programa \u00e9 encerrado. O que cada linha da fixture faz?
Resumindo, essa fixture est\u00e1 configurando e limpando um banco de dados de teste para cada teste que o solicita, assegurando que cada teste seja isolado e tenha seu pr\u00f3prio ambiente limpo para trabalhar. Isso \u00e9 uma boa pr\u00e1tica em testes de unidade, j\u00e1 que queremos que cada teste seja independente e n\u00e3o afete os demais. "},{"location":"03/#criando-um-teste-para-a-nossa-tabela","title":"Criando um Teste para a Nossa Tabela","text":"Agora, no arquivo "},{"location":"03/#executando-o-teste","title":"Executando o teste","text":"A execu\u00e7\u00e3o de testes \u00e9 uma parte vital do desenvolvimento de qualquer aplica\u00e7\u00e3o. Os testes nos ajudam a identificar e corrigir problemas antes que eles se tornem mais s\u00e9rios. Eles tamb\u00e9m fornecem a confian\u00e7a de que nossas mudan\u00e7as n\u00e3o quebraram nenhuma funcionalidade existente. No nosso caso, vamos executar os testes para validar nossos modelos de usu\u00e1rio e garantir que eles estejam funcionando como esperado. Para executar os testes, digite o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Neste caso, podemos ver que todos os nossos testes passaram com sucesso. Isso significa que nossa funcionalidade de cria\u00e7\u00e3o de usu\u00e1rio est\u00e1 funcionando corretamente e que nosso modelo de usu\u00e1rio est\u00e1 sendo corretamente persistido no banco de dados. Com nossos modelos e testes de banco de dados agora em ordem, estamos prontos para avan\u00e7ar para a pr\u00f3xima fase de configura\u00e7\u00e3o de nosso banco de dados e gerenciamento de migra\u00e7\u00f5es. "},{"location":"03/#configuracao-do-ambiente-do-banco-de-dados","title":"Configura\u00e7\u00e3o do ambiente do banco de dados","text":"Por fim, vamos configurar nosso banco de dados. Primeiro, vamos criar um novo arquivo chamado No arquivo Agora, vamos definir o Com isso, quando a classe Finalmente, adicione o arquivo de banco de dados, "},{"location":"03/#instalando-o-alembic-e-criando-a-primeira-migracao","title":"Instalando o Alembic e Criando a Primeira Migra\u00e7\u00e3o","text":"Antes de avan\u00e7armos, \u00e9 importante entender o que s\u00e3o migra\u00e7\u00f5es de banco de dados e por que s\u00e3o \u00fateis. As migra\u00e7\u00f5es s\u00e3o uma maneira de fazer altera\u00e7\u00f5es ou atualiza\u00e7\u00f5es no banco de dados, como adicionar uma tabela ou uma coluna a uma tabela, ou alterar o tipo de dados de uma coluna. Elas s\u00e3o extremamente \u00fateis, pois nos permitem manter o controle de todas as altera\u00e7\u00f5es feitas no esquema do banco de dados ao longo do tempo. Elas tamb\u00e9m nos permitem reverter para uma vers\u00e3o anterior do esquema do banco de dados, se necess\u00e1rio. Caso nunca tenha trabalhado com Migra\u00e7\u00f5esTemos uma live de Python focada nesse assunto em espec\u00edfico Link direto Agora, vamos come\u00e7ar instalando o Alembic, que \u00e9 uma ferramenta de migra\u00e7\u00e3o de banco de dados para SQLAlchemy. Usaremos o Poetry para adicionar o Alembic ao nosso projeto: $ Execu\u00e7\u00e3o no terminal! Ap\u00f3s a instala\u00e7\u00e3o do Alembic, precisamos inici\u00e1-lo em nosso projeto. O comando de inicializa\u00e7\u00e3o criar\u00e1 um diret\u00f3rio Com isso, a estrutura do nosso projeto sofre algumas altera\u00e7\u00f5es e novos arquivos s\u00e3o criados: No arquivo Com o Alembic devidamente instalado e iniciado, agora \u00e9 o momento de gerar nossa primeira migra\u00e7\u00e3o. Mas, antes disso, precisamos garantir que o Alembic consiga acessar nossas configura\u00e7\u00f5es e modelos corretamente. Para isso, vamos fazer algumas altera\u00e7\u00f5es no arquivo Neste arquivo, precisamos:
O arquivo Feitas essas altera\u00e7\u00f5es, estamos prontos para gerar nossa primeira migra\u00e7\u00e3o autom\u00e1tica. O Alembic \u00e9 capaz de gerar migra\u00e7\u00f5es a partir das mudan\u00e7as detectadas nos nossos modelos do SQLAlchemy. Para criar a migra\u00e7\u00e3o, utilizamos o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Este comando instrui o Alembic a criar uma nova revis\u00e3o de migra\u00e7\u00e3o no diret\u00f3rio Ao criar uma migra\u00e7\u00e3o autom\u00e1tica com o Alembic, um arquivo \u00e9 gerado dentro da pasta Vamos analisar o arquivo de migra\u00e7\u00e3o: migrations/versions/e018397cecf4_create_users_table.py Esse arquivo descreve as mudan\u00e7as a serem feitas no banco de dados. Ele usa a linguagem core do SQLAlchemy, que \u00e9 mais baixo n\u00edvel que o ORM. As fun\u00e7\u00f5es Apesar desta migra\u00e7\u00e3o ter sido criada, ela ainda n\u00e3o foi aplicada ao nosso banco de dados. No entanto, o Alembic j\u00e1 criou um arquivo Debian/Ubuntu Mac
Para aplicar as migra\u00e7\u00f5es, usamos o comando Agora, se examinarmos nosso banco de dados novamente, veremos que a tabela users foi criada: $ Execu\u00e7\u00e3o no terminal! Finalmente, lembre-se de que todas essas mudan\u00e7as que fizemos s\u00f3 existem localmente no seu ambiente de trabalho at\u00e9 agora. Para que sejam compartilhadas com outras pessoas, precisamos fazer commit dessas mudan\u00e7as no nosso sistema de controle de vers\u00e3o. "},{"location":"03/#commit","title":"Commit","text":"Primeiro, vamos verificar o status do nosso reposit\u00f3rio para ver as mudan\u00e7as que fizemos: $ Execu\u00e7\u00e3o no terminal! Voc\u00ea ver\u00e1 uma lista de arquivos que foram modificados ou adicionados. As altera\u00e7\u00f5es devem incluir os arquivos de migra\u00e7\u00e3o que criamos, bem como quaisquer altera\u00e7\u00f5es que fizemos em nossos arquivos de modelo e configura\u00e7\u00e3o. Em seguida, vamos adicionar todas as mudan\u00e7as ao pr\u00f3ximo commit: $ Execu\u00e7\u00e3o no terminal! Agora, estamos prontos para fazer o commit das nossas altera\u00e7\u00f5es. Vamos fornecer uma mensagem de commit que descreve as mudan\u00e7as que fizemos: $ Execu\u00e7\u00e3o no terminal! Finalmente, vamos enviar as mudan\u00e7as para o reposit\u00f3rio remoto: $ Execu\u00e7\u00e3o no terminal! E pronto! As mudan\u00e7as que fizemos foram salvas no hist\u00f3rico do Git e agora est\u00e3o dispon\u00edveis no git. "},{"location":"03/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, demos passos significativos para preparar nosso projeto FastAPI para interagir com um banco de dados. Come\u00e7amos definindo nosso primeiro modelo de dados, o Avan\u00e7amos para configurar o ambiente de desenvolvimento, onde estabelecemos um arquivo Na \u00faltima parte desta aula, focamos na instala\u00e7\u00e3o e configura\u00e7\u00e3o do Alembic, uma ferramenta de migra\u00e7\u00e3o de banco de dados para SQLAlchemy. Usando o Alembic, criamos nossa primeira migra\u00e7\u00e3o que, automaticamente, gera o esquema do banco de dados a partir dos nossos modelos SQLAlchemy. Com esses passos, nosso projeto est\u00e1 bem encaminhado para come\u00e7ar a persistir dados. Na pr\u00f3xima aula, avan\u00e7aremos para a fase crucial de conectar o SQLAlchemy aos endpoints do nosso projeto. Isso permitir\u00e1 a realiza\u00e7\u00e3o de opera\u00e7\u00f5es de CRUD nos nossos usu\u00e1rios diretamente atrav\u00e9s da API. "},{"location":"04/","title":"Integrando Banco de Dados a API","text":""},{"location":"04/#integrando-banco-de-dados-a-api","title":"Integrando Banco de Dados a API","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Ap\u00f3s a cria\u00e7\u00e3o de nossos modelos e migra\u00e7\u00f5es na aula passada, chegou o momento de dar um passo significativo: integrar o banco de dados \u00e0 nossa aplica\u00e7\u00e3o FastAPI. Vamos deixar de lado o banco de dados fict\u00edcio que criamos anteriormente e mergulhar na implementa\u00e7\u00e3o de um banco de dados real e funcional. "},{"location":"04/#integrando-sqlalchemy-a-nossa-aplicacao-fastapi","title":"Integrando SQLAlchemy \u00e0 Nossa Aplica\u00e7\u00e3o FastAPI","text":"Para aqueles que n\u00e3o est\u00e3o familiarizados, o SQLAlchemy \u00e9 uma biblioteca Python que facilita a intera\u00e7\u00e3o com um banco de dados SQL. Ele faz isso oferecendo uma forma de trabalhar com bancos de dados que aproveita a facilidade e o poder do Python, ao mesmo tempo em que mant\u00e9m a efici\u00eancia e a flexibilidade dos bancos de dados SQL. Caso nunca tenha trabalhado com SQLAlchemyTemos diversas lives de Python focadas nesse assunto. Esta sobre o ORM em espec\u00edfico: Link direto Essa sobre as novidades da vers\u00e3o 1.4 e do estilo de programa\u00e7\u00e3o da vers\u00e3o 2.0 Link direto E finalmente uma focada no processo de migra\u00e7\u00f5es com o SQLalchemy + Alembic (que veremos nessa aula) Link direto Uma pe\u00e7a chave do SQLAlchemy \u00e9 o conceito de uma \"sess\u00e3o\". Se voc\u00ea \u00e9 novo no mundo dos bancos de dados, pode pensar na sess\u00e3o como um carrinho de compras virtual: conforme voc\u00ea navega pelo site (ou, neste caso, conforme seu c\u00f3digo executa), voc\u00ea pode adicionar ou remover itens desse carrinho. No entanto, nenhuma altera\u00e7\u00e3o \u00e9 realmente feita at\u00e9 que voc\u00ea decida finalizar a compra. No contexto do SQLAlchemy, \"finalizar a compra\" \u00e9 equivalente a fazer o commit das suas altera\u00e7\u00f5es. A sess\u00e3o no SQLAlchemy \u00e9 t\u00e3o poderosa que, na verdade, incorpora tr\u00eas padr\u00f5es de arquitetura importantes.
Entender esses conceitos \u00e9 importante, pois nos ajuda a entender melhor como o SQLAlchemy funciona e como podemos us\u00e1-lo de forma mais eficaz. Agora que temos uma ideia do que \u00e9 uma sess\u00e3o, vamos configurar uma para nosso projeto. Para isso, criaremos a fun\u00e7\u00e3o "},{"location":"04/#gerenciando-dependencias-com-fastapi","title":"Gerenciando Depend\u00eancias com FastAPI","text":"Assim como a sess\u00e3o SQLAlchemy, que implementa v\u00e1rios padr\u00f5es arquiteturais importantes, FastAPI tamb\u00e9m usa um conceito de padr\u00e3o arquitetural chamado \"Inje\u00e7\u00e3o de Depend\u00eancia\". No mundo do desenvolvimento de software, uma \"depend\u00eancia\" \u00e9 um componente que um m\u00f3dulo de software precisa para realizar sua fun\u00e7\u00e3o. Imagine um m\u00f3dulo como uma f\u00e1brica e as depend\u00eancias como as partes ou mat\u00e9rias-primas que a f\u00e1brica precisa para produzir seus produtos. Em vez de a f\u00e1brica ter que buscar essas pe\u00e7as por conta pr\u00f3pria (o que seria ineficiente), elas s\u00e3o entregues \u00e0 f\u00e1brica, prontas para serem usadas. Este \u00e9 o conceito de Inje\u00e7\u00e3o de Depend\u00eancia. A Inje\u00e7\u00e3o de Depend\u00eancia permite que mantenhamos um baixo n\u00edvel de acoplamento entre diferentes m\u00f3dulos de um sistema. As depend\u00eancias entre os m\u00f3dulos n\u00e3o s\u00e3o definidas no c\u00f3digo, mas sim pela configura\u00e7\u00e3o de uma infraestrutura de software (container) que \u00e9 respons\u00e1vel por \"injetar\" em cada componente suas depend\u00eancias declaradas. Em termos pr\u00e1ticos, o que isso significa \u00e9 que, em vez de cada parte do nosso c\u00f3digo ter que criar suas pr\u00f3prias inst\u00e2ncias de classes ou servi\u00e7os de que depende (o que pode levar a duplica\u00e7\u00e3o de c\u00f3digo e tornar os testes mais dif\u00edceis), essas inst\u00e2ncias s\u00e3o criadas uma vez e depois injetadas onde s\u00e3o necess\u00e1rias. FastAPI fornece a fun\u00e7\u00e3o Agora que temos a nossa sess\u00e3o de banco de dados sendo gerenciada por meio do FastAPI e da inje\u00e7\u00e3o de depend\u00eancias, vamos atualizar nossos endpoints para que possam tirar proveito disso. Come\u00e7aremos com a rota de POST para a cria\u00e7\u00e3o de usu\u00e1rios. Ao inv\u00e9s de usarmos o banco de dados falso que criamos inicialmente, agora vamos fazer a inser\u00e7\u00e3o real dos usu\u00e1rios no nosso banco de dados. Para isso, vamos modificar o nosso endpoint da seguinte maneira: fast_zero/app.py Nesse c\u00f3digo, a fun\u00e7\u00e3o Agora que nossa rota de POST est\u00e1 funcionando com o banco de dados real, precisamos atualizar nossos testes para refletir essa mudan\u00e7a. Como estamos usando a inje\u00e7\u00e3o de depend\u00eancias, precisamos tamb\u00e9m usar essa funcionalidade nos nossos testes para que possamos injetar a sess\u00e3o de banco de dados de teste. Vamos alterar a nossa fixture Com isso, quando o FastAPI tentar injetar a sess\u00e3o em nossos endpoints, ele vai injetar a sess\u00e3o de teste que definimos, em vez da sess\u00e3o real. E como estamos usando um banco de dados em mem\u00f3ria para os testes, nossos testes n\u00e3o v\u00e3o interferir nos dados reais do nosso aplicativo. tests/test_app.py Agora que temos a nossa fixture configurada, vamos atualizar o nosso teste O nosso teste ainda n\u00e3o consegue ser executado, mas existe um motivo para isso. "},{"location":"04/#threads-e-conexoes","title":"Threads e conex\u00f5es","text":"No ambiente de testes do FastAPI, a aplica\u00e7\u00e3o e os testes podem rodar em threads diferentes. Isso pode levar a um erro com o SQLite, pois os objetos SQLite criados em uma thread s\u00f3 podem ser usados na mesma thread. Para contornar isso, adicionaremos os seguintes par\u00e2metros na cria\u00e7\u00e3o da
Assim, nossa fixture deve ficar dessa forma: tests/conftest.py Depois de realizar essas mudan\u00e7as, podemos executar nossos testes e verificar se est\u00e3o passando. Por\u00e9m, embora o teste Nos pr\u00f3ximos passos, vamos realizar essas modifica\u00e7\u00f5es para garantir que todo o nosso aplicativo esteja usando o banco de dados real. "},{"location":"04/#modificando-o-endpoint-get-users","title":"Modificando o Endpoint GET /users","text":"Agora que temos o nosso banco de dados configurado e funcionando, \u00e9 o momento de atualizar o nosso endpoint de GET para interagir com o banco de dados real. Em vez de trabalhar com uma lista fict\u00edcia de usu\u00e1rios, queremos buscar os usu\u00e1rios diretamente do nosso banco de dados, permitindo uma intera\u00e7\u00e3o din\u00e2mica e real com os dados. fast_zero/app.py Neste c\u00f3digo, adicionamos algumas funcionalidades essenciais para a busca de dados. Os par\u00e2metros
Essas adi\u00e7\u00f5es tornam o nosso endpoint mais flex\u00edvel e otimizado para lidar com diferentes cen\u00e1rios de uso. "},{"location":"04/#testando-o-endpoint-get-users","title":"Testando o Endpoint GET /users","text":"Com a mudan\u00e7a para o banco de dados real, nosso banco de dados de teste ser\u00e1 sempre resetado para cada teste. Portanto, n\u00e3o podemos mais executar o teste que t\u00ednhamos antes, pois n\u00e3o haver\u00e3o usu\u00e1rios no banco. Para verificar se o nosso endpoint est\u00e1 funcionando corretamente, vamos criar um novo teste que solicita uma lista de usu\u00e1rios de um banco vazio: tests/test_app.py Agora que temos nosso novo teste, podemos execut\u00e1-lo para verificar se o nosso endpoint GET est\u00e1 funcionando corretamente. Com esse novo teste, a fun\u00e7\u00e3o Por\u00e9m, \u00e9 claro, queremos tamb\u00e9m testar o caso em que existem usu\u00e1rios no banco. Para isso, vamos criar uma nova fixture que cria um usu\u00e1rio em nosso banco de dados de teste. "},{"location":"04/#criando-uma-fixture-para-user","title":"Criando uma fixture para User","text":"Para criar essa fixture, vamos aproveitar a nossa fixture de sess\u00e3o do SQLAlchemy, e criar um novo usu\u00e1rio dentro dela: tests/conftest.py Com essa fixture, sempre que precisarmos de um usu\u00e1rio em nossos testes, podemos simplesmente passar Agora podemos criar um novo teste para verificar se o nosso endpoint est\u00e1 retornando o usu\u00e1rio correto quando existe um usu\u00e1rio no banco: tests/test_app.py Agora podemos rodar o nosso teste novamente e verificar se ele est\u00e1 passando: $ Execu\u00e7\u00e3o no terminal! No entanto, mesmo que nosso c\u00f3digo pare\u00e7a correto, podemos encontrar um problema: o Pydantic n\u00e3o consegue converter diretamente nosso modelo SQLAlchemy para um modelo Pydantic. Vamos resolver isso agora. "},{"location":"04/#integrando-o-schema-ao-model","title":"Integrando o Schema ao Model","text":"A integra\u00e7\u00e3o direta do ORM com o nosso esquema Pydantic n\u00e3o \u00e9 imediata e exige algumas modifica\u00e7\u00f5es. O Pydantic, por padr\u00e3o, n\u00e3o sabe como lidar com os modelos do SQLAlchemy, o que nos leva ao erro observado nos testes. A solu\u00e7\u00e3o para esse problema passa por fazer uma altera\u00e7\u00e3o no esquema Para resolver o problema de convers\u00e3o entre SQLAlchemy e Pydantic, precisamos atualizar o nosso esquema Com essa mudan\u00e7a, nosso esquema Pydantic agora pode ser convertido a partir de um modelo SQLAlchemy. Agora podemos executar nosso teste novamente e verificar se ele est\u00e1 passando. $ Execu\u00e7\u00e3o no terminal! Agora que temos nosso endpoint GET funcionando corretamente e testado, podemos seguir para o endpoint PUT, e continuar com o processo de atualiza\u00e7\u00e3o dos nossos endpoints. "},{"location":"04/#modificando-o-endpoint-put-users","title":"Modificando o Endpoint PUT /users","text":"Agora, vamos modificar o endpoint de PUT para suportar o banco de dados, como fizemos com os endpoints POST e GET: fast_zero/app.py Semelhante ao que fizemos antes, estamos injetando a sess\u00e3o do SQLAlchemy em nosso endpoint e utilizando-a para buscar o usu\u00e1rio a ser atualizado. Se o usu\u00e1rio n\u00e3o for encontrado, retornamos um erro 404. Ao executar nosso linter, ele ir\u00e1 apontar um erro informando que importamos UserDB mas nunca o usamos. $ Execu\u00e7\u00e3o no terminal! Isso ocorre porque a rota PUT era a \u00fanica que estava utilizando UserDB, e agora que modificamos esta rota, podemos remover UserDB dos nossos e tamb\u00e9m excluir sua defini\u00e7\u00e3o no arquivo schemas.py Caso fique em d\u00favida sobre o que remover, seu arquivo "},{"location":"04/#adicionando-o-teste-do-put","title":"Adicionando o teste do PUT","text":"Tamb\u00e9m precisamos adicionar um teste para o nosso novo endpoint PUT: tests/test_app.py "},{"location":"04/#modificando-o-endpoint-delete-users","title":"Modificando o Endpoint DELETE /users","text":"Em seguida, modificamos o endpoint DELETE da mesma maneira: fast_zero/app.py Neste caso, estamos novamente usando a sess\u00e3o do SQLAlchemy para encontrar o usu\u00e1rio a ser deletado e, em seguida, exclu\u00edmos esse usu\u00e1rio do banco de dados. "},{"location":"04/#adicionando-testes-para-delete","title":"Adicionando testes para DELETE","text":"Assim como para o endpoint PUT, precisamos adicionar um teste para o nosso endpoint DELETE: tests/test_app.py "},{"location":"04/#cobertura-e-testes-nao-feitos","title":"Cobertura e testes n\u00e3o feitos","text":"Com o banco de dados agora em funcionamento, podemos verificar a cobertura de c\u00f3digo do arquivo Esses tr\u00eas casos ficam como exerc\u00edcio para quem est\u00e1 acompanhando este curso. Al\u00e9m disso, n\u00e3o devemos esquecer de remover a implementa\u00e7\u00e3o do banco de dados falso Agora que terminamos a atualiza\u00e7\u00e3o dos nossos endpoints, vamos fazer o commit das nossas altera\u00e7\u00f5es. O processo \u00e9 o seguinte: $ Execu\u00e7\u00e3o no terminal! Com isso, terminamos a atualiza\u00e7\u00e3o dos nossos endpoints para usar o nosso banco de dados real. "},{"location":"04/#conclusao","title":"Conclus\u00e3o","text":"Parab\u00e9ns por chegar ao final desta aula! Voc\u00ea deu um passo significativo no desenvolvimento de nossa aplica\u00e7\u00e3o, substituindo a implementa\u00e7\u00e3o do banco de dados falso pela integra\u00e7\u00e3o com um banco de dados real usando SQLAlchemy. Tamb\u00e9m vimos como ajustar os nossos testes para considerar essa nova realidade. Nesta aula, abordamos como modificar os endpoints para interagir com o banco de dados real e como utilizar a inje\u00e7\u00e3o de depend\u00eancias do FastAPI para gerenciar nossas sess\u00f5es do SQLAlchemy. Tamb\u00e9m discutimos a import\u00e2ncia dos testes para garantir que nossos endpoints est\u00e3o funcionando corretamente, e como as fixtures do Pytest podem nos auxiliar na prepara\u00e7\u00e3o do ambiente para esses testes. Tamb\u00e9m nos deparamos com situa\u00e7\u00f5es onde o Pydantic e o SQLAlchemy n\u00e3o interagem perfeitamente bem, e como solucionar esses casos. No final desta aula, voc\u00ea deve estar confort\u00e1vel em integrar um banco de dados real a uma aplica\u00e7\u00e3o FastAPI, saber como escrever testes robustos que levem em considera\u00e7\u00e3o a intera\u00e7\u00e3o com o banco de dados, e estar ciente de poss\u00edveis desafios ao trabalhar com Pydantic e SQLAlchemy juntos. "},{"location":"05/","title":"Autentica\u00e7\u00e3o e Autoriza\u00e7\u00e3o com JWT","text":""},{"location":"05/#autenticacao-e-autorizacao-com-jwt","title":"Autentica\u00e7\u00e3o e Autoriza\u00e7\u00e3o com JWT","text":"Objetivos da Aula:
Aula Slides C\u00f3digo "},{"location":"05/#introducao","title":"Introdu\u00e7\u00e3o","text":"Nesta aula, vamos abordar dois aspectos cruciais de qualquer aplica\u00e7\u00e3o web: a autentica\u00e7\u00e3o e a autoriza\u00e7\u00e3o. At\u00e9 agora, nossos usu\u00e1rios podem criar, ler, atualizar e deletar suas contas, mas qualquer pessoa pode fazer essas a\u00e7\u00f5es. N\u00e3o queremos que qualquer usu\u00e1rio possa deletar ou modificar a conta de outro usu\u00e1rio. Para evitar isso, vamos implementar autentica\u00e7\u00e3o e autoriza\u00e7\u00e3o em nossa aplica\u00e7\u00e3o. A autentica\u00e7\u00e3o \u00e9 o processo de verificar quem um usu\u00e1rio \u00e9, enquanto a autoriza\u00e7\u00e3o \u00e9 o processo de verificar o que ele tem permiss\u00e3o para fazer. Usaremos o JSON Web Token (JWT) para implementar a autentica\u00e7\u00e3o, e adicionaremos l\u00f3gica de autoriza\u00e7\u00e3o aos nossos endpoints. Al\u00e9m disso, at\u00e9 agora, estamos armazenando as senhas dos usu\u00e1rios como texto puro no banco de dados, o que \u00e9 uma pr\u00e1tica insegura. Vamos corrigir isso utilizando a biblioteca Bcrypt para encriptar as senhas. "},{"location":"05/#o-que-e-um-jwt","title":"O que \u00e9 um JWT","text":"O JWT \u00e9 um padr\u00e3o (RFC 7519) que define uma maneira compacta e aut\u00f4noma de transmitir informa\u00e7\u00f5es entre as partes de maneira segura. Essas informa\u00e7\u00f5es s\u00e3o transmitidas como um objeto JSON que \u00e9 digitalmente assinado usando um segredo (com o algoritmo HMAC) ou um par de chaves p\u00fablica/privada usando RSA ou ECDSA. Um JWT consiste em tr\u00eas partes:
Essas tr\u00eas partes s\u00e3o separadas por pontos (.) e juntas formam um token JWT. Formando a estrutura: \u00c9 importante ressaltar que, apesar de a informa\u00e7\u00e3o em um JWT estar codificada, ela n\u00e3o est\u00e1 criptografada. Isso significa que qualquer pessoa com acesso ao token pode decodificar e ler as informa\u00e7\u00f5es nele. No entanto, sem o segredo usado para assinar o token, eles n\u00e3o podem alterar as informa\u00e7\u00f5es ou forjar um novo token. Portanto, n\u00e3o devemos incluir informa\u00e7\u00f5es sens\u00edveis ou confidenciais no payload do JWT. Se quisermos ver o header, o payload e a assinatura contidas nesse token podemos acessar o debuger do jwt e checar quais as informa\u00e7\u00f5es que est\u00e3o nesse token "},{"location":"05/#como-funciona-o-jwt","title":"Como funciona o JWT","text":"Em uma aplica\u00e7\u00e3o web, o processo de autentica\u00e7\u00e3o geralmente funciona da seguinte maneira:
Nos pr\u00f3ximos t\u00f3picos, vamos detalhar como podemos gerar e verificar tokens JWT em nossa aplica\u00e7\u00e3o FastAPI, bem como adicionar autentica\u00e7\u00e3o e autoriza\u00e7\u00e3o aos nossos endpoints. "},{"location":"05/#gerando-tokens-jwt","title":"Gerando tokens JWT","text":"Para gerar tokens JWT, precisamos de duas bibliotecas extras: Agora, vamos criar uma fun\u00e7\u00e3o para gerar nossos tokens JWT. Criaremos um novo arquivo para gerenciar a seguran\u00e7a: A fun\u00e7\u00e3o Note que a constante Embora esse c\u00f3digo ser\u00e1 coberto no futuro com a utiliza\u00e7\u00e3o do token, \u00e9 interessante criarmos um teste para essa fun\u00e7\u00e3o com uma finalidade puramente did\u00e1tica. De forma em que consigamos ver os tokens gerados pelo Com isso vamos criar um arquivo chamado Na pr\u00f3xima se\u00e7\u00e3o, vamos ver como podemos usar a biblioteca Armazenar senhas em texto puro \u00e9 uma pr\u00e1tica de seguran\u00e7a extremamente perigosa. Em vez disso, \u00e9 uma pr\u00e1tica padr\u00e3o criptografar (\"hash\") as senhas antes de armazen\u00e1-las. Quando um usu\u00e1rio tenta se autenticar, a senha inserida \u00e9 criptografada novamente e comparada com a vers\u00e3o criptografada armazenada no banco de dados. Se as duas correspondem, o usu\u00e1rio \u00e9 autenticado. Vamos implementar essa funcionalidade usando a biblioteca A fun\u00e7\u00e3o Agora, quando um usu\u00e1rio se registra em nossa aplica\u00e7\u00e3o, devemos usar a fun\u00e7\u00e3o Na pr\u00f3xima se\u00e7\u00e3o, vamos modificar nossos endpoints para fazer uso dessas fun\u00e7\u00f5es. "},{"location":"05/#modificando-o-endpoint-de-post-para-encriptar-a-senha","title":"Modificando o endpoint de POST para encriptar a senha","text":"Com as fun\u00e7\u00f5es de cria\u00e7\u00e3o de hash de senha e verifica\u00e7\u00e3o de senha em vigor, agora podemos atualizar nossos endpoints para usar essa nova funcionalidade de encripta\u00e7\u00e3o. Primeiro, vamos modificar a fun\u00e7\u00e3o Desta forma, a senha n\u00e3o ser\u00e1 mais criada em texto plano no objeto Por n\u00e3o validar o password, usando o retorno "},{"location":"05/#modificando-o-endpoint-de-atualizacao-de-usuarios","title":"Modificando o endpoint de atualiza\u00e7\u00e3o de usu\u00e1rios","text":"\u00c9 igualmente importante modificar a fun\u00e7\u00e3o Assim, a atualiza\u00e7\u00e3o de um Assim como no teste da rota de cria\u00e7\u00e3o, os testes tamb\u00e9m passam normalmente por n\u00e3o validarem o campo password. $ Execu\u00e7\u00e3o no terminal! "},{"location":"05/#criando-um-endpoint-de-geracao-do-token","title":"Criando um endpoint de gera\u00e7\u00e3o do token","text":"Antes de criar o endpoint, precisamos criar um schema para o nosso token. Em um contexto JWT, "},{"location":"05/#utilizando-oauth2passwordrequestform","title":"Utilizando OAuth2PasswordRequestForm","text":"A classe Para usar os formul\u00e1rios no FastAPI, precisamos instalar o "},{"location":"05/#criando-um-endpoint-de-geracao-do-token_1","title":"Criando um endpoint de gera\u00e7\u00e3o do token","text":"Agora vamos criar o endpoint que ir\u00e1 autenticar o usu\u00e1rio e fornecer um token de acesso JWT. Este endpoint ir\u00e1 receber as informa\u00e7\u00f5es de login do usu\u00e1rio, verificar se as credenciais s\u00e3o v\u00e1lidas e, em caso afirmativo, retornar um token de acesso JWT. fast_zero/app.py Esse endpoint recebe os dados do formul\u00e1rio atrav\u00e9s do Agora vamos escrever um teste para verificar se o nosso novo endpoint est\u00e1 funcionando corretamente. tests/test_app.py Nesse teste, n\u00f3s enviamos uma requisi\u00e7\u00e3o POST para o endpoint \"/token\" com um username e uma senha v\u00e1lidos. Ent\u00e3o, n\u00f3s verificamos que a resposta cont\u00e9m um \"access_token\" e um \"token_type\", que s\u00e3o os campos que esperamos de um JWT v\u00e1lido. No entanto, h\u00e1 um problema. Agora que a senha est\u00e1 sendo criptografada, nosso teste falhar\u00e1: $ Execu\u00e7\u00e3o no terminal! Para corrigir isso, precisamos garantir que a senha esteja sendo criptografada na fixture antes de ser salva: tests/confitest.py Vamos rodar o teste novamente. No entanto, ainda teremos um problema. Agora s\u00f3 temos a vers\u00e3o criptografada da senha, que n\u00e3o \u00e9 \u00fatil para fazer o login, j\u00e1 que o login exige a senha em texto puro: $ Execu\u00e7\u00e3o no terminal! Para resolver isso, faremos uma modifica\u00e7\u00e3o no objeto user (um monkey patch) para adicionar a senha em texto puro: tests/confitest.py Monkey patching \u00e9 uma t\u00e9cnica em que modificamos ou estendemos o c\u00f3digo em tempo de execu\u00e7\u00e3o. Neste caso, estamos adicionando um novo atributo Agora, podemos alterar o teste para usar E agora todos os testes devem passar normalmente: $ Execu\u00e7\u00e3o no terminal! Isso conclui a parte de autentica\u00e7\u00e3o de nossa API. No pr\u00f3ximo passo, iremos implementar a autoriza\u00e7\u00e3o nos endpoints. "},{"location":"05/#protegendo-os-endpoints","title":"Protegendo os Endpoints","text":"Agora que temos uma forma de autenticar nossos usu\u00e1rios e emitir tokens JWT, \u00e9 hora de usar essa infraestrutura para proteger nossos endpoints. Neste passo, vamos adicionar autentica\u00e7\u00e3o aos endpoints PUT e DELETE. Para garantir que as informa\u00e7\u00f5es do usu\u00e1rio sejam extra\u00eddas corretamente do token JWT, precisamos de um schema especial, o Nesse ponto, criaremos uma a fun\u00e7\u00e3o Aqui, a fun\u00e7\u00e3o A vari\u00e1vel No bloco Por fim, realizamos uma consulta ao banco de dados para encontrar o usu\u00e1rio com o e-mail correspondente ao username contido no token. Primeiro, vamos aplicar a autentica\u00e7\u00e3o no endpoint PUT. Se o Com isso, podemos remover a query feita no endpoint para encontrar o User, pois ela j\u00e1 est\u00e1 sendo feita no Agora, vamos aplicar a autentica\u00e7\u00e3o no endpoint DELETE. Semelhante ao PUT, se o Com essa nova depend\u00eancia, o FastAPI automaticamente garantir\u00e1 que um token de autentica\u00e7\u00e3o v\u00e1lido seja fornecido antes de permitir o acesso a esses endpoints. Se o token n\u00e3o for v\u00e1lido, ou se o usu\u00e1rio tentar modificar ou deletar um usu\u00e1rio diferente, um erro ser\u00e1 retornado. "},{"location":"05/#atualizando-os-testes","title":"Atualizando os Testes","text":"Os testes precisam ser atualizados para refletir essas mudan\u00e7as. Primeiro, precisamos criar uma nova fixture que gere um token para um usu\u00e1rio de teste. tests/conftest.py Agora, podemos atualizar os testes para o endpoint PUT e DELETE para incluir a autentica\u00e7\u00e3o. tests/test_app.py Finalmente, podemos rodar todos os testes para garantir que tudo esteja funcionando corretamente. $ Execu\u00e7\u00e3o no terminal! Com essas altera\u00e7\u00f5es, nossos endpoints agora est\u00e3o seguramente protegidos pela autentica\u00e7\u00e3o. Apenas os usu\u00e1rios autenticados podem alterar ou deletar seus pr\u00f3prios dados. Isso traz uma camada adicional de seguran\u00e7a e integridade para o nosso aplicativo. "},{"location":"05/#commit","title":"Commit","text":"Depois de finalizar a prote\u00e7\u00e3o dos endpoints e atualizar os testes, \u00e9 hora de fazer commit das altera\u00e7\u00f5es. N\u00e3o se esque\u00e7a de revisar as altera\u00e7\u00f5es antes de fazer o commit. $ Execu\u00e7\u00e3o no terminal! "},{"location":"05/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, demos um passo importante para aumentar a seguran\u00e7a da nossa API. Implementamos a autentica\u00e7\u00e3o e a autoriza\u00e7\u00e3o para os endpoints PUT e DELETE, garantindo que apenas usu\u00e1rios autenticados possam alterar ou excluir seus pr\u00f3prios dados. Tamb\u00e9m atualizamos os testes para incluir a autentica\u00e7\u00e3o. Na pr\u00f3xima aula, continuaremos a expandir a funcionalidade da nossa API. At\u00e9 l\u00e1! "},{"location":"06/","title":"Refatorando a Estrutura do Projeto","text":""},{"location":"06/#refatorando-a-estrutura-do-projeto","title":"Refatorando a Estrutura do Projeto","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Ao longo da evolu\u00e7\u00e3o de um projeto, \u00e9 natural que sua estrutura inicial necessite de ajustes para manter a legibilidade, a facilidade de manuten\u00e7\u00e3o e a organiza\u00e7\u00e3o do c\u00f3digo. Nesta aula, faremos exatamente isso em nosso projeto FastAPI: vamos refatorar partes dele para melhorar sua estrutura e, em seguida, ampliar a cobertura de nossos testes para garantir que todos os cen\u00e1rios poss\u00edveis sejam tratados corretamente. Vamos come\u00e7ar! "},{"location":"06/#criando-routers","title":"Criando Routers","text":"O FastAPI oferece uma ferramenta poderosa conhecida como routers, que facilita a organiza\u00e7\u00e3o e agrupamento de diferentes rotas em uma aplica\u00e7\u00e3o. Pense em um router como um \"subaplicativo\" do FastAPI que pode ser integrado em uma aplica\u00e7\u00e3o principal. Isso n\u00e3o s\u00f3 mant\u00e9m o c\u00f3digo organizado e leg\u00edvel, mas tamb\u00e9m se mostra especialmente \u00fatil \u00e0 medida que a aplica\u00e7\u00e3o se expande e novas rotas s\u00e3o adicionadas. Esse tipo de organiza\u00e7\u00e3o nos oferece diversos benef\u00edcios:
Vamos iniciar criando uma nova estrutura de diret\u00f3rios chamada Esta organiza\u00e7\u00e3o facilita a expans\u00e3o do seu projeto e a manuten\u00e7\u00e3o de uma estrutura clara. "},{"location":"06/#implementando-um-router-para-usuarios","title":"Implementando um Router para Usu\u00e1rios","text":"No arquivo Com essa simples configura\u00e7\u00e3o, estamos prontos para definir rotas espec\u00edficas para usu\u00e1rios neste router, em vez de sobrecarregar o aplicativo principal. Utilizamos Desta forma podemos migrar todos os nossos imports e nossas fun\u00e7\u00f5es de endpoints para o arquivo Com o prefixo definido no router, os paths dos endpoints se tornam mais simples e diretos. Ao inv\u00e9s de '/users/{user_id}', por exemplo, usamos apenas '/{user_id}'. Exemplo do arquivofast_zero/routes/users.py completo fast_zero/routes/users.py Por termos criados as tags, isso reflete na organiza\u00e7\u00e3o do swagger "},{"location":"06/#criando-um-router-para-auth","title":"Criando um router para Auth","text":"No momento, temos rotas para O router para autentica\u00e7\u00e3o ser\u00e1 criado no arquivo Neste bloco de c\u00f3digo, n\u00f3s criamos um novo router que lidar\u00e1 exclusivamente com a rota de obten\u00e7\u00e3o de token ( \u00c9 crucial abordar um aspecto relacionado \u00e0 modifica\u00e7\u00e3o do router: o uso do par\u00e2metro Esse problema fica evidente ao clicar no bot\u00e3o Percebe-se que o caminho para a autoriza\u00e7\u00e3o est\u00e1 incorreto. Como consequ\u00eancia, ao tentar autenticar atrav\u00e9s do Swagger, nos deparamos com um erro na interface: No entanto, o erro n\u00e3o \u00e9 suficientemente descritivo para identificarmos a origem do problema, retornando apenas uma mensagem gen\u00e9rica de A solu\u00e7\u00e3o para este problema \u00e9 relativamente simples. Precisamos ajustar o par\u00e2metro Ap\u00f3s essa altera\u00e7\u00e3o, ao utilizar o Swagger, a autoriza\u00e7\u00e3o ser\u00e1 direcionada corretamente para o endpoint apropriado. "},{"location":"06/#plugando-as-rotas-em-app","title":"Plugando as rotas em app","text":"O FastAPI oferece uma maneira f\u00e1cil e direta de incluir routers em nossa aplica\u00e7\u00e3o principal. Isso nos permite organizar nossos endpoints de maneira eficiente e manter nosso arquivo Para incluir os routers em nossa aplica\u00e7\u00e3o principal, precisamos import\u00e1-los e usar a fun\u00e7\u00e3o Como voc\u00ea pode ver, nosso arquivo Depois de refatorar nosso c\u00f3digo, \u00e9 crucial verificar se tudo ainda est\u00e1 funcionando como esperado. Para isso, executamos nossos testes novamente. $ Execu\u00e7\u00e3o no terminal! Como voc\u00ea pode ver, todos os testes passaram. Isso significa que as altera\u00e7\u00f5es que fizemos no nosso c\u00f3digo n\u00e3o afetaram o funcionamento do nosso aplicativo. O router manteve todos os endpoints nas mesmas rotas, garantindo a continuidade do comportamento esperado. Agora, para melhor alinhar nossos testes com a nova estrutura do nosso c\u00f3digo, devemos reorganizar os arquivos de teste de acordo. Ou seja, tamb\u00e9m devemos criar arquivos de teste espec\u00edficos para cada router, em vez de manter todos os testes no arquivo Para acompanhar a nova estrutura routers, podemos desacoplar os testes do m\u00f3dulo
Vamos adaptar os testes para se encaixarem nessa nova estrutura. "},{"location":"06/#ajustando-os-testes-para-auth","title":"Ajustando os testes para Auth","text":"Vamos come\u00e7ar criando o arquivo \u00c9 importante notar que com a cria\u00e7\u00e3o do router usando Em seguida, vamos mover os testes relacionados ao dom\u00ednio do usu\u00e1rio para o arquivo Para a constru\u00e7\u00e3o desse arquivo, nenhum teste foi modificado. Eles foram somente movidos para o dom\u00ednio espec\u00edfico do router. Importante, por\u00e9m, notar que alguns destes testes usam a fixture token ","text":"A altera\u00e7\u00e3o da fixture de Fazendo assim com que os testes que dependem dessa fixture passem a funcionar. "},{"location":"06/#executando-os-testes_1","title":"Executando os testes","text":"Ap\u00f3s essa reestrutura\u00e7\u00e3o, \u00e9 importante garantir que tudo ainda est\u00e1 funcionando corretamente. Vamos executar os testes novamente para confirmar isso. $ Execu\u00e7\u00e3o no terminal! Como podemos ver, todos os testes continuam passando com sucesso, mesmo ap\u00f3s terem sido movidos para arquivos diferentes. Isso \u00e9 uma confirma\u00e7\u00e3o de que nossa reestrutura\u00e7\u00e3o foi bem-sucedida e que nossa aplica\u00e7\u00e3o continua funcionando como esperado. "},{"location":"06/#refinando-a-definicao-de-rotas-com-annotated","title":"Refinando a Defini\u00e7\u00e3o de Rotas com Annotated","text":"O FastAPI suporta um recurso fascinante da biblioteca nativa Ao definir uma anota\u00e7\u00e3o de tipo, seguimos a seguinte formata\u00e7\u00e3o: O tipo Veja o exemplo a seguir: fast_zero/routes/users.py Desse modo, conseguimos refinar a defini\u00e7\u00e3o dos endpoints para que se tornem mais concisos, sem alterar seu funcionamento: fast_zero/routes/users.py Da mesma forma, podemos otimizar o roteador de autentica\u00e7\u00e3o: fast_zero/routers/auth.py Atrav\u00e9s do uso de tipos Conforme mencionamos na aula sobre os 12 fatores, \u00e9 uma boa pr\u00e1tica manter as constantes que podem mudar dependendo do ambiente em vari\u00e1veis de ambiente. Isso torna o seu projeto mais seguro e modular, pois voc\u00ea pode alterar essas constantes sem ter que modificar o c\u00f3digo-fonte. Por exemplo, temos estas constantes em nosso m\u00f3dulo Estes valores n\u00e3o devem estar diretamente no c\u00f3digo-fonte, ent\u00e3o vamos mov\u00ea-los para nossas vari\u00e1veis de ambiente e represent\u00e1-los na nossa classe J\u00e1 temos uma classe ideal para fazer isso em Agora, precisamos adicionar estes valores ao nosso arquivo Com isso, podemos alterar o nosso c\u00f3digo em Primeiramente, vamos carregar as configura\u00e7\u00f5es da classe Com isso, todos os lugares onde as constantes eram usadas devem ser substitu\u00eddos por Desta forma, eliminamos todas as constantes do c\u00f3digo-fonte e passamos a usar as configura\u00e7\u00f5es a partir da classe Depois de todas essas mudan\u00e7as, \u00e9 muito importante garantir que tudo ainda est\u00e1 funcionando corretamente. Para isso, vamos rodar todos os testes que temos at\u00e9 agora. $ Execu\u00e7\u00e3o no terminal! Se tudo estiver certo, todos os testes devem passar. Lembre-se de que a refatora\u00e7\u00e3o n\u00e3o deve alterar a funcionalidade do nosso c\u00f3digo - apenas torn\u00e1-lo mais f\u00e1cil de ler e manter. "},{"location":"06/#commit","title":"Commit","text":"Para finalizar, vamos criar um commit para registrar todas as altera\u00e7\u00f5es que fizemos na nossa aplica\u00e7\u00e3o. Como essa \u00e9 uma grande mudan\u00e7a que envolve reestruturar a forma como lidamos com as rotas e mover as constantes para vari\u00e1veis de ambiente, podemos usar uma mensagem de commit descritiva que explique todas as principais altera\u00e7\u00f5es: $ Execu\u00e7\u00e3o no terminal! "},{"location":"06/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, vimos como refatorar a estrutura do nosso projeto FastAPI para torn\u00e1-lo mais manuten\u00edvel. Organizamos nosso c\u00f3digo em diferentes arquivos e usamos o sistema de roteadores do FastAPI para separar diferentes partes da nossa API. Tamb\u00e9m mudamos algumas constantes para o arquivo de configura\u00e7\u00e3o, tornando nosso c\u00f3digo mais seguro e flex\u00edvel. Finalmente, atualizamos nossos testes para refletir a nova estrutura do projeto. Refatorar \u00e9 um processo cont\u00ednuo - sempre h\u00e1 espa\u00e7o para melhorias. No entanto, com a estrutura que estabelecemos hoje, estamos em uma boa posi\u00e7\u00e3o para continuar a expandir nossa API no futuro. Na pr\u00f3xima aula, vamos explorar mais sobre autentica\u00e7\u00e3o e como gerenciar tokens de acesso e de atualiza\u00e7\u00e3o em nossa API FastAPI. Fique ligado! "},{"location":"07/","title":"Tornando o sistema de autentica\u00e7\u00e3o robusto","text":""},{"location":"07/#tornando-o-sistema-de-autenticacao-robusto","title":"Tornando o sistema de autentica\u00e7\u00e3o robusto","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Na aula de hoje, vamos aprofundar nosso sistema de autentica\u00e7\u00e3o. J\u00e1 vimos em aulas anteriores como criar um sistema de autentica\u00e7\u00e3o b\u00e1sico, mas h\u00e1 muitas \u00e1reas em que podemos torn\u00e1-lo mais robusto. Por exemplo, como podemos lidar com situa\u00e7\u00f5es em que as coisas d\u00e3o errado? Como podemos garantir que nosso sistema seja seguro mesmo em cen\u00e1rios adversos? Essas s\u00e3o algumas das quest\u00f5es que vamos explorar hoje. Vamos come\u00e7ar examinando mais de perto os testes para autentica\u00e7\u00e3o. At\u00e9 agora, s\u00f3 testamos os casos que d\u00e3o certo - ou seja, quando o usu\u00e1rio sempre existe. Mas \u00e9 igualmente importante testar o que acontece quando as coisas d\u00e3o errado. Afinal, n\u00e3o podemos simplesmente assumir que tudo sempre vai correr bem. Por isso, vamos aprender como testar esses casos negativos. Em seguida, vamos implementar um recurso importante em qualquer sistema de autentica\u00e7\u00e3o: o refresh do token. Isso nos permite manter a sess\u00e3o do usu\u00e1rio ativa, mesmo se o token original expirar. "},{"location":"07/#testes-para-autenticacao","title":"Testes para autentica\u00e7\u00e3o","text":"Antes de mergulharmos nos testes, vamos falar um pouco sobre por que eles s\u00e3o t\u00e3o importantes. Na programa\u00e7\u00e3o, \u00e9 f\u00e1cil cair na armadilha de pensar que, se algo funciona na maioria das vezes, ent\u00e3o est\u00e1 tudo bem. Mas a verdade \u00e9 que \u00e9 nos casos marginais que os bugs mais dif\u00edceis de encontrar e corrigir costumam se esconder. Por exemplo, o que acontece se tentarmos autenticar um usu\u00e1rio que n\u00e3o existe? Ou se tentarmos autenticar com as credenciais erradas? Se n\u00e3o testarmos esses cen\u00e1rios, podemos acabar com um sistema que parece funcionar na superf\u00edcie, mas que na verdade est\u00e1 cheio de falhas de seguran\u00e7a. No c\u00f3digo apresentado, se observarmos atentamente, vemos que o erro Essa lacuna em nossos testes representa um risco potencial, pois n\u00e3o estamos verificando como nosso sistema se comporta quando algu\u00e9m tenta, por exemplo, alterar os detalhes de um usu\u00e1rio sem ter permiss\u00f5es adequadas. Embora possamos assumir que nosso sistema se comportar\u00e1 corretamente, a falta de testes nos deixa sem uma confirma\u00e7\u00e3o concreta. "},{"location":"07/#testando-a-alteracao-de-um-usuario-nao-autorizado","title":"Testando a altera\u00e7\u00e3o de um usu\u00e1rio n\u00e3o autorizado","text":"Agora, vamos come\u00e7ar a escrever alguns testes para esses casos. Vamos come\u00e7ar com um cen\u00e1rio simples: o que acontece quando um usu\u00e1rio tenta alterar as informa\u00e7\u00f5es de outro usu\u00e1rio? Para testar isso, vamos criar um novo teste chamado test_update_user_with_wrong_user. tests/test_users.py Este teste vai simular um usu\u00e1rio tentando alterar as informa\u00e7\u00f5es de outro usu\u00e1rio. Se nosso sistema estiver funcionando corretamente, ele dever\u00e1 rejeitar essa tentativa e retornar um erro."},{"location":"07/#criando-modelos-por-demanda-com-factory-boy","title":"Criando modelos por demanda com factory-boy","text":"Embora o teste que escrevemos esteja tecnicamente correto, ele ainda n\u00e3o funcionar\u00e1 adequadamente porque, atualmente, s\u00f3 temos um usu\u00e1rio em nosso banco de dados de testes. Precisamos de uma maneira de criar m\u00faltiplos usu\u00e1rios de teste facilmente, e \u00e9 a\u00ed que entra o O Para come\u00e7ar, precisamos instalar o Depois de instalar o Explicando linha a linha, esse c\u00f3digo faz o seguinte:
Essa f\u00e1brica pode ser usada em testes para criar inst\u00e2ncias de A seguir, podemos usar essa nova f\u00e1brica para criar m\u00faltiplos usu\u00e1rios de teste. Para fazer isso, modificamos nossa fixture de usu\u00e1rio existente para usar a UserFactory. Assim, sempre que executarmos nossos testes, teremos usu\u00e1rios diferentes dispon\u00edveis. tests/conftest.py A cria\u00e7\u00e3o de outra fixture chamada Um aspecto interessante no uso das f\u00e1bricas \u00e9 que, sempre que forem chamadas, elas retornar\u00e3o um novo Com essa nova configura\u00e7\u00e3o, podemos finalmente testar o cen\u00e1rio de um usu\u00e1rio tentando alterar as informa\u00e7\u00f5es de outro usu\u00e1rio. E como voc\u00ea pode ver, nossos testes passaram com sucesso, o que indica que nosso sistema est\u00e1 lidando corretamente com essa situa\u00e7\u00e3o. tests/test_users.py Neste caso, n\u00e3o estamos usando a fixture Para enfatizar, a fixture Com o teste implementado, vamos execut\u00e1-lo para ver se nosso sistema est\u00e1 protegido contra essa a\u00e7\u00e3o indevida: $ Execu\u00e7\u00e3o no terminal! Se todos os testes passaram com sucesso, isso indica que nosso sistema est\u00e1 se comportando como esperado, inclusive no caso de tentativas indevidas de deletar um usu\u00e1rio. "},{"location":"07/#testando-o-delete-com-o-usuario-errado","title":"Testando o DELETE com o usu\u00e1rio errado","text":"Continuando nossos testes, agora vamos testar o que acontece quando tentamos deletar um usu\u00e1rio com um usu\u00e1rio errado. Talvez voc\u00ea esteja se perguntando, por que precisamos fazer isso? Bem, lembre-se de que a seguran\u00e7a \u00e9 uma parte crucial de qualquer sistema de autentica\u00e7\u00e3o. Precisamos garantir que um usu\u00e1rio n\u00e3o possa deletar a conta de outro usu\u00e1rio - apenas a pr\u00f3pria conta. Portanto, \u00e9 importante que testemos esse cen\u00e1rio para garantir que nosso sistema est\u00e1 seguro. Aqui est\u00e1 o teste que vamos usar: tests/test_users.py Como voc\u00ea pode ver, esse teste tenta deletar o user de um id diferente usando o token do user. Se nosso sistema estiver funcionando corretamente, ele dever\u00e1 rejeitar essa tentativa e retornar um status 400 com uma mensagem de erro indicando que o usu\u00e1rio n\u00e3o tem permiss\u00f5es suficientes para realizar essa a\u00e7\u00e3o. Vamos executar esse teste agora e ver o que acontece: $ Execu\u00e7\u00e3o no terminal! \u00d3timo, nosso teste passou! Isso significa que nosso sistema est\u00e1 corretamente impedindo um usu\u00e1rio de deletar a conta de outro usu\u00e1rio. Agora que terminamos de testar a autoriza\u00e7\u00e3o, vamos passar para o pr\u00f3ximo desafio: testar tokens expirados. Lembre-se, em um sistema de autentica\u00e7\u00e3o robusto, um token deve expirar ap\u00f3s um certo per\u00edodo de tempo por motivos de seguran\u00e7a. Portanto, \u00e9 importante que testemos o que acontece quando tentamos usar um token expirado. Vamos ver isso na pr\u00f3xima se\u00e7\u00e3o. "},{"location":"07/#testando-a-expiracao-do-token","title":"Testando a expira\u00e7\u00e3o do token","text":"Continuando com nossos testes de autentica\u00e7\u00e3o, a pr\u00f3xima coisa que precisamos testar \u00e9 a expira\u00e7\u00e3o do token. Tokens de autentica\u00e7\u00e3o s\u00e3o normalmente projetados para expirar ap\u00f3s um certo per\u00edodo de tempo por motivos de seguran\u00e7a. Isso evita que algu\u00e9m que tenha obtido um token possa us\u00e1-lo indefinidamente se ele for roubado ou perdido. Portanto, \u00e9 importante que verifiquemos que nosso sistema esteja tratando corretamente a expira\u00e7\u00e3o dos tokens. Para realizar esse teste, vamos usar uma biblioteca chamada Primeiro, vamos precisar instalar a biblioteca: Agora vamos criar nosso teste. Vamos come\u00e7ar pegando um token para um usu\u00e1rio, congelando o tempo, esperando pelo tempo de expira\u00e7\u00e3o do token e, em seguida, tentando usar o token para acessar um endpoint que requer autentica\u00e7\u00e3o. Ao elaborarmos o teste, usaremos a funcionalidade de congelamento de tempo do Lembre-se de que configuramos nosso token para expirar ap\u00f3s 30 minutos. Portanto, n\u00f3s avan\u00e7amos o tempo em 31 minutos para garantir que o token tenha expirado. Agora, vamos executar nosso teste e ver o que acontece: $ Execu\u00e7\u00e3o no terminal! \u00d3timo, nosso teste passou! Isso confirma que nosso sistema est\u00e1 lidando corretamente com a expira\u00e7\u00e3o dos tokens. No entanto, ainda h\u00e1 uma coisa que precisamos implementar: a atualiza\u00e7\u00e3o de tokens. Atualmente, quando um token expira, o usu\u00e1rio teria que fazer login novamente para obter um novo token. Isso n\u00e3o \u00e9 uma \u00f3tima experi\u00eancia para o usu\u00e1rio. Em vez disso, gostar\u00edamos de oferecer a possibilidade de o usu\u00e1rio atualizar seu token quando ele estiver prestes a expirar. Vamos ver como fazer isso na pr\u00f3xima se\u00e7\u00e3o. "},{"location":"07/#testando-o-usuario-nao-existente-e-senha-incorreta","title":"Testando o usu\u00e1rio n\u00e3o existente e senha incorreta","text":"Na constru\u00e7\u00e3o de qualquer sistema de autentica\u00e7\u00e3o, \u00e9 crucial garantir que os casos de erro sejam tratados corretamente. Isso n\u00e3o s\u00f3 previne poss\u00edveis falhas de seguran\u00e7a, mas tamb\u00e9m permite fornecer feedback \u00fatil aos usu\u00e1rios. Em nossa implementa\u00e7\u00e3o atual, temos duas situa\u00e7\u00f5es espec\u00edficas que devem retornar um erro: quando um usu\u00e1rio inexistente tenta fazer login e quando uma senha incorreta \u00e9 fornecida. Vamos abordar esses casos de erro em nossos pr\u00f3ximos testes. Embora possa parecer redundante testar esses casos j\u00e1 que ambos resultam no mesmo erro, \u00e9 importante verificar que ambos os cen\u00e1rios est\u00e3o corretamente tratados. Isso nos permitir\u00e1 manter a robustez do nosso sistema conforme ele evolui e muda ao longo do tempo. "},{"location":"07/#testando-a-excecao-para-um-usuario-inexistente","title":"Testando a exce\u00e7\u00e3o para um usu\u00e1rio inexistente","text":"Para este cen\u00e1rio, precisamos enviar um request para o endpoint de token com um e-mail que n\u00e3o existe no banco de dados. A resposta esperada \u00e9 um HTTP 400 com a mensagem de detalhe 'Incorrect email or password'. tests/test_auth.py "},{"location":"07/#testando-a-excecao-para-uma-senha-incorreta","title":"Testando a exce\u00e7\u00e3o para uma senha incorreta","text":"Aqui, precisamos enviar um request para o endpoint de token com uma senha incorreta para um usu\u00e1rio existente. A resposta esperada \u00e9 um HTTP 400 com a mensagem de detalhe 'Incorrect email or password'. tests/test_auth.py Com esses testes, garantimos que nossas exce\u00e7\u00f5es est\u00e3o sendo lan\u00e7adas corretamente. Essa \u00e9 uma parte importante da constru\u00e7\u00e3o de um sistema de autentica\u00e7\u00e3o robusto, pois nos permite ter confian\u00e7a de que estamos tratando corretamente os casos de erro. "},{"location":"07/#implementando-o-refresh-do-token","title":"Implementando o refresh do token","text":"O processo de renova\u00e7\u00e3o de token \u00e9 uma parte essencial na implementa\u00e7\u00e3o de autentica\u00e7\u00e3o JWT. Em muitos sistemas, por raz\u00f5es de seguran\u00e7a, os tokens de acesso t\u00eam um tempo de vida relativamente curto. Isso significa que eles expiram ap\u00f3s um determinado per\u00edodo de tempo, e quando isso acontece, o cliente precisa obter um novo token para continuar acessando os recursos do servidor. Aqui \u00e9 onde o processo de renova\u00e7\u00e3o de token entra: permite que um cliente obtenha um novo token de acesso sem a necessidade de autentica\u00e7\u00e3o completa (por exemplo, sem ter que fornecer novamente o nome de usu\u00e1rio e senha). Agora vamos implementar a fun\u00e7\u00e3o de renova\u00e7\u00e3o de token em nosso c\u00f3digo. fast_zero/routes/auth.py Vamos tamb\u00e9m implementar um teste para verificar se a fun\u00e7\u00e3o de renova\u00e7\u00e3o de token est\u00e1 funcionando corretamente. tests/test_auth.py Ainda \u00e9 importante garantir que nosso sistema trate corretamente os tokens expirados. Para isso, vamos adicionar um teste que verifica se um token expirado n\u00e3o pode ser usado para renovar um token. tests/test_auth.py Agora, se executarmos nossos testes, todos eles devem passar, incluindo os novos testes que acabamos de adicionar. $ Execu\u00e7\u00e3o no terminal! Com esses testes, podemos ter certeza de que cobrimos alguns casos importantes relacionados \u00e0 autentica\u00e7\u00e3o de usu\u00e1rios em nossa API. "},{"location":"07/#commit","title":"Commit","text":"Agora, vamos fazer um commit com as altera\u00e7\u00f5es que fizemos. $ Execu\u00e7\u00e3o no terminal! "},{"location":"07/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, abordamos uma grande quantidade de t\u00f3picos cruciais para a constru\u00e7\u00e3o de uma aplica\u00e7\u00e3o web segura e robusta. Come\u00e7amos com a implementa\u00e7\u00e3o da funcionalidade de renova\u00e7\u00e3o do token JWT, uma pe\u00e7a fundamental na arquitetura de autentica\u00e7\u00e3o baseada em token. Este processo garante que os usu\u00e1rios possam continuar acessando a aplica\u00e7\u00e3o, mesmo ap\u00f3s o token inicial ter expirado, sem a necessidade de fornecer suas credenciais novamente. Por\u00e9m, a implementa\u00e7\u00e3o do c\u00f3digo foi apenas a primeira parte do que fizemos. Uma parte significativa da nossa aula foi dedicada a testar de maneira exaustiva a nossa aplica\u00e7\u00e3o. Escrevemos testes para verificar o comportamento b\u00e1sico das nossas rotas de autentica\u00e7\u00e3o, mas n\u00e3o paramos por a\u00ed. Tamb\u00e9m consideramos v\u00e1rios casos de borda que podem surgir durante a autentica\u00e7\u00e3o de um usu\u00e1rio. Testamos, por exemplo, o que acontece quando se tenta obter um token com credenciais incorretas. Verificamos o comportamento da nossa aplica\u00e7\u00e3o quando um token expirado \u00e9 utilizado. Esses testes nos ajudam a garantir que nossa aplica\u00e7\u00e3o se comporte de maneira adequada n\u00e3o apenas nas situa\u00e7\u00f5es mais comuns, mas tamb\u00e9m quando algo sai do esperado. Al\u00e9m disso, ao implementar esses testes, n\u00f3s garantimos que futuras altera\u00e7\u00f5es no nosso c\u00f3digo n\u00e3o ir\u00e3o quebrar funcionalidades j\u00e1 existentes. Testes automatizados s\u00e3o uma parte fundamental de qualquer aplica\u00e7\u00e3o de alta qualidade, e o que fizemos hoje vai al\u00e9m do b\u00e1sico, mostrando como lidar com cen\u00e1rios complexos e realistas. A implementa\u00e7\u00e3o e os testes que fizemos hoje nos levam um passo adiante no desenvolvimento da nossa aplica\u00e7\u00e3o, deixando-a mais pr\u00f3xima de estar pronta para um ambiente de produ\u00e7\u00e3o. Na pr\u00f3xima aula, vamos utilizar a infraestrutura de autentica\u00e7\u00e3o que criamos hoje para permitir que os usu\u00e1rios criem, leiam, atualizem e deletem suas pr\u00f3prias listas de tarefas. Isso vai nos permitir explorar ainda mais as funcionalidades do FastAPI e do SQLAlchemy, al\u00e9m de continuar a expandir a nossa su\u00edte de testes. Esperamos ver voc\u00ea na pr\u00f3xima aula! "},{"location":"08/","title":"Criando Rotas CRUD para Gerenciamento de Tarefas em FastAPI","text":""},{"location":"08/#criando-rotas-crud-para-gerenciamento-de-tarefas-em-fastapi","title":"Criando Rotas CRUD para Gerenciamento de Tarefas em FastAPI","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Ol\u00e1 a todos! Estamos de volta com mais uma aula. Hoje vamos mergulhar na cria\u00e7\u00e3o das rotas CRUD para as nossas tarefas utilizando FastAPI. Essas opera\u00e7\u00f5es s\u00e3o fundamentais para qualquer aplica\u00e7\u00e3o de gerenciamento de tarefas e s\u00e3o o cora\u00e7\u00e3o do nosso sistema. Al\u00e9m disso, garantiremos que apenas o usu\u00e1rio que criou a tarefa possa acess\u00e1-la e modific\u00e1-la, garantindo a seguran\u00e7a e a privacidade dos dados. Vamos come\u00e7ar! "},{"location":"08/#estrutura-inicial-do-codigo","title":"Estrutura inicial do c\u00f3digo","text":"Primeiro, vamos criar um novo arquivo chamado Neste c\u00f3digo, criamos uma nova inst\u00e2ncia da classe A op\u00e7\u00e3o A op\u00e7\u00e3o Depois de definir o roteador, precisamos inclu\u00ed-lo em nossa aplica\u00e7\u00e3o principal. Vamos atualizar o arquivo Neste c\u00f3digo, chamamos o m\u00e9todo Agora, iremos implementar a tabela 'Todos' no nosso banco de dados. Esta tabela estar\u00e1 diretamente relacionada \u00e0 tabela 'User', pois toda tarefa pertence a um usu\u00e1rio. Esta rela\u00e7\u00e3o \u00e9 crucial para garantir que s\u00f3 o usu\u00e1rio dono da tarefa possa acessar e modificar suas tarefas. fast_zero/models.py Neste ponto, \u00e9 importante compreender o conceito de O uso do tipo Enum em Estes conceitos podem parecer um pouco complexos agora, mas ficar\u00e3o mais claros quando come\u00e7armos a implementar os testes. "},{"location":"08/#testando-as-novas-implementacoes-do-banco-de-dados","title":"Testando as novas implementa\u00e7\u00f5es do banco de dados","text":"Embora tenhamos 100% de cobertura de c\u00f3digo, isso n\u00e3o garante que tudo esteja funcionando corretamente. S\u00f3 implementamos a estrutura do banco de dados, mas n\u00e3o testamos a l\u00f3gica de como as tabelas e as rela\u00e7\u00f5es funcionam na pr\u00e1tica. Para isso, criamos um teste para verificar se a rela\u00e7\u00e3o entre tarefas e usu\u00e1rios est\u00e1 funcionando corretamente. Este teste cria uma nova tarefa para um usu\u00e1rio e verifica se essa tarefa aparece na lista de tarefas desse usu\u00e1rio. tests/test_db.py Com isso, voc\u00ea pode executar os testes: $ Execu\u00e7\u00e3o no terminal! Isso mostra que os testes foram bem-sucedidos. Mesmo sem testes mais extensivos, agora vamos come\u00e7ar a criar os esquemas para esse modelo e, em seguida, os endpoints. "},{"location":"08/#schemas-para-todos","title":"Schemas para Todos","text":"Vamos criar dois esquemas para nosso modelo de tarefas (todos):
Criamos o primeiro endpoint para a cria\u00e7\u00e3o de tarefas. Este \u00e9 um endpoint POST na rota '/todos'. \u00c9 importante destacar que, para criar uma tarefa, um usu\u00e1rio precisa estar autenticado e s\u00f3 esse usu\u00e1rio autenticado ser\u00e1 o propriet\u00e1rio da tarefa. fast_zero/routes/todos.py Neste endpoint, fazemos uso da depend\u00eancia Para garantir que nosso endpoint est\u00e1 funcionando corretamente, criamos um teste para ele. Este teste verifica se o endpoint '/todos' est\u00e1 criando tarefas corretamente. tests/test_todos.py No teste, fazemos uma requisi\u00e7\u00e3o POST para o endpoint '/todos' passando um token de autentica\u00e7\u00e3o v\u00e1lido e um JSON com os dados da tarefa a ser criada. Em seguida, verificamos se a resposta cont\u00e9m os dados corretos da tarefa criada. Para executar este teste, voc\u00ea deve usar o comando abaixo no terminal: $ Execu\u00e7\u00e3o no terminal! Com essa implementa\u00e7\u00e3o, os testes devem passar. Por\u00e9m, apesar do sucesso dos testes, nosso c\u00f3digo ainda n\u00e3o est\u00e1 completamente pronto. Ainda \u00e9 necess\u00e1rio criar uma migra\u00e7\u00e3o para a tabela de tarefas no banco de dados. "},{"location":"08/#criando-a-migracao-da-nova-tabela","title":"Criando a migra\u00e7\u00e3o da nova tabela","text":"Agora que temos nosso modelo de tarefas definido, precisamos criar uma migra\u00e7\u00e3o para adicionar a tabela de tarefas ao nosso banco de dados. Usamos o Alembic para criar e gerenciar nossas migra\u00e7\u00f5es. $ Execu\u00e7\u00e3o no terminal! Este comando gera um arquivo de migra\u00e7\u00e3o, que se parece com o c\u00f3digo abaixo: migrations/versions/de865434f506_create_todos_table.py Depois que a migra\u00e7\u00e3o for criada, precisamos aplic\u00e1-la ao nosso banco de dados. Execute o comando Agora que a migra\u00e7\u00e3o foi aplicada, nosso banco de dados deve ter uma nova tabela de tarefas. Para verificar, voc\u00ea pode abrir o banco de dados com o comando Finalmente, agora que temos a tabela de tarefas em nosso banco de dados, podemos testar nosso endpoint de cria\u00e7\u00e3o de tarefas no Swagger. Para fazer isso, execute nosso servidor FastAPI e abra o Swagger no seu navegador. "},{"location":"08/#endpoint-de-listagem","title":"Endpoint de listagem","text":"Agora que criamos a nossa migra\u00e7\u00e3o e temos o endpoint de cria\u00e7\u00e3o de Todos, temos que criar nosso endpoint de listagem de tarefas. Ele deve listar todas as tarefas de acordo com o Algumas coisas adicionais e que podem ser importantes na hora de recuperar as tarefas \u00e9 fazer um filtro de busca. Em alguns momentos queremos buscar uma tarefa por t\u00edtulo, em outro momento por descri\u00e7\u00e3o, \u00e0s vezes s\u00f3 pelo estado. Por exemplo, somente tarefas conclu\u00eddas. Para fazer isso, podemos contar com um recurso do FastAPI chamado Por exemplo, uma query string simples pode ser: Uma caracter\u00edstica importante das queries do FastAPI \u00e9 que podemos juntar mais de um atributo em uma busca. Por exemplo, podemos buscar somente as tarefas a fazer que contenham no t\u00edtulo \"trabalho\". Dessa forma, temos um endpoint mais eficiente, j\u00e1 que podemos realizar buscas complexas e refinadas com uma \u00fanica chamada. A combina\u00e7\u00e3o poderia ser algo como: A combina\u00e7\u00e3o de diferentes par\u00e2metros de query n\u00e3o s\u00f3 torna o endpoint mais flex\u00edvel, mas tamb\u00e9m permite que os usu\u00e1rios obtenham os dados de que precisam de maneira mais r\u00e1pida e conveniente. Isso contribui para uma melhor experi\u00eancia do usu\u00e1rio e otimiza a intera\u00e7\u00e3o com o banco de dados. O c\u00f3digo a seguir ilustra como o endpoint de listagem \u00e9 definido utilizando a Essa abordagem equilibra a flexibilidade e a efici\u00eancia, tornando o endpoint capaz de atender a uma variedade de necessidades de neg\u00f3cio. Utilizando os recursos do FastAPI, conseguimos implementar uma solu\u00e7\u00e3o robusta e f\u00e1cil de manter, que ser\u00e1 testada posteriormente para garantir sua funcionalidade e integridade. No c\u00f3digo acima, estamos utilizando filtros do SQLAlchemy, uma biblioteca ORM (Object-Relational Mapping) do Python, para adicionar condi\u00e7\u00f5es \u00e0 nossa consulta. Esses filtros correspondem aos par\u00e2metros que o usu\u00e1rio pode passar na URL.
Essas condi\u00e7\u00f5es s\u00e3o traduzidas em cl\u00e1usulas SQL pelo SQLAlchemy, permitindo que o banco de dados filtre os resultados de acordo com os crit\u00e9rios especificados pelo usu\u00e1rio. Essa integra\u00e7\u00e3o entre FastAPI e SQLAlchemy torna o processo de filtragem eficiente e a codifica\u00e7\u00e3o mais expressiva e clara. "},{"location":"08/#criando-uma-factory-para-simplificar-os-testes","title":"Criando uma factory para simplificar os testes","text":"Criar uma factory para o endpoint facilitaria os testes por diversas raz\u00f5es, especialmente quando se trata de testar o nosso endpoint de listagem que faz uso de m\u00faltiplas queries. Primeiro, a factory ajuda a encapsular a l\u00f3gica de cria\u00e7\u00e3o dos objetos necess\u00e1rios para o teste, como no caso dos objetos Com a complexidade das queries que nosso endpoint permite, precisamos cobrir todos os usos poss\u00edveis dessas queries. A factory vai nos ajudar a criar muitos casos de testes de forma pr\u00e1tica e eficiente, j\u00e1 que podemos gerar diferentes combina\u00e7\u00f5es de t\u00edtulos, descri\u00e7\u00f5es, estados, entre outros atributos, simulando diversas situa\u00e7\u00f5es de uso. Al\u00e9m disso, ao utilizar bibliotecas como o A fixture acima pode ser usada em diversos testes, reduzindo a duplica\u00e7\u00e3o de c\u00f3digo e melhorando a manuten\u00e7\u00e3o. Por exemplo, em um teste que precisa criar v\u00e1rios objetos A utiliza\u00e7\u00e3o de f\u00e1bricas tamb\u00e9m promove uma melhor separa\u00e7\u00e3o entre a l\u00f3gica de cria\u00e7\u00e3o do objeto e a l\u00f3gica do teste, tornando os testes mais leg\u00edveis e f\u00e1ceis de seguir. Com a Ao trabalhar com o endpoint de listagem de tarefas, temos v\u00e1rias varia\u00e7\u00f5es de query strings que precisam ser testadas. Cada uma dessas varia\u00e7\u00f5es representa um caso de uso diferente, e queremos garantir que o sistema funcione corretamente em todos eles. Vamos separar os testes em pequenos blocos e explicar cada um deles. "},{"location":"08/#testando-a-listagem-de-todos","title":"Testando a Listagem de Todos","text":"Primeiro, vamos criar um teste b\u00e1sico que verifica se o endpoint est\u00e1 listando todos os objetos Este teste valida que todos os 5 objetos Em seguida, vamos testar a pagina\u00e7\u00e3o para garantir que o offset e o limite estejam funcionando corretamente. tests/test_todos.py Este teste verifica que, quando aplicado o offset de 1 e o limite de 2, apenas 2 objetos Tamb\u00e9m queremos verificar se a filtragem por t\u00edtulo est\u00e1 funcionando conforme esperado. tests/test_todos.py Este teste garante que quando o filtro de t\u00edtulo \u00e9 aplicado, apenas as tarefas com o t\u00edtulo correspondente s\u00e3o retornadas. "},{"location":"08/#testando-o-filtro-por-descricao","title":"Testando o Filtro por Descri\u00e7\u00e3o","text":"Da mesma forma, queremos testar o filtro de descri\u00e7\u00e3o. tests/test_todos.py Este teste verifica que, quando filtramos pela descri\u00e7\u00e3o, apenas as tarefas com a descri\u00e7\u00e3o correspondente s\u00e3o retornadas. "},{"location":"08/#testando-o-filtro-por-estado","title":"Testando o Filtro por Estado","text":"Finalmente, precisamos testar o filtro de estado. tests/test_todos.py Este teste garante que quando filtramos pelo estado, apenas as tarefas com o estado correspondente s\u00e3o retornadas. "},{"location":"08/#testando-a-combinacao-de-filtros-de-estado-titulo-e-descricao","title":"Testando a Combina\u00e7\u00e3o de Filtros de Estado, T\u00edtulo e Descri\u00e7\u00e3o","text":"Em nosso conjunto de testes, tamb\u00e9m \u00e9 importante verificar se o endpoint \u00e9 capaz de lidar com m\u00faltiplos par\u00e2metros de consulta simultaneamente. Para isso, vamos criar um teste que combine os filtros de estado, t\u00edtulo e descri\u00e7\u00e3o. Isso assegurar\u00e1 que, quando esses par\u00e2metros s\u00e3o usados juntos, o endpoint retornar\u00e1 apenas as tarefas que correspondem a todas essas condi\u00e7\u00f5es. Este teste \u00e9 vital para garantir que os usu\u00e1rios podem realizar buscas complexas usando v\u00e1rios crit\u00e9rios ao mesmo tempo, e que o endpoint ir\u00e1 retornar os resultados esperados. A seguir, apresento o c\u00f3digo do teste: tests/test_todos.py Com esses testes, cobrimos todas as poss\u00edveis varia\u00e7\u00f5es de query strings para o nosso endpoint, garantindo que ele funciona corretamente em todas essas situa\u00e7\u00f5es. A abordagem modular para escrever esses testes facilita a leitura e a manuten\u00e7\u00e3o, al\u00e9m de permitir uma cobertura de teste abrangente e robusta. "},{"location":"08/#executando-os-testes","title":"Executando os testes","text":"Importante para que n\u00e3o esque\u00e7amos \u00e9 de executar os testes para ver se tudo corre bem: "},{"location":"08/#endpoint-de-alteracao","title":"Endpoint de Altera\u00e7\u00e3o","text":"Para fazer a altera\u00e7\u00e3o de uma tarefa, precisamos de um modelo onde tudo seja opcional, j\u00e1 que poder\u00edamos querer atualizar apenas um ou alguns campos da tarefa. Vamos criar o esquema Para podermos alterar somente os valores que recebemos no modelo, temos que fazer um A linha O par\u00e2metro Depois de obter a chave e o valor de cada campo definido, a linha Dessa forma, garantimos que somente os campos enviados ao schema sejam atualizados no objeto. "},{"location":"08/#testes-para-o-endpoint-de-alteracao","title":"Testes para o Endpoint de Altera\u00e7\u00e3o","text":"Os testes aqui incluem o caso de atualiza\u00e7\u00e3o bem-sucedida e o caso de erro quando a tarefa n\u00e3o \u00e9 encontrada: fast_zero/tests/test_todos.py Agora precisamos executar os testes para ver se est\u00e1 tudo correto: $ Execu\u00e7\u00e3o no terminal! Com tudo funcionando, podemos partir para o nosso endpoint de DELETE. "},{"location":"08/#endpoint-de-delecao","title":"Endpoint de Dele\u00e7\u00e3o","text":"A rota para deletar uma tarefa \u00e9 simples e direta. Caso o todo exista, vamos deletar ele com a "},{"location":"08/#testes-para-o-endpoint-de-delecao","title":"Testes para o Endpoint de Dele\u00e7\u00e3o","text":"Esses testes verificam tanto a remo\u00e7\u00e3o bem-sucedida quanto o caso de erro quando a tarefa n\u00e3o \u00e9 encontrada: fast_zero/tests/test_todos.py Por fim, precisamos executar os testes para ver se est\u00e1 tudo correto: $ Execu\u00e7\u00e3o no terminal! "},{"location":"08/#commit","title":"Commit","text":"Agora que voc\u00ea finalizou a implementa\u00e7\u00e3o desses endpoints, \u00e9 um bom momento para fazer um commit das suas mudan\u00e7as. Para isso, voc\u00ea pode seguir os seguintes passos:
Nesta aula exploramos os aspectos essenciais para construir uma API completa e funcional para gerenciar tarefas, integrando-se ao sistema de autentica\u00e7\u00e3o que j\u00e1 t\u00ednhamos desenvolvido. Iniciamos criando a estrutura de banco de dados para as tarefas, incluindo tabelas e migra\u00e7\u00f5es, e em seguida definimos os schemas necess\u00e1rios. A partir da\u00ed, trabalhamos na cria\u00e7\u00e3o dos endpoints para as opera\u00e7\u00f5es CRUD: cria\u00e7\u00e3o, leitura (listagem com filtragem), atualiza\u00e7\u00e3o (edi\u00e7\u00e3o) e exclus\u00e3o (dele\u00e7\u00e3o). Em cada est\u00e1gio, focamos na qualidade e na robustez, utilizando testes rigorosos para assegurar que os endpoints se comportassem conforme esperado. Exploramos tamb\u00e9m t\u00e9cnicas espec\u00edficas como atualiza\u00e7\u00e3o parcial e filtragem avan\u00e7ada, tornando a API flex\u00edvel e poderosa. O resultado foi um sistema integrado de gerenciamento de tarefas, ou um \"todo list\", ligado aos usu\u00e1rios e \u00e0 autentica\u00e7\u00e3o que j\u00e1 hav\u00edamos implementado. Esta aula refor\u00e7ou a import\u00e2ncia de um design cuidadoso e uma implementa\u00e7\u00e3o criteriosa, ilustrando como a FastAPI pode ser usada para criar APIs eficientes e profissionais. Agora que a nossa aplica\u00e7\u00e3o est\u00e1 crescendo e ganhando mais funcionalidades, na pr\u00f3xima aula, vamos mergulhar no mundo da dockeriza\u00e7\u00e3o. Iremos aprender a colocar a nossa aplica\u00e7\u00e3o dentro de um container Docker, facilitando o deploy e o escalonamento. Este \u00e9 um passo vital no desenvolvimento moderno de aplica\u00e7\u00f5es e estou ansioso para gui\u00e1-lo atrav\u00e9s dele. At\u00e9 l\u00e1! "},{"location":"09/","title":"Dockerizando a nossa aplica\u00e7\u00e3o e introduzindo o PostgreSQL","text":""},{"location":"09/#dockerizando-a-nossa-aplicacao-e-introduzindo-o-postgresql","title":"Dockerizando a nossa aplica\u00e7\u00e3o e introduzindo o PostgreSQL","text":"Objetivos da aula:
Aula Slides C\u00f3digo Depois de implementar nosso gerenciador de tarefas na aula anterior, temos uma primeira vers\u00e3o est\u00e1vel da nossa aplica\u00e7\u00e3o. Nesta aula, al\u00e9m de aprendermos a \"dockerizar\" nossa aplica\u00e7\u00e3o FastAPI, tamb\u00e9m abordaremos a migra\u00e7\u00e3o do banco de dados SQLite para o PostgreSQL. "},{"location":"09/#o-docker-e-a-nossa-aplicacao","title":"O Docker e a nossa aplica\u00e7\u00e3o","text":"Docker \u00e9 uma plataforma aberta que permite automatizar o processo de implanta\u00e7\u00e3o, escalonamento e opera\u00e7\u00e3o de aplica\u00e7\u00f5es dentro de cont\u00eaineres. Ele serve para \"empacotar\" uma aplica\u00e7\u00e3o e suas depend\u00eancias em um cont\u00eainer virtual que pode ser executado em qualquer sistema operacional que suporte Docker. Isso facilita a implanta\u00e7\u00e3o, o desenvolvimento e o compartilhamento de aplica\u00e7\u00f5es, al\u00e9m de proporcionar um ambiente isolado e consistente. "},{"location":"09/#criando-nosso-dockerfile","title":"Criando nosso Dockerfile","text":"Para criar um container Docker, escrevemos uma lista de passos de como construir o ambiente para execu\u00e7\u00e3o da nossa aplica\u00e7\u00e3o em um arquivo chamado Uma das coisas interessantes sobre Docker \u00e9 que existe um Hub de containers prontos onde a comunidade hospeda imagens \"prontas\", que podemos usar como ponto de partida. Por exemplo, a comunidade de python mant\u00e9m um grupo de imagens com o ambiente python pronto para uso. Podemos partir dessa imagem com o python j\u00e1 instalado adicionar os passos para que nossa aplica\u00e7\u00e3o seja executada. Aqui est\u00e1 um exemplo de Aqui est\u00e1 o que cada linha faz:
Vamos entender melhor esse \u00faltimo comando:
Para criar uma imagem Docker a partir do Dockerfile, usamos o comando Este comando l\u00ea o Dockerfile no diret\u00f3rio atual (indicado pelo Vamos ent\u00e3o verificar se a imagem foi criada com sucesso usando o comando: $ Execu\u00e7\u00e3o no terminal! Este comando lista todas as imagens Docker dispon\u00edveis no seu sistema. "},{"location":"09/#executando-o-container","title":"Executando o container","text":"Para executar o cont\u00eainer, usamos o comando Este comando iniciar\u00e1 nossa aplica\u00e7\u00e3o dentro de um cont\u00eainer Docker, que estar\u00e1 escutando na porta 8000. Para testar se tudo est\u00e1 funcionando corretamente, voc\u00ea pode acessar Caso voc\u00ea fique preso no terminal Caso voc\u00ea tenha a aplica\u00e7\u00e3o travada no terminal e n\u00e3o consiga sair, voc\u00ea pode teclar Ctrl+C para parar a execu\u00e7\u00e3o do container. "},{"location":"09/#gerenciando-containers-docker","title":"Gerenciando Containers docker","text":"Quando voc\u00ea trabalha com Docker, \u00e9 importante saber como gerenciar os cont\u00eaineres. Aqui est\u00e3o algumas opera\u00e7\u00f5es b\u00e1sicas para gerenci\u00e1-los:
Ambos os comandos (stop e rm) usam o nome do cont\u00eainer que definimos anteriormente com a flag O PostgreSQL \u00e9 um Sistema de Gerenciamento de Banco de Dados Objeto-Relacional (ORDBMS) poderoso e de c\u00f3digo aberto. Ele \u00e9 amplamente utilizado em produ\u00e7\u00e3o em muitos projetos devido \u00e0 sua robustez, escalabilidade e conjunto de recursos extensos. Mudar para um banco de dados como PostgreSQL tem v\u00e1rios benef\u00edcios:
Al\u00e9m disso, SQLite tem algumas limita\u00e7\u00f5es que podem torn\u00e1-lo inadequado para produ\u00e7\u00e3o em alguns casos. Por exemplo, ele n\u00e3o suporta alta concorr\u00eancia e pode ter problemas de performance com grandes volumes de dados. Nota Embora para o escopo da nossa aplica\u00e7\u00e3o e os objetivos de aprendizado o SQLite pudesse ser suficiente, \u00e9 sempre bom nos prepararmos para cen\u00e1rios de produ\u00e7\u00e3o real. A ado\u00e7\u00e3o de PostgreSQL nos d\u00e1 uma pr\u00e9via das pr\u00e1ticas do mundo real e garante que nossa aplica\u00e7\u00e3o possa escalar sem grandes modifica\u00e7\u00f5es de infraestrutura. "},{"location":"09/#como-executar-o-postgres","title":"Como executar o postgres?","text":"Embora o PostgreSQL seja poderoso, sua instala\u00e7\u00e3o direta em uma m\u00e1quina real pode ser desafiadora e pode resultar em configura\u00e7\u00f5es diferentes entre os ambientes de desenvolvimento. Felizmente, podemos utilizar o Docker para resolver esse problema. No Docker Hub, est\u00e3o dispon\u00edveis imagens pr\u00e9-constru\u00eddas do PostgreSQL, permitindo-nos executar o PostgreSQL com um \u00fanico comando. Confira a imagem oficial do PostgreSQL. Para executar um cont\u00eainer do PostgreSQL, use o seguinte comando: $ Execu\u00e7\u00e3o no terminal! "},{"location":"09/#explicando-as-flags-e-configuracoes","title":"Explicando as Flags e Configura\u00e7\u00f5es","text":"
Esta flag \u00e9 usada para definir vari\u00e1veis de ambiente no cont\u00eainer. No contexto do PostgreSQL, essas vari\u00e1veis s\u00e3o essenciais. Elas configuram o nome de usu\u00e1rio, nome do banco de dados, e senha durante a primeira execu\u00e7\u00e3o do cont\u00eainer. Sem elas, o PostgreSQL pode n\u00e3o iniciar da forma esperada. \u00c9 uma forma pr\u00e1tica de configurar o PostgreSQL sem interagir manualmente ou criar arquivos de configura\u00e7\u00e3o.
O PostgreSQL, por padr\u00e3o, escuta por conex\u00f5es na porta Sobre as vari\u00e1veis Os valores acima ( Para garantir a persist\u00eancia dos dados entre execu\u00e7\u00f5es do cont\u00eainer, utilizamos volumes. Um volume mapeia um diret\u00f3rio do sistema host para um diret\u00f3rio no cont\u00eainer. Isso \u00e9 crucial para bancos de dados, pois sem um volume, ao remover o cont\u00eainer, todos os dados armazenados dentro dele se perderiam. No PostgreSQL, o diret\u00f3rio padr\u00e3o para armazenamento de dados \u00e9 O par\u00e2metro do volume \u00e9 passado ao cont\u00eainer usando o par\u00e2metro Para que o SQLAlchemy suporte o PostgreSQL, precisamos instalar uma depend\u00eancia chamada Para instalar essa depend\u00eancia, utilize o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Uma das vantagens do SQLAlchemy enquanto ORM \u00e9 a flexibilidade. Com apenas algumas altera\u00e7\u00f5es m\u00ednimas, como a atualiza\u00e7\u00e3o da string de conex\u00e3o, podemos facilmente transicionar para um banco de dados diferente. Assim, ap\u00f3s ajustar o arquivo Para ajustar a conex\u00e3o com o PostgreSQL, modifique seu arquivo Caso tenha alterado as vari\u00e1veis de ambiente do cont\u00eainer Se voc\u00ea alterou Migra\u00e7\u00f5es s\u00e3o como vers\u00f5es para seu banco de dados, permitindo que voc\u00ea atualize sua estrutura de forma ordenada e controlada. Sempre que mudamos de banco de dados, ou at\u00e9 mesmo quando alteramos sua estrutura, as migra\u00e7\u00f5es precisam ser executadas para garantir que a base de dados esteja em sincronia com nosso c\u00f3digo. No contexto de cont\u00eaineres, rodar as migra\u00e7\u00f5es se torna ainda mais simples. Quando mudamos de banco de dados, como \u00e9 o caso de termos sa\u00eddo de um SQLite (por exemplo) para um PostgreSQL, as migra\u00e7\u00f5es s\u00e3o essenciais. O motivo \u00e9 simples: o novo banco de dados n\u00e3o ter\u00e1 a estrutura e os dados do antigo, a menos que migremos. As migra\u00e7\u00f5es ir\u00e3o garantir que o novo banco de dados tenha a mesma estrutura e rela\u00e7\u00f5es que o anterior. Antes de executar o proximo comandoAssegure-se de que ambos os cont\u00eaineres, tanto da aplica\u00e7\u00e3o quanto do banco de dados, estejam ativos. O cont\u00eainer do banco de dados deve estar rodando para que a aplica\u00e7\u00e3o possa se conectar a ele. Assegure-se de que o cont\u00eainer da aplica\u00e7\u00e3o esteja ativo. Estamos usando a flag Para aplicar migra\u00e7\u00f5es em um ambiente com cont\u00eaineres, frequentemente temos comandos espec\u00edficos associados ao servi\u00e7o. Vejamos como executar migra\u00e7\u00f5es usando o Docker: $ Execu\u00e7\u00e3o no terminal! O comando Ap\u00f3s executar as migra\u00e7\u00f5es, voc\u00ea pode verificar a cria\u00e7\u00e3o das tabelas utilizando um sistema de gerenciamento de banco de dados. A seguir, apresentamos um exemplo com o Beekeeper Studio: Lembre-se: Embora as tabelas estejam agora criadas e estruturadas, o banco de dados ainda n\u00e3o cont\u00e9m os dados anteriormente presentes no SQLite ou em qualquer outro banco que voc\u00ea estivesse utilizando antes. "},{"location":"09/#simplificando-nosso-fluxo-com-docker-compose","title":"Simplificando nosso fluxo comdocker-compose ","text":"Docker Compose \u00e9 uma ferramenta que permite definir e gerenciar aplicativos multi-cont\u00eainer com Docker. \u00c9 como se voc\u00ea tivesse um maestro conduzindo uma orquestra: o maestro (ou Docker Compose) garante que todos os m\u00fasicos (ou cont\u00eaineres) toquem em harmonia. Definimos nossa aplica\u00e7\u00e3o e servi\u00e7os relacionados, como o PostgreSQL, em um arquivo Ao adotar o Docker Compose, facilitamos o desenvolvimento e a execu\u00e7\u00e3o da nossa aplica\u00e7\u00e3o com seus servi\u00e7os dependentes utilizando um \u00fanico comando. "},{"location":"09/#criacao-do-docker-composeyml","title":"Cria\u00e7\u00e3o dodocker-compose.yml ","text":"docker-compose.yaml Explica\u00e7\u00e3o linha a linha:
Sobre o docker-compose Para usar o Docker Compose, voc\u00ea precisa t\u00ea-lo instalado em seu sistema. Ele n\u00e3o est\u00e1 inclu\u00eddo na instala\u00e7\u00e3o padr\u00e3o do Docker, ent\u00e3o lembre-se de instal\u00e1-lo separadamente! O guia oficial de instala\u00e7\u00e3o pode ser encontrado aqui Com este arquivo Para parar os servi\u00e7os e manter os dados seguros nos volumes definidos, use: Esses comandos simplificam o fluxo de trabalho e garantem que os servi\u00e7os iniciem corretamente e se comuniquem conforme o esperado. Execu\u00e7\u00e3o em modo desanexado Voc\u00ea pode iniciar os servi\u00e7os em segundo plano com a flag Automatizar as migra\u00e7\u00f5es do banco de dados \u00e9 uma pr\u00e1tica recomendada para garantir que sua aplica\u00e7\u00e3o esteja sempre sincronizada com o estado mais atual do seu esquema de banco de dados. \u00c9 como preparar todos os ingredientes antes de come\u00e7ar a cozinhar: voc\u00ea garante que tudo o que \u00e9 necess\u00e1rio est\u00e1 pronto para ser usado. Para automatizar as migra\u00e7\u00f5es em nossos cont\u00eaineres Docker, utilizamos um Por que usar o Entrypoint? No Docker, o Implementando o Entrypoint Criamos um script chamado Explica\u00e7\u00e3o Detalhada do Script:
Como Funciona na Pr\u00e1tica? Quando o cont\u00eainer \u00e9 iniciado, o Docker executa o script de Visualizando o Processo: Voc\u00ea pode pensar no Adicionando o Entrypoint ao Docker Compose: Inclu\u00edmos o Reconstruindo e Executando com Novas Configura\u00e7\u00f5es: Para aplicar as altera\u00e7\u00f5es, reconstru\u00edmos e executamos os servi\u00e7os com a op\u00e7\u00e3o Observando o Comportamento Esperado: Quando o cont\u00eainer \u00e9 iniciado, voc\u00ea deve ver as migra\u00e7\u00f5es sendo aplicadas, seguidas pela inicializa\u00e7\u00e3o da aplica\u00e7\u00e3o: $ Exemplo do resultado no terminal! Este processo garante que as migra\u00e7\u00f5es do banco de dados s\u00e3o realizadas automaticamente, mantendo a base de dados alinhada com a aplica\u00e7\u00e3o e pronta para a\u00e7\u00e3o assim que o servidor Uvicorn entra em cena. Nota de revis\u00e3o sobre vari\u00e1veis de ambienteUtilizar vari\u00e1veis de ambiente definidas em um arquivo Ainda assim, \u00e9 valioso mencionar como essa configura\u00e7\u00e3o mais segura seria realizada, especialmente para aqueles que planejam utilizar o Docker Compose em produ\u00e7\u00e3o. Em ambientes de produ\u00e7\u00e3o com Docker Compose, \u00e9 uma boa pr\u00e1tica gerenciar vari\u00e1veis de ambiente sens\u00edveis, como credenciais, por meio de um arquivo As vari\u00e1veis de ambiente podem ser definidas em nosso arquivo Para aplicar essas vari\u00e1veis, referencie o arquivo Adotar essa abordagem evita a exposi\u00e7\u00e3o das vari\u00e1veis de ambiente no arquivo de configura\u00e7\u00e3o. Esta n\u00e3o foi a abordagem padr\u00e3o no curso devido \u00e0 complexidade adicional e \u00e0 inten\u00e7\u00e3o de evitar confus\u00f5es. Dependendo do ambiente estabelecido pela equipe de DevOps/SRE em um projeto real, essa gest\u00e3o pode variar entre vari\u00e1veis de ambiente, arquivos Se optar por utilizar um arquivo Com essa configura\u00e7\u00e3o, o Pydantic ir\u00e1 ignorar quaisquer vari\u00e1veis no Agradecimentos especiais a @vcwild e @williangl pelas revis\u00f5es valiosas nesta aula que me fizeram criar essa nota. "},{"location":"09/#testes-com-docker","title":"Testes com Docker","text":"Agora que temos o Para executar os testes, utilizamos o comando: $ Execu\u00e7\u00e3o no terminal! Vamos entender melhor o que cada parte do comando faz:
Ao utilizar esse comando, o Docker Compose cuidar\u00e1 de iniciar os servi\u00e7os dos quais Se executarmos o comando podemos ver que ele inicia o banco de dados, inicia o container da aplica\u00e7\u00e3o e na sequ\u00eancia executa o comando que passamos no \u00c9 importante notar que, embora o Assim tendo o ambiente limpo novamente. "},{"location":"09/#executando-os-testes-no-postgresql","title":"Executando os testes no PostgreSQL","text":"Embora nosso Por conta disso, os testes t\u00eam sido executados no SQLite, mesmo com a presen\u00e7a do PostgreSQL no ambiente do Docker. No entanto, \u00e9 importante que os testes sejam executados no mesmo ambiente que o que rodar\u00e1 em produ\u00e7\u00e3o, para que n\u00e3o encontremos problemas relacionados a incompatibilidade de opera\u00e7\u00f5es no banco de dados. A altera\u00e7\u00e3o \u00e9 relativamente simples, temos que tornar a nossa fixture o mais pr\u00f3ximo poss\u00edvel do cliente da sess\u00e3o de produ\u00e7\u00e3o. Para fazer isso, precisamos alterar somente a chamada Com essa modifica\u00e7\u00e3o, agora estamos apontando para o banco de dados PostgreSQL, conforme definido nas configura\u00e7\u00f5es da nossa aplica\u00e7\u00e3o ( Agora, com a nova configura\u00e7\u00e3o, os testes utilizar\u00e3o o PostgreSQL, proporcionando um ambiente de testes mais fiel ao ambiente de produ\u00e7\u00e3o e, consequentemente, aumentando a confiabilidade dos testes executados: $ Execu\u00e7\u00e3o no terminal! Dessa forma temos um ambiente mais coeso e podemos reproduzir nossas configura\u00e7\u00f5es de forma bastante simples em qualquer ambiente. "},{"location":"09/#commit","title":"Commit","text":"Ap\u00f3s criar nosso arquivo
Dockerizar nossa aplica\u00e7\u00e3o FastAPI, junto com o PostgreSQL, nos permite garantir consist\u00eancia em diferentes ambientes. A combina\u00e7\u00e3o de Docker e Docker Compose simplifica o processo de desenvolvimento e implanta\u00e7\u00e3o. Na pr\u00f3xima aula, vamos aprender como levar nossa aplica\u00e7\u00e3o para o pr\u00f3ximo n\u00edvel executando os testes de forma remota com a integra\u00e7\u00e3o cont\u00ednua do GitHub Actions. "},{"location":"10/","title":"[WIP] Automatizando os testes com Integra\u00e7\u00e3o Cont\u00ednua","text":""},{"location":"10/#wip-automatizando-os-testes-com-integracao-continua","title":"[WIP] Automatizando os testes com Integra\u00e7\u00e3o Cont\u00ednua","text":"T\u00f3pico de manuten\u00e7\u00e3o: https://github.com/dunossauro/fastapi-do-zero/issues/34 Objetivos da aula:
Na aula passada, preparamos nossa aplica\u00e7\u00e3o para execu\u00e7\u00e3o em containers Docker. Nesta aula, focaremos em garantir que nossa aplica\u00e7\u00e3o continue funcionando conforme o esperado a cada atualiza\u00e7\u00e3o de c\u00f3digo. Para isso, introduziremos o conceito de Integra\u00e7\u00e3o Cont\u00ednua (CI). "},{"location":"10/#integracao-continua-ci","title":"Integra\u00e7\u00e3o Cont\u00ednua (CI)","text":"Integra\u00e7\u00e3o Cont\u00ednua (CI) \u00e9 uma pr\u00e1tica de desenvolvimento que envolve a integra\u00e7\u00e3o frequente de c\u00f3digo ao projeto principal. Com cada integra\u00e7\u00e3o - geralmente um commit - \u00e9 disparado um processo automatizado que constr\u00f3i e testa o c\u00f3digo. Isso permite detectar e corrigir problemas rapidamente, contribuindo para a manuten\u00e7\u00e3o da qualidade do software. "},{"location":"10/#github-actions","title":"GitHub Actions","text":"GitHub Actions \u00e9 um servi\u00e7o fornecido pelo GitHub que permite a automatiza\u00e7\u00e3o de workflows, incluindo a execu\u00e7\u00e3o de testes e implanta\u00e7\u00e3o de software, diretamente em seu reposit\u00f3rio GitHub. Cada tarefa \u00e9 definida como uma \"a\u00e7\u00e3o\", e a\u00e7\u00f5es podem ser combinadas para criar um \"workflow\" que atende a necessidades espec\u00edficas de desenvolvimento. "},{"location":"10/#configurando-o-workflow-de-ci","title":"Configurando o workflow de CI","text":"Vamos configurar um workflow de CI para nossa aplica\u00e7\u00e3o utilizando o GitHub Actions. Crie um novo arquivo em seu reposit\u00f3rio, sob o diret\u00f3rio Vamos analisar este arquivo:
Ap\u00f3s adicionar e configurar o arquivo do workflow, voc\u00ea deve commitar as mudan\u00e7as em seu reposit\u00f3rio. Siga os passos: $ Execu\u00e7\u00e3o no terminal! "},{"location":"10/#conclusao","title":"Conclus\u00e3o","text":"A Integra\u00e7\u00e3o Cont\u00ednua \u00e9 uma pr\u00e1tica fundamental no desenvolvimento moderno de software, e o GitHub Actions \u00e9 uma ferramenta poderosa para implementar essa pr\u00e1tica. Ele n\u00e3o apenas ajuda a manter a qualidade do c\u00f3digo ao garantir que todos os testes sejam executados a cada commit, mas tamb\u00e9m permite detectar e corrigir problemas mais cedo no ciclo de desenvolvimento. Al\u00e9m disso, monitorar a cobertura de testes com o Codecov nos permite manter um alto padr\u00e3o de qualidade, garantindo que todas as partes do nosso c\u00f3digo sejam testadas. Na pr\u00f3xima aula, vamos levar nossa aplica\u00e7\u00e3o ao pr\u00f3ximo n\u00edvel, preparando-a para o deployment em produ\u00e7\u00e3o! "},{"location":"11/","title":"[WIP] Fazendo deploy no Fly.io e configurando o PostgreSQL","text":""},{"location":"11/#wip-fazendo-deploy-no-flyio-e-configurando-o-postgresql","title":"[WIP] Fazendo deploy no Fly.io e configurando o PostgreSQL","text":"Objetivos da aula:
Na aula anterior, n\u00f3s automatizamos nossos testes e integramos tudo em um pipeline de integra\u00e7\u00e3o e deploy cont\u00ednuos. Agora, vamos aprender a fazer o deploy da nossa aplica\u00e7\u00e3o em um ambiente de produ\u00e7\u00e3o usando o Fly.io. O Fly.io \u00e9 uma plataforma de deploy que nos permite lan\u00e7ar nossas aplica\u00e7\u00f5es Docker na nuvem. Ele tamb\u00e9m fornece uma s\u00e9rie de recursos, como balanceamento de carga, cria\u00e7\u00e3o de inst\u00e2ncias de banco de dados e configura\u00e7\u00e3o de vari\u00e1veis de ambiente. Para iniciar, precisamos instalar a CLI do Fly.io, chamada Este comando ir\u00e1 abrir o navegador para voc\u00ea entrar com suas credenciais do Fly.io. "},{"location":"11/#criando-a-aplicacao-no-flyio-e-fazendo-o-deploy","title":"Criando a aplica\u00e7\u00e3o no Fly.io e fazendo o deploy","text":"Depois de logado, podemos criar uma nova aplica\u00e7\u00e3o no Fly.io usando o comando: $ Execu\u00e7\u00e3o no terminal! Este comando ir\u00e1 perguntar algumas informa\u00e7\u00f5es sobre sua aplica\u00e7\u00e3o e ent\u00e3o criar\u00e1 uma nova aplica\u00e7\u00e3o no Fly.io. Com nossa aplica\u00e7\u00e3o criada, podemos agora fazer o deploy da nossa imagem Docker usando o comando: $ Execu\u00e7\u00e3o no terminal! A op\u00e7\u00e3o Antes de avan\u00e7armos, \u00e9 importante mencionar uma especificidade do Fly.io: para criar uma inst\u00e2ncia do PostgreSQL, a plataforma requer que um cart\u00e3o de cr\u00e9dito seja fornecido. Esta \u00e9 uma medida de seguran\u00e7a adotada para evitar o uso indevido de seus servi\u00e7os, como a execu\u00e7\u00e3o de ferramentas de minera\u00e7\u00e3o. Apesar dessa exig\u00eancia, o servi\u00e7o de PostgreSQL \u00e9 oferecido de forma gratuita. Mais detalhes podem ser encontrados neste artigo. Agora, vamos criar uma inst\u00e2ncia do PostgreSQL. O Fly.io fornece um servi\u00e7o PostgreSQL que podemos usar para criar uma nova inst\u00e2ncia do PostgreSQL com apenas alguns comandos. "},{"location":"11/#configurando-as-variaveis-de-ambiente-e-rodando-as-migracoes-do-alembic","title":"Configurando as vari\u00e1veis de ambiente e rodando as migra\u00e7\u00f5es do Alembic","text":"Com a inst\u00e2ncia criada, algumas vari\u00e1veis de ambiente ser\u00e3o automaticamente definidas para n\u00f3s. Para que o Alembic possa executar as migra\u00e7\u00f5es, precisamos configurar a vari\u00e1vel Substitua Finalmente, podemos executar nossas migra\u00e7\u00f5es Alembic. Usaremos a CLI do Fly.io para executar o comando dentro de um cont\u00eainer do nosso aplicativo: $ Execu\u00e7\u00e3o no terminal! Substitua Agora que temos nosso aplicativo funcionando no Fly.io, podemos configurar o Github Actions para fazer o deploy autom\u00e1tico sempre que fizermos um push no nosso reposit\u00f3rio. Para isso, precisaremos adicionar alguns passos ao nosso arquivo de pipeline do Github Actions: Com isso, nossa aplica\u00e7\u00e3o est\u00e1 pronta para uso no Fly.io! "},{"location":"11/#conclusao","title":"Conclus\u00e3o","text":"Ao longo desta aula, n\u00f3s mergulhamos no mundo do deploy de aplica\u00e7\u00f5es com o Fly.io, uma plataforma que facilita imensamente a tarefa de colocar nossas aplica\u00e7\u00f5es para funcionar na nuvem. Al\u00e9m disso, tamb\u00e9m tivemos a chance de entender como gerenciar vari\u00e1veis de ambiente de forma segura e eficiente, permitindo a nossa aplica\u00e7\u00e3o se adaptar a diferentes contextos de execu\u00e7\u00e3o. Aprendemos como subir nossa imagem Docker no Fly.io e como este processo pode ser simplificado e automatizado. Tamb\u00e9m vimos como \u00e9 poss\u00edvel ter nosso banco de dados rodando no mesmo ambiente da nossa aplica\u00e7\u00e3o, facilitando a manuten\u00e7\u00e3o e a escalabilidade. Configuramos e utilizamos o PostgreSQL no Fly.io, o que nos deu uma vis\u00e3o pr\u00e1tica de como gerenciar bancos de dados em um ambiente de produ\u00e7\u00e3o. Ao fazer isso, pudemos integrar ainda mais a nossa aplica\u00e7\u00e3o ao ambiente em que ela est\u00e1 rodando. Al\u00e9m disso, exploramos a import\u00e2ncia das migra\u00e7\u00f5es e como elas podem ser gerenciadas usando o Alembic, que nos permitiu atualizar nosso banco de dados de forma controlada e rastre\u00e1vel. Finalmente, vimos como podemos automatizar todo o processo de deploy usando o Github Actions. Esta \u00e9 uma pr\u00e1tica extremamente \u00fatil e poderosa, pois permite que a nossa aplica\u00e7\u00e3o esteja sempre atualizada com as \u00faltimas altera\u00e7\u00f5es que fizemos, sem a necessidade de qualquer interven\u00e7\u00e3o manual. Com todas essas pe\u00e7as, temos agora uma aplica\u00e7\u00e3o robusta e pronta para escalar, com todos os elementos necess\u00e1rios para ser operada em um ambiente de produ\u00e7\u00e3o real. Estas s\u00e3o ferramentas e pr\u00e1ticas que est\u00e3o no cora\u00e7\u00e3o do desenvolvimento de software moderno, e domin\u00e1-las nos permitir\u00e1 construir aplica\u00e7\u00f5es cada vez mais complexas e eficientes. Na pr\u00f3xima aula, faremos uma recapitula\u00e7\u00e3o de tudo o que aprendemos neste curso e discutiremos os pr\u00f3ximos passos. Continue acompanhando para fortalecer ainda mais seus conhecimentos em desenvolvimento de aplica\u00e7\u00f5es com FastAPI, Docker, CI/CD e muito mais. At\u00e9 a pr\u00f3xima aula! "},{"location":"12/","title":"Despedida do curso","text":""},{"location":"12/#wip-despedida","title":"[WIP] Despedida","text":"Objetivos da aula:
Estamos chegando ao final de nossa jornada juntos neste curso. Durante esse tempo, tivemos a oportunidade de explorar uma s\u00e9rie de conceitos e tecnologias essenciais para o desenvolvimento de aplica\u00e7\u00f5es web modernas e escal\u00e1veis. \u00c9 importante lembrar que o que vimos aqui \u00e9 apenas a ponta do iceberg. Ainda h\u00e1 muitos aspectos e detalhes que n\u00e3o pudemos cobrir neste curso, como tratamento de logs, observabilidade, seguran\u00e7a avan\u00e7ada, otimiza\u00e7\u00f5es de desempenho, entre outros. Encorajo a todos que continuem explorando e aprendendo. "},{"location":"12/#revisao","title":"Revis\u00e3o","text":"Ao longo deste curso, cobrimos uma s\u00e9rie de t\u00f3picos essenciais para o desenvolvimento de aplica\u00e7\u00f5es web modernas e robustas:
J\u00e1 cobrimos alguns temas n\u00e3o citados neste curso usando FastAPI em Algumas Lives de Python. Voc\u00ea pode assistir para aprender mais tamb\u00e9m. "},{"location":"12/#templates-e-websockets","title":"Templates e WebSockets","text":"Na Live de Python #164 conversamos sobre websockets com Python e usamos FastAPI para exemplificar o comportamento. Durante essa live criamos uma aplica\u00e7\u00e3o de chat e usamos os templates com Jinja2 e Brython. "},{"location":"12/#graphql-strawberry","title":"GraphQL (Strawberry)","text":"Na Live de Python #185 conversamos sobre GraphQL um padr\u00e3o alternativo a REST APIs. Todos os exemplos foram aplicados usando Strawberry e FastAPI "},{"location":"12/#sqlmodel","title":"SQLModel","text":"Na Live de Python #235 conversamos sobre SQLModel um ORM alternativo ao SQLAlchemy que se integra com o Pydantic. O SQLModel tamb\u00e9m foi desenvolvido pelo Sebastian (criador do FastAPI). Caminhando ao final dessa aula, podemos ver a implementa\u00e7\u00e3o do SQLModel em uma aplica\u00e7\u00e3o b\u00e1sica com FastAPI. "},{"location":"12/#proximos-passos","title":"Pr\u00f3ximos passos","text":"[WIP] "},{"location":"12/#conclusao","title":"Conclus\u00e3o","text":"Todos esses conceitos e pr\u00e1ticas s\u00e3o componentes fundamentais no desenvolvimento de aplica\u00e7\u00f5es web modernas e escal\u00e1veis. Eles nos permitem criar aplica\u00e7\u00f5es robustas, confi\u00e1veis e eficientes, que podem ser facilmente mantidas e escaladas. Gostaria de agradecer a todos que acompanharam essa s\u00e9rie de aulas. Espero que tenham encontrado valor nas informa\u00e7\u00f5es e pr\u00e1ticas que compartilhamos aqui. Lembre-se, a jornada do aprendizado \u00e9 cont\u00ednua e cada passo conta. Continue explorando, aprendendo e crescendo. At\u00e9 a pr\u00f3xima! "}]} \ No newline at end of file +{"config":{"lang":["pt"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"FastAPI do Zero!","text":""},{"location":"#fastapi-do-zero","title":"FastAPI do ZERO","text":"Esse material ainda est\u00e1 em fase de desenvolvimento. Caso encontre algum erro, ficarei extremamente feliz que voc\u00ea me notifique ou envie um Pull Request! Problemas j\u00e1 conhecidos Construindo um Projeto com Bancos de Dados, Testes e Deploy Boas-vindas \u00e0 sua jornada de aprendizado com o framework FastAPI! Neste curso, o foco \u00e9 proporcionar um entendimento pr\u00e1tico das habilidades essenciais para o desenvolvimento eficiente de APIs. Exploraremos temas como integra\u00e7\u00e3o com bancos de dados e implementa\u00e7\u00e3o de testes, oferecendo uma base s\u00f3lida para quem busca trabalhar com essa ferramenta. A abordagem \u00e9 direta e informativa, visando nos equipar com o conhecimento necess\u00e1rio para come\u00e7ar a criar nossos pr\u00f3prios projetos. "},{"location":"#o-que-e-fastapi","title":"O que \u00e9 FastAPI?","text":"FastAPI \u00e9 um framework Python moderno, projetado para simplicidade, velocidade e efici\u00eancia. A combina\u00e7\u00e3o de alto desempenho com anota\u00e7\u00f5es de tipo Python facilita o desenvolvimento de APIs RESTful. "},{"location":"#sobre-o-curso","title":"Sobre o curso","text":"Este curso foi desenvolvido para oferecer uma experi\u00eancia pr\u00e1tica no uso do FastAPI, uma das ferramentas mais modernas para constru\u00e7\u00e3o de APIs. Ao longo do curso, o objetivo \u00e9 que voc\u00ea obtenha uma compreens\u00e3o das funcionalidades do FastAPI e de boas pr\u00e1ticas associadas a ele. O projeto central do curso ser\u00e1 a constru\u00e7\u00e3o de um gerenciador de tarefas (uma lista de tarefas), come\u00e7ando do zero. Esse projeto incluir\u00e1 a implementa\u00e7\u00e3o da autentica\u00e7\u00e3o do usu\u00e1rio e das opera\u00e7\u00f5es CRUD completas. Para a constru\u00e7\u00e3o do projeto, ser\u00e3o utilizadas as vers\u00f5es mais recentes das ferramentas, dispon\u00edveis em 2023, como a vers\u00e3o 0.100 do FastAPI, a vers\u00e3o 2.0 do Pydantic, a vers\u00e3o 2.0 do SQLAlchemy ORM, al\u00e9m do Python 3.11 e do Alembic para gerenciamento de migra\u00e7\u00f5es. Al\u00e9m da constru\u00e7\u00e3o do projeto, o curso tamb\u00e9m incluir\u00e1 a pr\u00e1tica de testes, utilizando o pytest. Essa abordagem tem como objetivo garantir que as APIs desenvolvidas sejam n\u00e3o apenas funcionais, mas tamb\u00e9m robustas e confi\u00e1veis. "},{"location":"#o-que-voce-vai-aprender","title":"O que voc\u00ea vai aprender?","text":"Aqui est\u00e1 uma vis\u00e3o geral dos t\u00f3picos que vamos abordar neste curso:
SIM! Esse curso foi todo desenvolvido de forma aberta e com a ajuda financeira de pessoas incr\u00edveis. Caso voc\u00ea sinta vontade de contribuir, voc\u00ea pode me pagar um caf\u00e9 por pix (pix.dunossauro@gmail.com) ou apoiar a campanha recorrente de financiamento coletivo da live de python que \u00e9 o que paga as contas aqui de casa. "},{"location":"#onde-o-curso-sera-disponibilizado","title":"Onde o curso ser\u00e1 disponibilizado?","text":"Esse material est\u00e1 em fase de desenvolvimento e todas as aulas estar\u00e3o dispon\u00edveis no meu canal do YouTube. Voc\u00ea pode conferir outros materiais dispon\u00edveis por l\u00e1 enquanto os v\u00eddeos n\u00e3o saem, ou se inscrever para ser notificado quando os v\u00eddeos sa\u00edrem! http://youtube.com/@dunossauro Aqui estar\u00e1 listada a playlist quando dispon\u00edvel! "},{"location":"#pre-requisitos","title":"Pr\u00e9-requisitos","text":"Para aproveitar ao m\u00e1ximo este curso, \u00e9 recomendado que voc\u00ea tenha algum conhecimento pr\u00e9vio de Python. Al\u00e9m disso, algum entendimento b\u00e1sico de desenvolvimento web e APIs RESTful ser\u00e1 \u00fatil, mas n\u00e3o essencial, pois a abordagem deste curso \u00e9 pr\u00e1tica e centrada em um projeto concreto. Atrav\u00e9s de exemplos reais e instru\u00e7\u00f5es passo a passo, voc\u00ea ter\u00e1 a oportunidade de acompanhar o processo de constru\u00e7\u00e3o de uma aplica\u00e7\u00e3o real. Mesmo que os conceitos de desenvolvimento web sejam novos para voc\u00ea, a \u00eanfase na aplica\u00e7\u00e3o pr\u00e1tica e a estrutura detalhada do curso facilitar\u00e3o o entendimento e a aplica\u00e7\u00e3o dessas habilidades at\u00e9 o fim do processo. Caso esteja iniciando seus estudos em Python!Caso voc\u00ea ainda n\u00e3o se sinta uma pessoa preparada, ou caiu aqui sem saber exatamente o que esperar. Temos um pequeno curso introdut\u00f3rio. Destinado aos primeiros passos com python. Link direto Tamb\u00e9m temos uma live focada em dicas para iniciar os estudos em python Link direto Ou ent\u00e3o a leitura do livro Pense em python "},{"location":"#aulas","title":"Aulas","text":"
Prazer! Eu me chamo Eduardo. Mas as pessoas me conhecem na internet como @dunossauro. Eu sou um programador Python muito empolgado e curioso. Toco um projeto pessoal chamado Live de Python h\u00e1 pouco mais de 6 anos. Onde conversamos sobre tudo e mais um pouco quando o assunto \u00e9 Python. Esse projeto que estamos desenvolvendo \u00e9 um peda\u00e7o, um projeto, de um grande curso de FastAPI que estou montando. Espero que voc\u00ea se divirta ao m\u00e1ximo com a parte pr\u00e1tica enquanto escrevo em mais detalhes todo o potencial te\u00f3rico que lan\u00e7arei no futuro! Caso queira saber mais sobre esse projeto completo. "},{"location":"#licenca","title":"\ud83d\udcd6 Licen\u00e7a","text":"Todo esse curso foi escrito e produzido por Eduardo Mendes (@dunossauro). Todo esse material \u00e9 gratuito e est\u00e1 sob licen\u00e7a Creative Commons BY-NC-SA. O que quer dizer que:
Pontos de aten\u00e7\u00e3o:
Toda essa p\u00e1gina foi feita usando as seguintes bibliotecas:
Para os slides:
O versionamento de tudo est\u00e1 sendo feito no reposit\u00f3rio do curso Github "},{"location":"#deploy","title":"\ud83d\ude80 Deploy","text":"Os deploys das p\u00e1ginas est\u00e1ticas geradas pelo MkDocs est\u00e3o sendo feitos no Netlify "},{"location":"#conclusao","title":"Conclus\u00e3o","text":"Neste curso, a inten\u00e7\u00e3o \u00e9 fornecer uma compreens\u00e3o completa do framework FastAPI, utilizando-o para construir uma aplica\u00e7\u00e3o de gerenciamento de tarefas. O aprendizado ser\u00e1 focado na pr\u00e1tica, e cada conceito ser\u00e1 acompanhado por exemplos e exerc\u00edcios relevantes. A jornada come\u00e7ar\u00e1 com a configura\u00e7\u00e3o do ambiente de desenvolvimento e introdu\u00e7\u00e3o ao FastAPI. Ao longo das aulas, abordaremos t\u00f3picos como autentica\u00e7\u00e3o, opera\u00e7\u00f5es CRUD, testes com pytest e deploy. A \u00eanfase ser\u00e1 colocada na aplica\u00e7\u00e3o de boas pr\u00e1ticas e no entendimento das ferramentas e tecnologias atualizadas, incluindo as vers\u00f5es mais recentes do FastAPI, Pydantic, SQLAlchemy ORM, Python e Alembic. Este conte\u00fado foi pensado para auxiliar na compreens\u00e3o de como criar uma API eficiente e confi\u00e1vel, dando aten\u00e7\u00e3o a aspectos importantes como testes e integra\u00e7\u00e3o com banco de dados. "},{"location":"#faq","title":"F.A.Q.","text":"Perguntas frequentes que me fizeram durante os v\u00eddeos
Objetivos dessa aula:
Aula Slides C\u00f3digo Nesta aula pr\u00e1tica, vamos come\u00e7ar nossa jornada na constru\u00e7\u00e3o de uma API com FastAPI. Esse \u00e9 um moderno e r\u00e1pido (altamente perform\u00e1tico) framework web para constru\u00e7\u00e3o de APIs com Python 3.7+ baseado em Python type hints. Partiremos do b\u00e1sico, configurando nosso ambiente de desenvolvimento. Discutiremos desde a escolha e instala\u00e7\u00e3o da vers\u00e3o correta do Python at\u00e9 a instala\u00e7\u00e3o e configura\u00e7\u00e3o do Poetry, um gerenciador de pacotes e depend\u00eancias para Python. Al\u00e9m disso, instalaremos e configuraremos uma s\u00e9rie de ferramentas de desenvolvimento \u00fateis, como Ruff, Blue, Isort, pytest e Taskipy. Depois de configurado o nosso ambiente, criaremos nosso primeiro programa \"Hello, World!\" com FastAPI. Isso nos permitir\u00e1 confirmar que tudo est\u00e1 funcionando corretamente. E, finalmente, exploraremos uma parte crucial do Desenvolvimento Orientado por Testes (TDD), escrevendo nosso primeiro teste com Pytest. "},{"location":"01/#ambiente-de-desenvolvimento","title":"Ambiente de Desenvolvimento","text":"Para iniciar essa aula voc\u00ea vai precisar de algumas ferramentas.
Se voc\u00ea precisar reconstruir o ambiente usado nesse curso, \u00e9 recomendado que voc\u00ea use o pyenv. Caso tenha problemas durante a instala\u00e7\u00e3o. O pyenv conta com dois assistentes simplificados para sua configura\u00e7\u00e3o. Para windows, use o pyenv-windows. Para GNU/Linux e MacOS, use o pyenv-installer. Navegue at\u00e9 o diret\u00f3rio onde far\u00e1 os exerc\u00edcios e executar\u00e1 os c\u00f3digos de exemplo no seu terminal e digite os seguintes comandos: $ Execu\u00e7\u00e3o no terminal! Certifique que a vers\u00e3o do python 3.11 esteja instalada: $ Execu\u00e7\u00e3o no terminal! A resposta esperada \u00e9 que o Ap\u00f3s instalar o Python, o pr\u00f3ximo passo \u00e9 instalar o Poetry, um gerenciador de pacotes e depend\u00eancias para Python. O Poetry facilita a cria\u00e7\u00e3o, o gerenciamento e a distribui\u00e7\u00e3o de pacotes Python. Caso esse seja seu primeiro contato com o PoetryTemos uma live de python explicando somente ele Link direto Para instalar o Poetry, voc\u00ea pode seguir as instru\u00e7\u00f5es presentes na documenta\u00e7\u00e3o oficial do Poetry para o seu sistema operacional. Alternativamente, se voc\u00ea optou por usar o pipx, pode instalar o Poetry com o seguinte comando: $ Execu\u00e7\u00e3o no terminal! "},{"location":"01/#criacao-do-projeto-fastapi-e-instalacao-das-dependencias","title":"Cria\u00e7\u00e3o do Projeto FastAPI e Instala\u00e7\u00e3o das Depend\u00eancias","text":"Agora que temos o Python e o Poetry prontos, podemos come\u00e7ar a criar nosso projeto FastAPI. Vamos inicialmente criar um novo diret\u00f3rio para nosso projeto e navegar para ele: $ Execu\u00e7\u00e3o no terminal! Ele criar\u00e1 uma estrutura como essa: Para que a vers\u00e3o que instalamos com pyenv seja usada em nosso projeto criado com poetry, devemos dizer ao pyenv qual vers\u00e3o do python ser\u00e1 usada nesse diret\u00f3rio: $ Execu\u00e7\u00e3o no terminal! Em conjunto com essa instru\u00e7\u00e3o, devemos dizer ao poetry que usaremos essa vers\u00e3o em nosso projeto. Para isso vamos alterar o arquivo de configura\u00e7\u00e3o do projeto o Desta forma, temos uma vers\u00e3o do python selecionada para esse projeto e uma garantia que o poetry usar\u00e1 essa vers\u00e3o para a cria\u00e7\u00e3o do nosso ambiente virtual. Em seguida, inicializaremos um novo projeto Python com Poetry e instalaremos as depend\u00eancias necess\u00e1rias - FastAPI e Uvicorn: $ Execu\u00e7\u00e3o no terminal! "},{"location":"01/#primeira-execucao-de-um-hello-world","title":"Primeira Execu\u00e7\u00e3o de um \"Hello, World!\"","text":"Para garantir que tudo est\u00e1 configurado corretamente, vamos criar um pequeno programa \"Hello, World!\" com FastAPI. Em um novo arquivo chamado Agora, podemos iniciar nosso servidor FastAPI com o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Acesse http://localhost:8000 no seu navegador, e voc\u00ea deve ver a mensagem \"Hello, World!\" em formato JSON. "},{"location":"01/#instalando-as-ferramentas-de-desenvolvimento","title":"Instalando as ferramentas de desenvolvimento","text":"As ferramentas de desenvolvimento escolhidas podem variar de acordo com a prefer\u00eancia pessoal. Nesta aula, utilizaremos algumas que s\u00e3o particularmente \u00fateis para demonstrar certos conceitos:
Para instalar as depend\u00eancias, podemos usar um grupo do poetry focado nelas, para n\u00e3o serem usadas em produ\u00e7\u00e3o: $ Execu\u00e7\u00e3o no terminal! O HTTPX foi inclu\u00eddo, pois ele \u00e9 uma depend\u00eancia do cliente de testes do FastAPI. "},{"location":"01/#configurando-as-ferramentas-de-desenvolvimento","title":"Configurando as ferramentas de desenvolvimento","text":"Ap\u00f3s a instala\u00e7\u00e3o das depend\u00eancias, vamos precisar configurar todas as ferramentas de desenvolvimento no arquivo Come\u00e7ando pelo ruff, vamos definir o comprimento de linha para 79 caracteres (conforme sugerido na PEP 8) e em seguida, informaremos que o diret\u00f3rio de ambiente virtual e o de migra\u00e7\u00f5es de banco de dados dever\u00e3o ser ignorados: pyproject.toml "},{"location":"01/#isort","title":"isort","text":"Para evitar conflitos de formata\u00e7\u00e3o entre o isort e o blue, definiremos o black como perfil de formata\u00e7\u00e3o a ser seguido, j\u00e1 que o blue \u00e9 um fork dele. Como o black utiliza 88 caracteres por linha, vamos alterar para 79 que \u00e9 o padr\u00e3o que o blue segue e que tamb\u00e9m estamos seguindo: pyproject.toml "},{"location":"01/#pytest","title":"pytest","text":"Configuraremos o pytest para reconhecer o caminho base para execu\u00e7\u00e3o dos testes na raiz do projeto "},{"location":"01/#blue","title":"blue","text":"Configuraremos o blue para excluir o caminho das migra\u00e7\u00f5es quando essas forem utilizadas: pyproject.toml "},{"location":"01/#taskipy","title":"Taskipy","text":"Para simplificar a execu\u00e7\u00e3o de certos comandos, vamos criar algumas tarefas com o Taskipy. pyproject.toml Os comandos definidos fazem o seguinte:
Para executar um comando, \u00e9 bem mais simples, precisando somente passar a palavra O meu est\u00e1 exatamente assim: pyproject.toml "},{"location":"01/#os-efeitos-dessas-configuracoes-de-desenvolvimento","title":"Os efeitos dessas configura\u00e7\u00f5es de desenvolvimento","text":"Caso voc\u00ea tenha copiado o c\u00f3digo que usamos para definir Dessa forma, veremos que cometemos algumas infra\u00e7\u00f5es na formata\u00e7\u00e3o da PEP-8. O blue nos informar\u00e1 que dever\u00edamos ter adicionado duas linhas antes de uma defini\u00e7\u00e3o de fun\u00e7\u00e3o: Para corrigir isso, podemos usar o nosso comando de formata\u00e7\u00e3o de c\u00f3digo: ComandoResultado $ Execu\u00e7\u00e3o no terminal! fast_zero/app.py "},{"location":"01/#introducao-ao-pytest-testando-o-hello-world","title":"Introdu\u00e7\u00e3o ao Pytest: Testando o \"Hello, World!\"","text":"Antes de entendermos a din\u00e2mica dos testes, precisamos entender o efeito que eles t\u00eam no nosso c\u00f3digo. Um bom lugar para come\u00e7ar isso \u00e9 analisando a cobertura. Vamos executar os testes. $ Execu\u00e7\u00e3o no terminal! Teremos uma resposta como essa: $ Execu\u00e7\u00e3o no terminal! As primeiras duas linhas s\u00e3o referentes ao comando do Temos uma live de Python explicando os conceitos b\u00e1sicos da biblioteca Link direto A parte importante dessa Mensagem est\u00e1 na tabela gerada pelo Por n\u00e3o ter encontrado nenhum teste, o pytest retornou um \"erro\". Isso significa que nossa tarefa Isso gera um relat\u00f3rio de cobertura de testes em formato HTML. Podemos abrir esse arquivo em nosso navegador e entender exatamente quais linhas do c\u00f3digo n\u00e3o est\u00e3o sendo testadas. Se clicarmos no arquivo Isto significa que precisamos testar todo esse arquivo. "},{"location":"01/#escrevendo-o-teste","title":"Escrevendo o teste","text":"Agora, vamos escrever nosso primeiro teste com Pytest. Para testar o FastAPI, precisamos de um cliente de teste. Isso pode ser obtido no m\u00f3dulo S\u00f3 o fato de termos definido um cliente, j\u00e1 nos mostra uma cobertura bastante diferente: $ Execu\u00e7\u00e3o no terminal! Devido ao fato de n\u00e3o ter coletado nenhum teste, o pytest ainda retornou um \"erro\". Para ver a cobertura, precisaremos executar novamente o No navegador, podemos ver que a \u00fanica linha n\u00e3o \"testada\" \u00e9 aquela onde temos a l\u00f3gica do endpoint: No verde vemos o que foi executado quando chamamos o teste, no vermelho o que n\u00e3o foi. Para resolver isso, temos que criar um teste de fato, fazendo uma chamada para nossa API usando o cliente de teste que definimos: tests/test_app.py Esse teste faz uma requisi\u00e7\u00e3o GET no endpoint Dessa forma, temos um teste que coletou 1 item (1 teste). Esse teste foi aprovado e a cobertura n\u00e3o deixou de abranger nenhuma linha de c\u00f3digo. Como conseguimos coletar um item, o Agora que escrevemos nosso teste de forma intuitiva, podemos entender o que cada passo do teste faz. Essa compreens\u00e3o \u00e9 vital, pois pode nos ajudar a escrever testes no futuro com mais confian\u00e7a e efic\u00e1cia. Para desvendar o m\u00e9todo por tr\u00e1s da nossa abordagem, vamos explorar a estrat\u00e9gia conhecida como AAA, que divide o teste em tr\u00eas fases distintas: Arrange, Act, Assert. Caso fazer testes ainda seja complicado para voc\u00eaTemos uma live de python focada em ensinar os primeiros passos no mundo dos testes. Link direto Vamos pegar esse teste que fizemos e entender os passos que fizemos para conseguir testar esse endpoint: tests/test_app.py Com base nesse c\u00f3digo, podemos observar as tr\u00eas fases: "},{"location":"01/#fase-1-organizar-arrange","title":"Fase 1 - Organizar (Arrange)","text":"Nesta primeira etapa, estamos preparando o ambiente para o teste. No exemplo, a linha com o coment\u00e1rio Aqui \u00e9 a etapa onde acontece a a\u00e7\u00e3o principal do teste, que consiste em chamar o Sistema Sob Teste (SUT). No nosso caso, o SUT \u00e9 a rota Esta \u00e9 a etapa de verificar se tudo correu como esperado. \u00c9 f\u00e1cil notar onde estamos fazendo a verifica\u00e7\u00e3o, pois essa linha sempre tem a palavra reservada Agora que compreendemos o que cada linha de teste faz em espec\u00edfico, podemos nos orientar de forma clara nos testes que escreveremos no futuro. Cada uma das linhas usadas tem uma raz\u00e3o de estar no teste, e conhecer essa estrutura n\u00e3o s\u00f3 nos d\u00e1 uma compreens\u00e3o mais profunda do que estamos fazendo, mas tamb\u00e9m nos d\u00e1 confian\u00e7a para explorar e escrever testes mais complexos. "},{"location":"01/#criando-nosso-repositorio-no-git","title":"Criando nosso reposit\u00f3rio no git","text":"Antes de concluirmos a aula, precisamos criar nosso reposit\u00f3rio no git e criar nosso arquivo "},{"location":"01/#conclusao","title":"Conclus\u00e3o","text":"Pronto! Agora temos um ambiente de desenvolvimento totalmente configurado para come\u00e7ar a trabalhar com FastAPI e j\u00e1 fizemos nossa primeira imers\u00e3o no Desenvolvimento Orientado por Testes. Na pr\u00f3xima aula, vamos aprofundar na estrutura\u00e7\u00e3o da nossa aplica\u00e7\u00e3o FastAPI. At\u00e9 l\u00e1! "},{"location":"02/","title":"Estruturando o Projeto e Criando Rotas CRUD","text":""},{"location":"02/#estruturando-o-projeto-e-criando-rotas-crud","title":"Estruturando o Projeto e Criando Rotas CRUD","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Boas-vindas de volta \u00e0 nossa s\u00e9rie de cursos \"FastAPI do Zero: Criando um Projeto com Bancos de Dados, Testes e Deploy\". Hoje, na Aula 3, avan\u00e7aremos na estrutura\u00e7\u00e3o do nosso projeto FastAPI e implementar todas as rotas CRUD (Criar, Ler, Atualizar e Deletar) para o nosso recurso de usu\u00e1rio. "},{"location":"02/#o-que-e-uma-api","title":"O que \u00e9 uma API?","text":"Acr\u00f4nimo de Application Programming Interface (Interface de Programa\u00e7\u00e3o de Aplica\u00e7\u00f5es), uma API \u00e9 um conjunto de regras e protocolos que permitem a comunica\u00e7\u00e3o entre diferentes softwares. As APIs servem como uma ponte entre diferentes programas, permitindo que eles se comuniquem e compartilhem informa\u00e7\u00f5es de maneira eficiente e segura. No mundo moderno, as APIs geralmente comunicam usando o formato de dados JSON (JavaScript Object Notation), que \u00e9 uma maneira leve e eficiente de transmitir dados entre a API e o cliente. As APIs s\u00e3o fundamentais no mundo da programa\u00e7\u00e3o moderna, ao permitirem a intera\u00e7\u00e3o entre diferentes sistemas, independentemente de como foram projetados ou em que linguagem foram escritos. "},{"location":"02/#o-que-e-http","title":"O que \u00e9 HTTP?","text":"HTTP, ou Hypertext Transfer Protocol (Protocolo de Transfer\u00eancia de Hipertexto), \u00e9 o protocolo fundamental na web para a transfer\u00eancia de dados e comunica\u00e7\u00e3o entre clientes e servidores. No contexto das APIs, o HTTP \u00e9 o protocolo que permite a comunica\u00e7\u00e3o entre o cliente (geralmente o navegador de um usu\u00e1rio, mas pode ser qualquer coisa que saiba como fazer solicita\u00e7\u00f5es HTTP) e o servidor onde a API est\u00e1 hospedada. As informa\u00e7\u00f5es entre o cliente e o servidor s\u00e3o trocadas na forma de JSON, tornando-se uma linguagem universal para a troca de informa\u00e7\u00f5es na web. O HTTP \u00e9 baseado no modelo de requisi\u00e7\u00e3o-resposta: o cliente faz uma requisi\u00e7\u00e3o para o servidor, e o servidor responde a essa requisi\u00e7\u00e3o. Essas requisi\u00e7\u00f5es e respostas s\u00e3o formatadas de acordo com as regras do protocolo HTTP. A seguir, vamos explorar como os verbos HTTP, os c\u00f3digos de resposta e os c\u00f3digos de erro s\u00e3o utilizados para gerenciar a comunica\u00e7\u00e3o entre o cliente e a API. "},{"location":"02/#compreendendo-os-verbos-http-codigos-de-resposta-e-codigos-de-erro","title":"Compreendendo os Verbos HTTP, C\u00f3digos de Resposta e C\u00f3digos de Erro","text":"Quando trabalhamos com APIs REST, o uso apropriado dos verbos HTTP, c\u00f3digos de resposta e c\u00f3digos de erro \u00e9 crucial para criar uma API clara e consistente. "},{"location":"02/#verbos-http","title":"Verbos HTTP","text":"Os verbos HTTP indicam a a\u00e7\u00e3o desejada a ser executada em um determinado recurso. Os verbos mais comuns s\u00e3o:
Os c\u00f3digos de resposta HTTP informam ao cliente sobre o resultado de sua solicita\u00e7\u00e3o. Aqui est\u00e3o alguns dos c\u00f3digos de resposta mais comuns:
Os c\u00f3digos de erro HTTP indicam que houve um problema com a solicita\u00e7\u00e3o. Alguns c\u00f3digos de erro comuns incluem:
Ao trabalhar com APIs REST, \u00e9 importante lidar corretamente com esses c\u00f3digos de resposta e erro para proporcionar uma boa experi\u00eancia para os usu\u00e1rios da API. "},{"location":"02/#como-acontece-a-comunicacao-web-entre-cliente-e-servidor","title":"Como acontece a comunica\u00e7\u00e3o web entre cliente e servidor","text":"A comunica\u00e7\u00e3o entre cliente e servidor na web \u00e9 um processo que ocorre em v\u00e1rias etapas e \u00e9 governado por protocolos de comunica\u00e7\u00e3o espec\u00edficos. O protocolo mais comum \u00e9 o HTTP (Hypertext Transfer Protocol). Essa forma de comunica\u00e7\u00e3o \u00e9 geralmente descrita como stateless, o que significa que cada requisi\u00e7\u00e3o \u00e9 processada de forma independente, sem qualquer conhecimento das requisi\u00e7\u00f5es anteriores. A informa\u00e7\u00e3o \u00e9 trocada na forma de mensagens HTTP, que cont\u00eam dados e informa\u00e7\u00f5es sobre como esses dados devem ser processados. Um aspecto fundamental dessa comunica\u00e7\u00e3o \u00e9 a troca de dados na forma de objetos JSON, que s\u00e3o uma maneira eficiente e flex\u00edvel de representar dados estruturados. Este diagrama representa a sequ\u00eancia b\u00e1sica de uma comunica\u00e7\u00e3o cliente-servidor usando HTTP e JSON:
Essa \u00e9 uma vis\u00e3o geral simplificada do processo. Na pr\u00e1tica, a comunica\u00e7\u00e3o entre cliente e servidor pode envolver muitas outras nuances, como autentica\u00e7\u00e3o, redirecionamento, cookies e muito mais. "},{"location":"02/#pydantic-e-a-validacao-de-dados","title":"Pydantic e a valida\u00e7\u00e3o de dados","text":"Antes de mergulharmos no c\u00f3digo, vamos entender alguns conceitos importantes. Caso esse seja seu primeiro contato com PydanticTemos uma live de python exclusiva sobre esse assunto Link direto O Pydantic \u00e9 uma biblioteca Python que oferece valida\u00e7\u00e3o de dados e configura\u00e7\u00f5es usando anota\u00e7\u00f5es de tipos Python. Ela \u00e9 utilizada extensivamente com o FastAPI para lidar com a valida\u00e7\u00e3o e serializa\u00e7\u00e3o/desserializa\u00e7\u00e3o de dados. O Pydantic tem um papel crucial ao trabalhar com JSON, pois permite a valida\u00e7\u00e3o dos dados recebidos neste formato, assim como sua convers\u00e3o para formatos nativos do Python e vice-versa. O uso do Pydantic nos permite definir modelos de dados, ou \"esquemas\", com campos anotados com tipos de dados. O Pydantic garante que as inst\u00e2ncias desses modelos sempre estejam em conformidade com o esquema definido. Esquemas: No contexto da programa\u00e7\u00e3o, um esquema \u00e9 uma representa\u00e7\u00e3o estrutural de um objeto ou entidade. Por exemplo, no nosso caso, um usu\u00e1rio pode ser representado por um esquema que cont\u00e9m campos para nome de usu\u00e1rio, e-mail e senha. Esquemas s\u00e3o \u00fateis porque permitem definir a estrutura de um objeto de uma maneira clara e reutiliz\u00e1vel. Valida\u00e7\u00e3o de dados: Este \u00e9 o processo de verificar se os dados recebidos est\u00e3o em conformidade com as regras e restri\u00e7\u00f5es definidas. Por exemplo, se esperamos que o campo \"email\" contenha um endere\u00e7o de e-mail v\u00e1lido, a valida\u00e7\u00e3o de dados garantir\u00e1 que os dados inseridos nesse campo de fato correspondam a um formato de e-mail v\u00e1lido. Vamos considerar um exemplo onde recebemos o seguinte objeto JSON, representando um novo usu\u00e1rio que quer se registrar em nosso servi\u00e7o: Para lidar com esta entrada de dados, devemos definir um esquema Pydantic que corresponda \u00e0 estrutura deste objeto JSON. Usamos anota\u00e7\u00f5es de tipos Python para definir o tipo de dado de cada campo: Neste exemplo, o campo Ao usar este esquema, qualquer tentativa de criar um usu\u00e1rio com dados que n\u00e3o correspondam a este formato (por exemplo, um email que n\u00e3o \u00e9 v\u00e1lido, ou um campo de nome de usu\u00e1rio que n\u00e3o \u00e9 uma string) resultar\u00e1 em um erro de valida\u00e7\u00e3o. "},{"location":"02/#suporte-a-emails","title":"Suporte a emails","text":"Para que o Pydantic suporte a valida\u00e7\u00e3o de emails, \u00e9 necess\u00e1rio instalar o Ademais, se tentarmos criar um usu\u00e1rio com um email inv\u00e1lido, o Pydantic ir\u00e1 automaticamente validar o campo e retornar um erro \u00fatil. Isso nos poupa muito trabalho de valida\u00e7\u00e3o manual e ajuda a manter nossa API robusta e confi\u00e1vel. "},{"location":"02/#implementando-as-rotas-crud","title":"Implementando as Rotas CRUD","text":"CRUD \u00e9 um acr\u00f4nimo que representa as quatro opera\u00e7\u00f5es b\u00e1sicas que voc\u00ea pode realizar em qualquer banco de dados persistente:
Os c\u00f3digos de status HTTP s\u00e3o usados para indicar o resultado de cada opera\u00e7\u00e3o CRUD. Por exemplo, uma solicita\u00e7\u00e3o POST bem-sucedida (create) retorna o status HTTP 201 (Criado), enquanto uma solicita\u00e7\u00e3o GET bem-sucedida (read) retorna o status HTTP 200 (OK). \u00c9 importante notar que, ao trabalhar com FastAPI e Pydantic, nossos esquemas desempenham um papel vital na opera\u00e7\u00e3o de \"Create\" (criar). Ao usar a opera\u00e7\u00e3o POST para adicionar um novo registro ao nosso banco de dados, vamos aproveitar a valida\u00e7\u00e3o de dados do Pydantic para garantir que o novo registro esteja em conformidade com o esquema do nosso modelo de dados. Se os dados enviados na solicita\u00e7\u00e3o POST n\u00e3o passarem na valida\u00e7\u00e3o do Pydantic, nossa API retornar\u00e1 um c\u00f3digo de status HTTP 422 (Unprocessable Entity), indicando que os dados fornecidos s\u00e3o inv\u00e1lidos ou incompletos. Agora que temos uma compreens\u00e3o clara do que \u00e9 o CRUD, como se relaciona com os verbos HTTP, os c\u00f3digos de status e a valida\u00e7\u00e3o do Pydantic, podemos passar para a implementa\u00e7\u00e3o dessas opera\u00e7\u00f5es em nossa API FastAPI. Na nossa API, vamos criar rotas correspondentes para cada opera\u00e7\u00e3o CRUD, come\u00e7ando com a opera\u00e7\u00e3o \"create\" (criar), que ser\u00e1 implementada pela rota POST. "},{"location":"02/#implementando-a-rota-post","title":"Implementando a Rota POST","text":"A rota POST \u00e9 usada para criar um novo usu\u00e1rio em nosso sistema. Lembrando, o verbo HTTP POST est\u00e1 relacionado \u00e0 opera\u00e7\u00e3o \"Create\" do CRUD. Se tudo ocorrer como esperado e um novo usu\u00e1rio for criado com sucesso, a rota deve retornar o status HTTP 201 (Criado). Para a cria\u00e7\u00e3o dessa rota, vamos usar de base o JSON que criamos anteriormente. Para que a pessoa se cadastre na nossa plataforma, ela precisa enviar os dados de nome de usu\u00e1rio, email e senha: Para isso, vamos criar um esquema Pydantic equivalente em um arquivo de esquemas: Agora vamos criar nosso endpoint que esperar\u00e1 receber esse esquema Pydantic e retornar\u00e1 201, caso o JSON enviado seja v\u00e1lido: fast_zero/app.py Com esse endpoint criado, podemos executar a nossa aplica\u00e7\u00e3o: $ Execu\u00e7\u00e3o no terminal! E acessar a p\u00e1gina http://localhost:8000/docs. Isso nos mostrar\u00e1 as defini\u00e7\u00f5es do nosso endpoint usando o Swagger. Dessa forma, podemos testar de forma simplificada a nossa API, enviando o JSON e realizando alguns testes. Entretanto, precisamos prestar aten\u00e7\u00e3o a um detalhe: nosso modelo retorna a senha do usu\u00e1rio, o que \u00e9 uma p\u00e9ssima pr\u00e1tica de seguran\u00e7a. Para evitar isso, podemos criar um novo modelo que ser\u00e1 usado somente para resposta. Dessa forma, n\u00e3o expomos os dados que n\u00e3o queremos na API: fast_zero/schemas.py Precisamos tamb\u00e9m dizer ao FastAPI que esse ser\u00e1 o modelo de resposta, e converter nosso Note que somente adicionando o Agora, se fizermos de novo a chamada no Swagger, receberemos o mesmo objeto, mas sem expor a senha. Caso nunca tenha usado o SwaggerTemos uma live focada em OpenAPI, que s\u00e3o as especifica\u00e7\u00f5es do Swagger Link direto "},{"location":"02/#criando-um-banco-de-dados-falso","title":"Criando um banco de dados falso","text":"Finalmente, para brincar com essas rotas, podemos criar uma lista provis\u00f3ria para simular um banco de dados. Assim, podemos adicionar nossos dados e entender como o FastAPI funciona. Para isso, adicionamos uma lista provis\u00f3ria para o \"banco\" e alteramos nosso endpoint para inserir nossos modelos do Pydantic nessa lista: fast_zero/app.py Se queremos uma simula\u00e7\u00e3o de banco de dados, precisamos ter um Dessa forma, nada muda. No entanto, podemos prosseguir com a constru\u00e7\u00e3o dos outros endpoints. E lembre-se, \u00e9 importante testar esse endpoint para garantir que tudo esteja funcionando corretamente. "},{"location":"02/#implementando-o-teste-da-rota-post","title":"Implementando o teste da rota POST","text":"Antes de criar o teste de fato, vamos execut\u00e1-los para ver como anda a nossa cobertura: $ Execu\u00e7\u00e3o no terminal! Vemos que temos 3 Miss. Possivelmente das linhas que acabamos de escrever. Ent\u00e3o, vamos escrever nosso teste. Esse teste para a rota POST precisa verificar se a cria\u00e7\u00e3o de um novo usu\u00e1rio funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o POST com um novo usu\u00e1rio para a rota /users/. Em seguida, verificamos se a resposta tem o status HTTP 201 (Criado) e se a resposta cont\u00e9m o novo usu\u00e1rio criado. tests/test_app.py Ao executar o teste: $ Execu\u00e7\u00e3o no terminal! "},{"location":"02/#nao-se-repita-dry","title":"N\u00e3o se repita (DRY)","text":"Voc\u00ea deve ter notado que a linha Para solucionar essa repeti\u00e7\u00e3o, podemos usar uma funcionalidade do pytest chamada Fixture. Uma fixture \u00e9 como uma fun\u00e7\u00e3o que prepara dados ou estado necess\u00e1rios para o teste. Pode ser pensada como uma forma de n\u00e3o repetir a fase de Arrange de um teste, simplificando a chamada e n\u00e3o repetindo c\u00f3digo. Se fixtures s\u00e3o uma novidade para voc\u00eaExiste uma live de Python onde discutimos especificamente sobre fixtures Link direto Neste caso, vamos criar uma fixture que retorna nosso Agora, em vez de repetir a cria\u00e7\u00e3o do Com essa simples mudan\u00e7a, conseguimos tornar nosso c\u00f3digo mais limpo e f\u00e1cil de manter, seguindo o princ\u00edpio DRY. Vemos que estamos no caminho certo. Agora que a rota POST est\u00e1 implementada, vamos seguir para a pr\u00f3xima opera\u00e7\u00e3o CRUD: Read. "},{"location":"02/#implementando-a-rota-get","title":"Implementando a Rota GET","text":"A rota GET \u00e9 usada para recuperar informa\u00e7\u00f5es de um ou mais usu\u00e1rios do nosso sistema. No contexto do CRUD, o verbo HTTP GET est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Read\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). Para estruturar a resposta dessa rota, podemos criar um novo modelo chamado Com esse modelo definido, podemos criar nosso endpoint GET. Este endpoint retornar\u00e1 uma inst\u00e2ncia de Com essa implementa\u00e7\u00e3o, nossa API agora pode retornar uma lista de usu\u00e1rios. No entanto, nosso trabalho ainda n\u00e3o acabou. A pr\u00f3xima etapa \u00e9 escrever testes para garantir que nossa rota GET est\u00e1 funcionando corretamente. Isso nos ajudar\u00e1 a identificar e corrigir quaisquer problemas antes de prosseguirmos com a implementa\u00e7\u00e3o de outras rotas. "},{"location":"02/#implementando-o-teste-da-rota-de-get","title":"Implementando o teste da rota de GET","text":"Nosso teste da rota GET tem que verificar se a recupera\u00e7\u00e3o dos usu\u00e1rios est\u00e1 funcionando corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o GET para a rota /users/. Em seguida, verificamos se a resposta tem o status HTTP 200 (OK) e se a resposta cont\u00e9m a lista de usu\u00e1rios. tests/test_app.py Com as rotas POST e GET implementadas, agora podemos criar e recuperar usu\u00e1rios. Vamos implementar a pr\u00f3xima opera\u00e7\u00e3o CRUD: Update. "},{"location":"02/#implementando-a-rota-put","title":"Implementando a Rota PUT","text":"A rota PUT \u00e9 usada para atualizar as informa\u00e7\u00f5es de um usu\u00e1rio existente. No contexto do CRUD, o verbo HTTP PUT est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Update\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). No entanto, se o usu\u00e1rio solicitado n\u00e3o for encontrado, dever\u00edamos retornar o status HTTP 404 (N\u00e3o Encontrado). fast_zero/app.py "},{"location":"02/#implementando-o-teste-da-rota-de-put","title":"Implementando o teste da rota de PUT","text":"Nosso teste da rota PUT precisa verificar se a atualiza\u00e7\u00e3o de um usu\u00e1rio existente funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o PUT com as novas informa\u00e7\u00f5es do usu\u00e1rio para a rota Com as rotas POST, GET e PUT implementadas, agora podemos criar, recuperar e atualizar usu\u00e1rios. A \u00faltima opera\u00e7\u00e3o CRUD que precisamos implementar \u00e9 Delete. "},{"location":"02/#implementando-a-rota-delete","title":"Implementando a Rota DELETE","text":"A rota DELETE \u00e9 usada para excluir um usu\u00e1rio do nosso sistema. No contexto do CRUD, o verbo HTTP DELETE est\u00e1 associado \u00e0 opera\u00e7\u00e3o \"Delete\". Se a solicita\u00e7\u00e3o for bem-sucedida, a rota deve retornar o status HTTP 200 (OK). No entanto, se o usu\u00e1rio solicitado n\u00e3o for encontrado, dever\u00edamos retornar o status HTTP 404 (N\u00e3o Encontrado). Para transmitir uma mensagem de sucesso ou falha na opera\u00e7\u00e3o de exclus\u00e3o, podemos criar um modelo chamado Agora podemos criar nosso endpoint DELETE. Este endpoint receber\u00e1 o ID do usu\u00e1rio que queremos excluir. Note que, estamos lan\u00e7ando uma exce\u00e7\u00e3o HTTP quando o ID do usu\u00e1rio est\u00e1 fora do range da nossa lista (simula\u00e7\u00e3o do nosso banco de dados). Quando conseguimos excluir o usu\u00e1rio com sucesso, retornamos a mensagem de sucesso em um modelo do tipo Com a implementa\u00e7\u00e3o da rota DELETE conclu\u00edda, \u00e9 fundamental garantirmos que essa rota est\u00e1 funcionando conforme o esperado. Para isso, precisamos escrever testes para essa rota. "},{"location":"02/#implementando-o-teste-da-rota-de-delete","title":"Implementando o teste da rota de DELETE","text":"Nosso teste da rota DELETE precisa verificar se a exclus\u00e3o de um usu\u00e1rio existente funciona corretamente. N\u00f3s enviamos uma solicita\u00e7\u00e3o DELETE para a rota /users/{user_id}. Em seguida, verificamos se a resposta tem o status HTTP 200 (OK) e se a resposta cont\u00e9m uma mensagem informando que o usu\u00e1rio foi exclu\u00eddo. tests/test_app.py "},{"location":"02/#checando-tudo-antes-do-commit","title":"Checando tudo antes do commit","text":"Antes de fazermos o commit, \u00e9 uma boa pr\u00e1tica checarmos todo o c\u00f3digo, e podemos fazer isso com as a\u00e7\u00f5es que criamos com o taskipy. $ Execu\u00e7\u00e3o no terminal! "},{"location":"02/#commit","title":"Commit","text":"Ap\u00f3s toda essa jornada de aprendizado, constru\u00e7\u00e3o e teste de rotas, chegou a hora de registrar nosso progresso utilizando o git. Fazer commits regulares \u00e9 uma boa pr\u00e1tica, pois mant\u00e9m um hist\u00f3rico detalhado das altera\u00e7\u00f5es e facilita a volta a uma vers\u00e3o anterior do c\u00f3digo, se necess\u00e1rio. Primeiramente, vamos verificar as altera\u00e7\u00f5es feitas no projeto com o comando Em seguida, vamos adicionar todas as altera\u00e7\u00f5es para o pr\u00f3ximo commit. O comando Agora, estamos prontos para fazer o commit. Com o comando Por fim, enviamos nossas altera\u00e7\u00f5es para o reposit\u00f3rio remoto com E pronto! As altera\u00e7\u00f5es est\u00e3o seguras no hist\u00f3rico do git, e podemos continuar com o pr\u00f3ximo passo do projeto. "},{"location":"02/#conclusao","title":"Conclus\u00e3o","text":"Com a implementa\u00e7\u00e3o bem-sucedida das rotas CRUD, demos um passo significativo na constru\u00e7\u00e3o de uma API robusta e funcional com FastAPI. Agora podemos manipular usu\u00e1rios - criar, ler, atualizar e excluir - o que \u00e9 fundamental para muitos sistemas de informa\u00e7\u00e3o. O papel dos testes em cada etapa n\u00e3o pode ser subestimado. Testes n\u00e3o apenas nos ajudam a assegurar que nosso c\u00f3digo est\u00e1 funcionando como esperado, mas tamb\u00e9m nos permitem refinar nossas solu\u00e7\u00f5es e detectar problemas potenciais antes que eles afetem a funcionalidade geral do nosso sistema. Nunca subestime a import\u00e2ncia de executar seus testes sempre que fizer uma altera\u00e7\u00e3o em seu c\u00f3digo! At\u00e9 aqui, no entanto, trabalhamos com um \"banco de dados\" provis\u00f3rio, na forma de uma lista Python, que \u00e9 vol\u00e1til e n\u00e3o persiste os dados de uma execu\u00e7\u00e3o do aplicativo para outra. Para nosso aplicativo ser \u00fatil em um cen\u00e1rio do mundo real, precisamos armazenar nossos dados de forma mais duradoura. \u00c9 a\u00ed que os bancos de dados entram. No pr\u00f3ximo t\u00f3pico, vamos explorar uma das partes mais cr\u00edticas de qualquer aplicativo - a conex\u00e3o e intera\u00e7\u00e3o com um banco de dados. Vamos aprender a integrar nosso aplicativo FastAPI com um banco de dados real, permitindo a persist\u00eancia de nossos dados de usu\u00e1rio entre as sess\u00f5es do aplicativo. "},{"location":"03/","title":"Configurando o banco de dados e gerenciando migra\u00e7\u00f5es com Alembic","text":""},{"location":"03/#configurando-o-banco-de-dados-e-gerenciando-migracoes-com-alembic","title":"Configurando o Banco de Dados e Gerenciando Migra\u00e7\u00f5es com Alembic","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Ol\u00e1 a todos! Se voc\u00ea est\u00e1 chegando agora, recomendamos verificar as aulas anteriores de nosso curso \"FastAPI do Zero: Criando um Projeto com Bancos de Dados, Testes e Deploy\". Hoje, vamos mergulhar no SQLAlchemy e no Alembic, e come\u00e7aremos a configurar nosso banco de dados. Antes de mergulharmos na instala\u00e7\u00e3o e configura\u00e7\u00e3o, vamos esclarecer alguns conceitos. "},{"location":"03/#o-que-e-um-orm-e-por-que-usamos-um","title":"O que \u00e9 um ORM e por que usamos um?","text":"ORM significa Mapeamento Objeto-Relacional. \u00c9 uma t\u00e9cnica de programa\u00e7\u00e3o que vincula (ou mapeia) objetos a registros de banco de dados. Em outras palavras, um ORM permite que voc\u00ea interaja com seu banco de dados, como se voc\u00ea estivesse trabalhando com objetos Python. O SQLAlchemy \u00e9 um exemplo de ORM. Ele permite que voc\u00ea trabalhe com bancos de dados SQL de maneira mais natural aos programadores Python. Em vez de escrever consultas SQL cruas, voc\u00ea pode usar m\u00e9todos e atributos Python para manipular seus registros de banco de dados. Mas por que usar\u00edamos um ORM? Aqui est\u00e3o algumas raz\u00f5es:
Uma boa pr\u00e1tica no desenvolvimento de aplica\u00e7\u00f5es \u00e9 separar as configura\u00e7\u00f5es do c\u00f3digo. Configura\u00e7\u00f5es, como credenciais de banco de dados, s\u00e3o propensas a mudan\u00e7as entre ambientes diferentes (como desenvolvimento, teste e produ\u00e7\u00e3o). Mistur\u00e1-las com o c\u00f3digo pode tornar o processo de mudan\u00e7a entre esses ambientes complicado e propenso a erros. Caso queira saber mais sobre 12 fatoresTemos uma live focada nesse assunto com a participa\u00e7\u00e3o especial do Bruno Rocha Link direto Al\u00e9m disso, expor credenciais de banco de dados e outras informa\u00e7\u00f5es sens\u00edveis no c\u00f3digo-fonte \u00e9 uma pr\u00e1tica de seguran\u00e7a ruim. Se esse c\u00f3digo fosse comprometido, essas informa\u00e7\u00f5es poderiam ser usadas para acessar e manipular seus recursos. Por isso, usaremos o Isso est\u00e1 alinhado com a metodologia dos 12 fatores, um conjunto de melhores pr\u00e1ticas para desenvolvimento de aplica\u00e7\u00f5es modernas. O terceiro fator, \"Config\", afirma que as configura\u00e7\u00f5es que variam entre os ambientes devem ser armazenadas no ambiente e n\u00e3o no c\u00f3digo. Agora que entendemos melhor esses conceitos, vamos come\u00e7ar instalando as bibliotecas que vamos usar. O primeiro passo \u00e9 instalar o SQLAlchemy, um ORM que nos permite trabalhar com bancos de dados SQL de maneira Pythonic. Al\u00e9m disso, o Alembic, que \u00e9 uma ferramenta de migra\u00e7\u00e3o de banco de dados, funciona muito bem com o SQLAlchemy e nos ajudar\u00e1 a gerenciar as altera\u00e7\u00f5es do esquema do nosso banco de dados. $ Execu\u00e7\u00e3o no terminal! Al\u00e9m disso, para evitar a escrita de configura\u00e7\u00f5es do banco de dados diretamente no c\u00f3digo-fonte, usaremos o Agora estamos prontos para mergulhar na configura\u00e7\u00e3o do nosso banco de dados! Vamos em frente. "},{"location":"03/#o-basico-sobre-sqlalchemy","title":"O b\u00e1sico sobre SQLAlchemy","text":"SQLAlchemy \u00e9 uma biblioteca Python vers\u00e1til, concebida para intermediar a intera\u00e7\u00e3o entre Python e bancos de dados relacionais, como MySQL, PostgreSQL e SQLite. A biblioteca \u00e9 constitu\u00edda por duas partes principais: o Core e o ORM (Object Relational Mapper).
Al\u00e9m do Core e do ORM, o SQLAlchemy conta com outros componentes cruciais que ser\u00e3o foco desta aula, a Engine e a Session: "},{"location":"03/#engine","title":"Engine","text":"A 'Engine' do SQLAlchemy \u00e9 o ponto de contato com o banco de dados, estabelecendo e gerenciando as conex\u00f5es. Ela \u00e9 instanciada atrav\u00e9s da fun\u00e7\u00e3o Quanto \u00e0 persist\u00eancia de dados e consultas ao banco de dados utilizando o ORM, a Session \u00e9 a principal interface. Ela atua como um intermedi\u00e1rio entre o aplicativo Python e o banco de dados, mediada pela Engine. A Session \u00e9 encarregada de todas as transa\u00e7\u00f5es, fornecendo uma API para conduzi-las. Agora que conhecemos a Engine e a Session, vamos explorar a defini\u00e7\u00e3o de modelos de dados. "},{"location":"03/#definindo-os-modelos-de-dados-com-sqlalchemy","title":"Definindo os Modelos de Dados com SQLAlchemy","text":"Os modelos de dados definem a estrutura de como os dados ser\u00e3o armazenados no banco de dados. No ORM do SQLAlchemy, esses modelos s\u00e3o definidos como classes Python que herdam de uma classe base comum. A classe base \u00e9 criada a partir de Cada classe que herda da classe base \u00e9 automaticamente mapeada para uma tabela no banco de dados. Adicionalmente, a classe base inclui um objeto de metadados que \u00e9 uma cole\u00e7\u00e3o de todas as tabelas que foram declaradas. Este objeto \u00e9 utilizado para gerenciar opera\u00e7\u00f5es como cria\u00e7\u00e3o, modifica\u00e7\u00e3o e exclus\u00e3o de tabelas. Vamos agora definir nosso modelo Inclua o seguinte c\u00f3digo no arquivo Aqui, Antes de prosseguirmos, uma boa pr\u00e1tica seria criar um teste para validar se toda a estrutura do banco de dados funciona. Vamos criar um arquivo para validar isso: A partir daqui, voc\u00ea pode prosseguir com a estrutura\u00e7\u00e3o do conte\u00fado desse arquivo para definir os testes necess\u00e1rios para validar o seu modelo de usu\u00e1rio e sua intera\u00e7\u00e3o com o banco de dados. "},{"location":"03/#antes-de-escrever-os-testes","title":"Antes de Escrever os Testes","text":"A essa altura, se estiv\u00e9ssemos buscando apenas cobertura, poder\u00edamos simplesmente testar utilizando o modelo, e isso seria suficiente. No entanto, queremos verificar se toda a nossa intera\u00e7\u00e3o com o banco de dados ocorrer\u00e1 com sucesso. Isso inclui saber se os tipos de dados na tabela foram mapeados corretamente, se \u00e9 poss\u00edvel interagir com o banco de dados, se o ORM est\u00e1 estruturado adequadamente com a classe base. Precisamos garantir que todo esse esquema funcione. Neste diagrama, vemos a rela\u00e7\u00e3o completa entre o aplicativo Python e o banco de dados. A conex\u00e3o \u00e9 estabelecida atrav\u00e9s do SQLAlchemy ORM, que fornece uma Session para interagir com os Modelos. Esses modelos s\u00e3o mapeados para as tabelas no banco de dados, enquanto a Engine se conecta com o banco de dados e depende de Metadata para manter as informa\u00e7\u00f5es das tabelas. Portanto, criaremos uma fixture para que possamos usar todo esse esquema sempre que necess\u00e1rio. "},{"location":"03/#criando-uma-fixture-para-interacoes-com-o-banco-de-dados","title":"Criando uma Fixture para intera\u00e7\u00f5es com o Banco de Dados","text":"Para testar o banco, temos que fazer diversos passos, e isso pode tornar nosso teste bastante grande. Uma fixture pode ajudar a isolar toda essa configura\u00e7\u00e3o do banco de dados fora do teste. Assim, evitamos repetir o mesmo c\u00f3digo em todos os testes e ainda garantimos que cada teste tenha sua pr\u00f3pria vers\u00e3o limpa do banco de dados. Vamos criar uma fixture para a conex\u00e3o com o banco de dados chamada Aqui, estamos utilizando o SQLite como o banco de dados em mem\u00f3ria para os testes. Essa \u00e9 uma pr\u00e1tica comum em testes unit\u00e1rios, pois a utiliza\u00e7\u00e3o de um banco de dados em mem\u00f3ria \u00e9 mais r\u00e1pida do que um banco de dados persistido em disco. Com o SQLite em mem\u00f3ria, podemos criar e destruir bancos de dados facilmente, o que \u00e9 \u00fatil para isolar os testes e garantir que os dados de um teste n\u00e3o afetem outros testes. Al\u00e9m disso, n\u00e3o precisamos nos preocupar com a limpeza dos dados ap\u00f3s a execu\u00e7\u00e3o dos testes, j\u00e1 que o banco de dados em mem\u00f3ria \u00e9 descartado quando o programa \u00e9 encerrado. O que cada linha da fixture faz?
Resumindo, essa fixture est\u00e1 configurando e limpando um banco de dados de teste para cada teste que o solicita, assegurando que cada teste seja isolado e tenha seu pr\u00f3prio ambiente limpo para trabalhar. Isso \u00e9 uma boa pr\u00e1tica em testes de unidade, j\u00e1 que queremos que cada teste seja independente e n\u00e3o afete os demais. "},{"location":"03/#criando-um-teste-para-a-nossa-tabela","title":"Criando um Teste para a Nossa Tabela","text":"Agora, no arquivo "},{"location":"03/#executando-o-teste","title":"Executando o teste","text":"A execu\u00e7\u00e3o de testes \u00e9 uma parte vital do desenvolvimento de qualquer aplica\u00e7\u00e3o. Os testes nos ajudam a identificar e corrigir problemas antes que eles se tornem mais s\u00e9rios. Eles tamb\u00e9m fornecem a confian\u00e7a de que nossas mudan\u00e7as n\u00e3o quebraram nenhuma funcionalidade existente. No nosso caso, vamos executar os testes para validar nossos modelos de usu\u00e1rio e garantir que eles estejam funcionando como esperado. Para executar os testes, digite o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Neste caso, podemos ver que todos os nossos testes passaram com sucesso. Isso significa que nossa funcionalidade de cria\u00e7\u00e3o de usu\u00e1rio est\u00e1 funcionando corretamente e que nosso modelo de usu\u00e1rio est\u00e1 sendo corretamente persistido no banco de dados. Com nossos modelos e testes de banco de dados agora em ordem, estamos prontos para avan\u00e7ar para a pr\u00f3xima fase de configura\u00e7\u00e3o de nosso banco de dados e gerenciamento de migra\u00e7\u00f5es. "},{"location":"03/#configuracao-do-ambiente-do-banco-de-dados","title":"Configura\u00e7\u00e3o do ambiente do banco de dados","text":"Por fim, vamos configurar nosso banco de dados. Primeiro, vamos criar um novo arquivo chamado No arquivo Agora, vamos definir o Com isso, quando a classe Finalmente, adicione o arquivo de banco de dados, "},{"location":"03/#instalando-o-alembic-e-criando-a-primeira-migracao","title":"Instalando o Alembic e Criando a Primeira Migra\u00e7\u00e3o","text":"Antes de avan\u00e7armos, \u00e9 importante entender o que s\u00e3o migra\u00e7\u00f5es de banco de dados e por que s\u00e3o \u00fateis. As migra\u00e7\u00f5es s\u00e3o uma maneira de fazer altera\u00e7\u00f5es ou atualiza\u00e7\u00f5es no banco de dados, como adicionar uma tabela ou uma coluna a uma tabela, ou alterar o tipo de dados de uma coluna. Elas s\u00e3o extremamente \u00fateis, pois nos permitem manter o controle de todas as altera\u00e7\u00f5es feitas no esquema do banco de dados ao longo do tempo. Elas tamb\u00e9m nos permitem reverter para uma vers\u00e3o anterior do esquema do banco de dados, se necess\u00e1rio. Caso nunca tenha trabalhado com Migra\u00e7\u00f5esTemos uma live de Python focada nesse assunto em espec\u00edfico Link direto Agora, vamos come\u00e7ar instalando o Alembic, que \u00e9 uma ferramenta de migra\u00e7\u00e3o de banco de dados para SQLAlchemy. Usaremos o Poetry para adicionar o Alembic ao nosso projeto: $ Execu\u00e7\u00e3o no terminal! Ap\u00f3s a instala\u00e7\u00e3o do Alembic, precisamos inici\u00e1-lo em nosso projeto. O comando de inicializa\u00e7\u00e3o criar\u00e1 um diret\u00f3rio Com isso, a estrutura do nosso projeto sofre algumas altera\u00e7\u00f5es e novos arquivos s\u00e3o criados: No arquivo Com o Alembic devidamente instalado e iniciado, agora \u00e9 o momento de gerar nossa primeira migra\u00e7\u00e3o. Mas, antes disso, precisamos garantir que o Alembic consiga acessar nossas configura\u00e7\u00f5es e modelos corretamente. Para isso, vamos fazer algumas altera\u00e7\u00f5es no arquivo Neste arquivo, precisamos:
O arquivo Feitas essas altera\u00e7\u00f5es, estamos prontos para gerar nossa primeira migra\u00e7\u00e3o autom\u00e1tica. O Alembic \u00e9 capaz de gerar migra\u00e7\u00f5es a partir das mudan\u00e7as detectadas nos nossos modelos do SQLAlchemy. Para criar a migra\u00e7\u00e3o, utilizamos o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Este comando instrui o Alembic a criar uma nova revis\u00e3o de migra\u00e7\u00e3o no diret\u00f3rio Ao criar uma migra\u00e7\u00e3o autom\u00e1tica com o Alembic, um arquivo \u00e9 gerado dentro da pasta Vamos analisar o arquivo de migra\u00e7\u00e3o: migrations/versions/e018397cecf4_create_users_table.py Esse arquivo descreve as mudan\u00e7as a serem feitas no banco de dados. Ele usa a linguagem core do SQLAlchemy, que \u00e9 mais baixo n\u00edvel que o ORM. As fun\u00e7\u00f5es Apesar desta migra\u00e7\u00e3o ter sido criada, ela ainda n\u00e3o foi aplicada ao nosso banco de dados. No entanto, o Alembic j\u00e1 criou um arquivo Debian/Ubuntu Mac
Para aplicar as migra\u00e7\u00f5es, usamos o comando Agora, se examinarmos nosso banco de dados novamente, veremos que a tabela users foi criada: $ Execu\u00e7\u00e3o no terminal! Finalmente, lembre-se de que todas essas mudan\u00e7as que fizemos s\u00f3 existem localmente no seu ambiente de trabalho at\u00e9 agora. Para que sejam compartilhadas com outras pessoas, precisamos fazer commit dessas mudan\u00e7as no nosso sistema de controle de vers\u00e3o. "},{"location":"03/#commit","title":"Commit","text":"Primeiro, vamos verificar o status do nosso reposit\u00f3rio para ver as mudan\u00e7as que fizemos: $ Execu\u00e7\u00e3o no terminal! Voc\u00ea ver\u00e1 uma lista de arquivos que foram modificados ou adicionados. As altera\u00e7\u00f5es devem incluir os arquivos de migra\u00e7\u00e3o que criamos, bem como quaisquer altera\u00e7\u00f5es que fizemos em nossos arquivos de modelo e configura\u00e7\u00e3o. Em seguida, vamos adicionar todas as mudan\u00e7as ao pr\u00f3ximo commit: $ Execu\u00e7\u00e3o no terminal! Agora, estamos prontos para fazer o commit das nossas altera\u00e7\u00f5es. Vamos fornecer uma mensagem de commit que descreve as mudan\u00e7as que fizemos: $ Execu\u00e7\u00e3o no terminal! Finalmente, vamos enviar as mudan\u00e7as para o reposit\u00f3rio remoto: $ Execu\u00e7\u00e3o no terminal! E pronto! As mudan\u00e7as que fizemos foram salvas no hist\u00f3rico do Git e agora est\u00e3o dispon\u00edveis no git. "},{"location":"03/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, demos passos significativos para preparar nosso projeto FastAPI para interagir com um banco de dados. Come\u00e7amos definindo nosso primeiro modelo de dados, o Avan\u00e7amos para configurar o ambiente de desenvolvimento, onde estabelecemos um arquivo Na \u00faltima parte desta aula, focamos na instala\u00e7\u00e3o e configura\u00e7\u00e3o do Alembic, uma ferramenta de migra\u00e7\u00e3o de banco de dados para SQLAlchemy. Usando o Alembic, criamos nossa primeira migra\u00e7\u00e3o que, automaticamente, gera o esquema do banco de dados a partir dos nossos modelos SQLAlchemy. Com esses passos, nosso projeto est\u00e1 bem encaminhado para come\u00e7ar a persistir dados. Na pr\u00f3xima aula, avan\u00e7aremos para a fase crucial de conectar o SQLAlchemy aos endpoints do nosso projeto. Isso permitir\u00e1 a realiza\u00e7\u00e3o de opera\u00e7\u00f5es de CRUD nos nossos usu\u00e1rios diretamente atrav\u00e9s da API. "},{"location":"04/","title":"Integrando Banco de Dados a API","text":""},{"location":"04/#integrando-banco-de-dados-a-api","title":"Integrando Banco de Dados a API","text":"Objetivos dessa aula:
Aula Slides C\u00f3digo Ap\u00f3s a cria\u00e7\u00e3o de nossos modelos e migra\u00e7\u00f5es na aula passada, chegou o momento de dar um passo significativo: integrar o banco de dados \u00e0 nossa aplica\u00e7\u00e3o FastAPI. Vamos deixar de lado o banco de dados fict\u00edcio que criamos anteriormente e mergulhar na implementa\u00e7\u00e3o de um banco de dados real e funcional. "},{"location":"04/#integrando-sqlalchemy-a-nossa-aplicacao-fastapi","title":"Integrando SQLAlchemy \u00e0 Nossa Aplica\u00e7\u00e3o FastAPI","text":"Para aqueles que n\u00e3o est\u00e3o familiarizados, o SQLAlchemy \u00e9 uma biblioteca Python que facilita a intera\u00e7\u00e3o com um banco de dados SQL. Ele faz isso oferecendo uma forma de trabalhar com bancos de dados que aproveita a facilidade e o poder do Python, ao mesmo tempo em que mant\u00e9m a efici\u00eancia e a flexibilidade dos bancos de dados SQL. Caso nunca tenha trabalhado com SQLAlchemyTemos diversas lives de Python focadas nesse assunto. Esta sobre o ORM em espec\u00edfico: Link direto Essa sobre as novidades da vers\u00e3o 1.4 e do estilo de programa\u00e7\u00e3o da vers\u00e3o 2.0 Link direto E finalmente uma focada no processo de migra\u00e7\u00f5es com o SQLalchemy + Alembic (que veremos nessa aula) Link direto Uma pe\u00e7a chave do SQLAlchemy \u00e9 o conceito de uma \"sess\u00e3o\". Se voc\u00ea \u00e9 novo no mundo dos bancos de dados, pode pensar na sess\u00e3o como um carrinho de compras virtual: conforme voc\u00ea navega pelo site (ou, neste caso, conforme seu c\u00f3digo executa), voc\u00ea pode adicionar ou remover itens desse carrinho. No entanto, nenhuma altera\u00e7\u00e3o \u00e9 realmente feita at\u00e9 que voc\u00ea decida finalizar a compra. No contexto do SQLAlchemy, \"finalizar a compra\" \u00e9 equivalente a fazer o commit das suas altera\u00e7\u00f5es. A sess\u00e3o no SQLAlchemy \u00e9 t\u00e3o poderosa que, na verdade, incorpora tr\u00eas padr\u00f5es de arquitetura importantes.
Entender esses conceitos \u00e9 importante, pois nos ajuda a entender melhor como o SQLAlchemy funciona e como podemos us\u00e1-lo de forma mais eficaz. Agora que temos uma ideia do que \u00e9 uma sess\u00e3o, vamos configurar uma para nosso projeto. Para isso, criaremos a fun\u00e7\u00e3o "},{"location":"04/#gerenciando-dependencias-com-fastapi","title":"Gerenciando Depend\u00eancias com FastAPI","text":"Assim como a sess\u00e3o SQLAlchemy, que implementa v\u00e1rios padr\u00f5es arquiteturais importantes, FastAPI tamb\u00e9m usa um conceito de padr\u00e3o arquitetural chamado \"Inje\u00e7\u00e3o de Depend\u00eancia\". No mundo do desenvolvimento de software, uma \"depend\u00eancia\" \u00e9 um componente que um m\u00f3dulo de software precisa para realizar sua fun\u00e7\u00e3o. Imagine um m\u00f3dulo como uma f\u00e1brica e as depend\u00eancias como as partes ou mat\u00e9rias-primas que a f\u00e1brica precisa para produzir seus produtos. Em vez de a f\u00e1brica ter que buscar essas pe\u00e7as por conta pr\u00f3pria (o que seria ineficiente), elas s\u00e3o entregues \u00e0 f\u00e1brica, prontas para serem usadas. Este \u00e9 o conceito de Inje\u00e7\u00e3o de Depend\u00eancia. A Inje\u00e7\u00e3o de Depend\u00eancia permite que mantenhamos um baixo n\u00edvel de acoplamento entre diferentes m\u00f3dulos de um sistema. As depend\u00eancias entre os m\u00f3dulos n\u00e3o s\u00e3o definidas no c\u00f3digo, mas sim pela configura\u00e7\u00e3o de uma infraestrutura de software (container) que \u00e9 respons\u00e1vel por \"injetar\" em cada componente suas depend\u00eancias declaradas. Em termos pr\u00e1ticos, o que isso significa \u00e9 que, em vez de cada parte do nosso c\u00f3digo ter que criar suas pr\u00f3prias inst\u00e2ncias de classes ou servi\u00e7os de que depende (o que pode levar a duplica\u00e7\u00e3o de c\u00f3digo e tornar os testes mais dif\u00edceis), essas inst\u00e2ncias s\u00e3o criadas uma vez e depois injetadas onde s\u00e3o necess\u00e1rias. FastAPI fornece a fun\u00e7\u00e3o Agora que temos a nossa sess\u00e3o de banco de dados sendo gerenciada por meio do FastAPI e da inje\u00e7\u00e3o de depend\u00eancias, vamos atualizar nossos endpoints para que possam tirar proveito disso. Come\u00e7aremos com a rota de POST para a cria\u00e7\u00e3o de usu\u00e1rios. Ao inv\u00e9s de usarmos o banco de dados falso que criamos inicialmente, agora vamos fazer a inser\u00e7\u00e3o real dos usu\u00e1rios no nosso banco de dados. Para isso, vamos modificar o nosso endpoint da seguinte maneira: fast_zero/app.py Nesse c\u00f3digo, a fun\u00e7\u00e3o Agora que nossa rota de POST est\u00e1 funcionando com o banco de dados real, precisamos atualizar nossos testes para refletir essa mudan\u00e7a. Como estamos usando a inje\u00e7\u00e3o de depend\u00eancias, precisamos tamb\u00e9m usar essa funcionalidade nos nossos testes para que possamos injetar a sess\u00e3o de banco de dados de teste. Vamos alterar a nossa fixture Com isso, quando o FastAPI tentar injetar a sess\u00e3o em nossos endpoints, ele vai injetar a sess\u00e3o de teste que definimos, em vez da sess\u00e3o real. E como estamos usando um banco de dados em mem\u00f3ria para os testes, nossos testes n\u00e3o v\u00e3o interferir nos dados reais do nosso aplicativo. tests/test_app.py Agora que temos a nossa fixture configurada, vamos atualizar o nosso teste O nosso teste ainda n\u00e3o consegue ser executado, mas existe um motivo para isso. "},{"location":"04/#threads-e-conexoes","title":"Threads e conex\u00f5es","text":"No ambiente de testes do FastAPI, a aplica\u00e7\u00e3o e os testes podem rodar em threads diferentes. Isso pode levar a um erro com o SQLite, pois os objetos SQLite criados em uma thread s\u00f3 podem ser usados na mesma thread. Para contornar isso, adicionaremos os seguintes par\u00e2metros na cria\u00e7\u00e3o da
Assim, nossa fixture deve ficar dessa forma: tests/conftest.py Depois de realizar essas mudan\u00e7as, podemos executar nossos testes e verificar se est\u00e3o passando. Por\u00e9m, embora o teste Nos pr\u00f3ximos passos, vamos realizar essas modifica\u00e7\u00f5es para garantir que todo o nosso aplicativo esteja usando o banco de dados real. "},{"location":"04/#modificando-o-endpoint-get-users","title":"Modificando o Endpoint GET /users","text":"Agora que temos o nosso banco de dados configurado e funcionando, \u00e9 o momento de atualizar o nosso endpoint de GET para interagir com o banco de dados real. Em vez de trabalhar com uma lista fict\u00edcia de usu\u00e1rios, queremos buscar os usu\u00e1rios diretamente do nosso banco de dados, permitindo uma intera\u00e7\u00e3o din\u00e2mica e real com os dados. fast_zero/app.py Neste c\u00f3digo, adicionamos algumas funcionalidades essenciais para a busca de dados. Os par\u00e2metros
Essas adi\u00e7\u00f5es tornam o nosso endpoint mais flex\u00edvel e otimizado para lidar com diferentes cen\u00e1rios de uso. "},{"location":"04/#testando-o-endpoint-get-users","title":"Testando o Endpoint GET /users","text":"Com a mudan\u00e7a para o banco de dados real, nosso banco de dados de teste ser\u00e1 sempre resetado para cada teste. Portanto, n\u00e3o podemos mais executar o teste que t\u00ednhamos antes, pois n\u00e3o haver\u00e3o usu\u00e1rios no banco. Para verificar se o nosso endpoint est\u00e1 funcionando corretamente, vamos criar um novo teste que solicita uma lista de usu\u00e1rios de um banco vazio: tests/test_app.py Agora que temos nosso novo teste, podemos execut\u00e1-lo para verificar se o nosso endpoint GET est\u00e1 funcionando corretamente. Com esse novo teste, a fun\u00e7\u00e3o Por\u00e9m, \u00e9 claro, queremos tamb\u00e9m testar o caso em que existem usu\u00e1rios no banco. Para isso, vamos criar uma nova fixture que cria um usu\u00e1rio em nosso banco de dados de teste. "},{"location":"04/#criando-uma-fixture-para-user","title":"Criando uma fixture para User","text":"Para criar essa fixture, vamos aproveitar a nossa fixture de sess\u00e3o do SQLAlchemy, e criar um novo usu\u00e1rio dentro dela: tests/conftest.py Com essa fixture, sempre que precisarmos de um usu\u00e1rio em nossos testes, podemos simplesmente passar Agora podemos criar um novo teste para verificar se o nosso endpoint est\u00e1 retornando o usu\u00e1rio correto quando existe um usu\u00e1rio no banco: tests/test_app.py Agora podemos rodar o nosso teste novamente e verificar se ele est\u00e1 passando: $ Execu\u00e7\u00e3o no terminal! No entanto, mesmo que nosso c\u00f3digo pare\u00e7a correto, podemos encontrar um problema: o Pydantic n\u00e3o consegue converter diretamente nosso modelo SQLAlchemy para um modelo Pydantic. Vamos resolver isso agora. "},{"location":"04/#integrando-o-schema-ao-model","title":"Integrando o Schema ao Model","text":"A integra\u00e7\u00e3o direta do ORM com o nosso esquema Pydantic n\u00e3o \u00e9 imediata e exige algumas modifica\u00e7\u00f5es. O Pydantic, por padr\u00e3o, n\u00e3o sabe como lidar com os modelos do SQLAlchemy, o que nos leva ao erro observado nos testes. A solu\u00e7\u00e3o para esse problema passa por fazer uma altera\u00e7\u00e3o no esquema Para resolver o problema de convers\u00e3o entre SQLAlchemy e Pydantic, precisamos atualizar o nosso esquema Com essa mudan\u00e7a, nosso esquema Pydantic agora pode ser convertido a partir de um modelo SQLAlchemy. Agora podemos executar nosso teste novamente e verificar se ele est\u00e1 passando. $ Execu\u00e7\u00e3o no terminal! Agora que temos nosso endpoint GET funcionando corretamente e testado, podemos seguir para o endpoint PUT, e continuar com o processo de atualiza\u00e7\u00e3o dos nossos endpoints. "},{"location":"04/#modificando-o-endpoint-put-users","title":"Modificando o Endpoint PUT /users","text":"Agora, vamos modificar o endpoint de PUT para suportar o banco de dados, como fizemos com os endpoints POST e GET: fast_zero/app.py Semelhante ao que fizemos antes, estamos injetando a sess\u00e3o do SQLAlchemy em nosso endpoint e utilizando-a para buscar o usu\u00e1rio a ser atualizado. Se o usu\u00e1rio n\u00e3o for encontrado, retornamos um erro 404. Ao executar nosso linter, ele ir\u00e1 apontar um erro informando que importamos UserDB mas nunca o usamos. $ Execu\u00e7\u00e3o no terminal! Isso ocorre porque a rota PUT era a \u00fanica que estava utilizando UserDB, e agora que modificamos esta rota, podemos remover UserDB dos nossos e tamb\u00e9m excluir sua defini\u00e7\u00e3o no arquivo schemas.py Caso fique em d\u00favida sobre o que remover, seu arquivo "},{"location":"04/#adicionando-o-teste-do-put","title":"Adicionando o teste do PUT","text":"Tamb\u00e9m precisamos adicionar um teste para o nosso novo endpoint PUT: tests/test_app.py "},{"location":"04/#modificando-o-endpoint-delete-users","title":"Modificando o Endpoint DELETE /users","text":"Em seguida, modificamos o endpoint DELETE da mesma maneira: fast_zero/app.py Neste caso, estamos novamente usando a sess\u00e3o do SQLAlchemy para encontrar o usu\u00e1rio a ser deletado e, em seguida, exclu\u00edmos esse usu\u00e1rio do banco de dados. "},{"location":"04/#adicionando-testes-para-delete","title":"Adicionando testes para DELETE","text":"Assim como para o endpoint PUT, precisamos adicionar um teste para o nosso endpoint DELETE: tests/test_app.py "},{"location":"04/#cobertura-e-testes-nao-feitos","title":"Cobertura e testes n\u00e3o feitos","text":"Com o banco de dados agora em funcionamento, podemos verificar a cobertura de c\u00f3digo do arquivo Esses tr\u00eas casos ficam como exerc\u00edcio para quem est\u00e1 acompanhando este curso. Al\u00e9m disso, n\u00e3o devemos esquecer de remover a implementa\u00e7\u00e3o do banco de dados falso Agora que terminamos a atualiza\u00e7\u00e3o dos nossos endpoints, vamos fazer o commit das nossas altera\u00e7\u00f5es. O processo \u00e9 o seguinte: $ Execu\u00e7\u00e3o no terminal! Com isso, terminamos a atualiza\u00e7\u00e3o dos nossos endpoints para usar o nosso banco de dados real. "},{"location":"04/#conclusao","title":"Conclus\u00e3o","text":"Parab\u00e9ns por chegar ao final desta aula! Voc\u00ea deu um passo significativo no desenvolvimento de nossa aplica\u00e7\u00e3o, substituindo a implementa\u00e7\u00e3o do banco de dados falso pela integra\u00e7\u00e3o com um banco de dados real usando SQLAlchemy. Tamb\u00e9m vimos como ajustar os nossos testes para considerar essa nova realidade. Nesta aula, abordamos como modificar os endpoints para interagir com o banco de dados real e como utilizar a inje\u00e7\u00e3o de depend\u00eancias do FastAPI para gerenciar nossas sess\u00f5es do SQLAlchemy. Tamb\u00e9m discutimos a import\u00e2ncia dos testes para garantir que nossos endpoints est\u00e3o funcionando corretamente, e como as fixtures do Pytest podem nos auxiliar na prepara\u00e7\u00e3o do ambiente para esses testes. Tamb\u00e9m nos deparamos com situa\u00e7\u00f5es onde o Pydantic e o SQLAlchemy n\u00e3o interagem perfeitamente bem, e como solucionar esses casos. No final desta aula, voc\u00ea deve estar confort\u00e1vel em integrar um banco de dados real a uma aplica\u00e7\u00e3o FastAPI, saber como escrever testes robustos que levem em considera\u00e7\u00e3o a intera\u00e7\u00e3o com o banco de dados, e estar ciente de poss\u00edveis desafios ao trabalhar com Pydantic e SQLAlchemy juntos. "},{"location":"05/","title":"Autentica\u00e7\u00e3o e Autoriza\u00e7\u00e3o com JWT","text":""},{"location":"05/#autenticacao-e-autorizacao-com-jwt","title":"Autentica\u00e7\u00e3o e Autoriza\u00e7\u00e3o com JWT","text":"Objetivos da Aula:
Aula Slides C\u00f3digo "},{"location":"05/#introducao","title":"Introdu\u00e7\u00e3o","text":"Nesta aula, vamos abordar dois aspectos cruciais de qualquer aplica\u00e7\u00e3o web: a autentica\u00e7\u00e3o e a autoriza\u00e7\u00e3o. At\u00e9 agora, nossos usu\u00e1rios podem criar, ler, atualizar e deletar suas contas, mas qualquer pessoa pode fazer essas a\u00e7\u00f5es. N\u00e3o queremos que qualquer usu\u00e1rio possa deletar ou modificar a conta de outro usu\u00e1rio. Para evitar isso, vamos implementar autentica\u00e7\u00e3o e autoriza\u00e7\u00e3o em nossa aplica\u00e7\u00e3o. A autentica\u00e7\u00e3o \u00e9 o processo de verificar quem um usu\u00e1rio \u00e9, enquanto a autoriza\u00e7\u00e3o \u00e9 o processo de verificar o que ele tem permiss\u00e3o para fazer. Usaremos o JSON Web Token (JWT) para implementar a autentica\u00e7\u00e3o, e adicionaremos l\u00f3gica de autoriza\u00e7\u00e3o aos nossos endpoints. Al\u00e9m disso, at\u00e9 agora, estamos armazenando as senhas dos usu\u00e1rios como texto puro no banco de dados, o que \u00e9 uma pr\u00e1tica insegura. Vamos corrigir isso utilizando a biblioteca Bcrypt para encriptar as senhas. "},{"location":"05/#o-que-e-um-jwt","title":"O que \u00e9 um JWT","text":"O JWT \u00e9 um padr\u00e3o (RFC 7519) que define uma maneira compacta e aut\u00f4noma de transmitir informa\u00e7\u00f5es entre as partes de maneira segura. Essas informa\u00e7\u00f5es s\u00e3o transmitidas como um objeto JSON que \u00e9 digitalmente assinado usando um segredo (com o algoritmo HMAC) ou um par de chaves p\u00fablica/privada usando RSA ou ECDSA. Um JWT consiste em tr\u00eas partes:
Essas tr\u00eas partes s\u00e3o separadas por pontos (.) e juntas formam um token JWT. Formando a estrutura: \u00c9 importante ressaltar que, apesar de a informa\u00e7\u00e3o em um JWT estar codificada, ela n\u00e3o est\u00e1 criptografada. Isso significa que qualquer pessoa com acesso ao token pode decodificar e ler as informa\u00e7\u00f5es nele. No entanto, sem o segredo usado para assinar o token, eles n\u00e3o podem alterar as informa\u00e7\u00f5es ou forjar um novo token. Portanto, n\u00e3o devemos incluir informa\u00e7\u00f5es sens\u00edveis ou confidenciais no payload do JWT. Se quisermos ver o header, o payload e a assinatura contidas nesse token podemos acessar o debuger do jwt e checar quais as informa\u00e7\u00f5es que est\u00e3o nesse token "},{"location":"05/#como-funciona-o-jwt","title":"Como funciona o JWT","text":"Em uma aplica\u00e7\u00e3o web, o processo de autentica\u00e7\u00e3o geralmente funciona da seguinte maneira:
Nos pr\u00f3ximos t\u00f3picos, vamos detalhar como podemos gerar e verificar tokens JWT em nossa aplica\u00e7\u00e3o FastAPI, bem como adicionar autentica\u00e7\u00e3o e autoriza\u00e7\u00e3o aos nossos endpoints. "},{"location":"05/#gerando-tokens-jwt","title":"Gerando tokens JWT","text":"Para gerar tokens JWT, precisamos de duas bibliotecas extras: Agora, vamos criar uma fun\u00e7\u00e3o para gerar nossos tokens JWT. Criaremos um novo arquivo para gerenciar a seguran\u00e7a: A fun\u00e7\u00e3o Note que a constante Embora esse c\u00f3digo ser\u00e1 coberto no futuro com a utiliza\u00e7\u00e3o do token, \u00e9 interessante criarmos um teste para essa fun\u00e7\u00e3o com uma finalidade puramente did\u00e1tica. De forma em que consigamos ver os tokens gerados pelo Com isso vamos criar um arquivo chamado Na pr\u00f3xima se\u00e7\u00e3o, vamos ver como podemos usar a biblioteca Armazenar senhas em texto puro \u00e9 uma pr\u00e1tica de seguran\u00e7a extremamente perigosa. Em vez disso, \u00e9 uma pr\u00e1tica padr\u00e3o criptografar (\"hash\") as senhas antes de armazen\u00e1-las. Quando um usu\u00e1rio tenta se autenticar, a senha inserida \u00e9 criptografada novamente e comparada com a vers\u00e3o criptografada armazenada no banco de dados. Se as duas correspondem, o usu\u00e1rio \u00e9 autenticado. Vamos implementar essa funcionalidade usando a biblioteca A fun\u00e7\u00e3o Agora, quando um usu\u00e1rio se registra em nossa aplica\u00e7\u00e3o, devemos usar a fun\u00e7\u00e3o Na pr\u00f3xima se\u00e7\u00e3o, vamos modificar nossos endpoints para fazer uso dessas fun\u00e7\u00f5es. "},{"location":"05/#modificando-o-endpoint-de-post-para-encriptar-a-senha","title":"Modificando o endpoint de POST para encriptar a senha","text":"Com as fun\u00e7\u00f5es de cria\u00e7\u00e3o de hash de senha e verifica\u00e7\u00e3o de senha em vigor, agora podemos atualizar nossos endpoints para usar essa nova funcionalidade de encripta\u00e7\u00e3o. Primeiro, vamos modificar a fun\u00e7\u00e3o Desta forma, a senha n\u00e3o ser\u00e1 mais criada em texto plano no objeto Por n\u00e3o validar o password, usando o retorno "},{"location":"05/#modificando-o-endpoint-de-atualizacao-de-usuarios","title":"Modificando o endpoint de atualiza\u00e7\u00e3o de usu\u00e1rios","text":"\u00c9 igualmente importante modificar a fun\u00e7\u00e3o Assim, a atualiza\u00e7\u00e3o de um Assim como no teste da rota de cria\u00e7\u00e3o, os testes tamb\u00e9m passam normalmente por n\u00e3o validarem o campo password. $ Execu\u00e7\u00e3o no terminal! "},{"location":"05/#criando-um-endpoint-de-geracao-do-token","title":"Criando um endpoint de gera\u00e7\u00e3o do token","text":"Antes de criar o endpoint, precisamos criar um schema para o nosso token. Em um contexto JWT, "},{"location":"05/#utilizando-oauth2passwordrequestform","title":"Utilizando OAuth2PasswordRequestForm","text":"A classe Para usar os formul\u00e1rios no FastAPI, precisamos instalar o "},{"location":"05/#criando-um-endpoint-de-geracao-do-token_1","title":"Criando um endpoint de gera\u00e7\u00e3o do token","text":"Agora vamos criar o endpoint que ir\u00e1 autenticar o usu\u00e1rio e fornecer um token de acesso JWT. Este endpoint ir\u00e1 receber as informa\u00e7\u00f5es de login do usu\u00e1rio, verificar se as credenciais s\u00e3o v\u00e1lidas e, em caso afirmativo, retornar um token de acesso JWT. fast_zero/app.py Esse endpoint recebe os dados do formul\u00e1rio atrav\u00e9s do Agora vamos escrever um teste para verificar se o nosso novo endpoint est\u00e1 funcionando corretamente. tests/test_app.py Nesse teste, n\u00f3s enviamos uma requisi\u00e7\u00e3o POST para o endpoint \"/token\" com um username e uma senha v\u00e1lidos. Ent\u00e3o, n\u00f3s verificamos que a resposta cont\u00e9m um \"access_token\" e um \"token_type\", que s\u00e3o os campos que esperamos de um JWT v\u00e1lido. No entanto, h\u00e1 um problema. Agora que a senha est\u00e1 sendo criptografada, nosso teste falhar\u00e1: $ Execu\u00e7\u00e3o no terminal! Para corrigir isso, precisamos garantir que a senha esteja sendo criptografada na fixture antes de ser salva: tests/confitest.py Vamos rodar o teste novamente. No entanto, ainda teremos um problema. Agora s\u00f3 temos a vers\u00e3o criptografada da senha, que n\u00e3o \u00e9 \u00fatil para fazer o login, j\u00e1 que o login exige a senha em texto puro: $ Execu\u00e7\u00e3o no terminal! Para resolver isso, faremos uma modifica\u00e7\u00e3o no objeto user (um monkey patch) para adicionar a senha em texto puro: tests/confitest.py Monkey patching \u00e9 uma t\u00e9cnica em que modificamos ou estendemos o c\u00f3digo em tempo de execu\u00e7\u00e3o. Neste caso, estamos adicionando um novo atributo Agora, podemos alterar o teste para usar E agora todos os testes devem passar normalmente: $ Execu\u00e7\u00e3o no terminal! Isso conclui a parte de autentica\u00e7\u00e3o de nossa API. No pr\u00f3ximo passo, iremos implementar a autoriza\u00e7\u00e3o nos endpoints. "},{"location":"05/#protegendo-os-endpoints","title":"Protegendo os Endpoints","text":"Agora que temos uma forma de autenticar nossos usu\u00e1rios e emitir tokens JWT, \u00e9 hora de usar essa infraestrutura para proteger nossos endpoints. Neste passo, vamos adicionar autentica\u00e7\u00e3o aos endpoints PUT e DELETE. Para garantir que as informa\u00e7\u00f5es do usu\u00e1rio sejam extra\u00eddas corretamente do token JWT, precisamos de um schema especial, o Nesse ponto, criaremos uma a fun\u00e7\u00e3o Aqui, a fun\u00e7\u00e3o A vari\u00e1vel No bloco Por fim, realizamos uma consulta ao banco de dados para encontrar o usu\u00e1rio com o e-mail correspondente ao username contido no token. Primeiro, vamos aplicar a autentica\u00e7\u00e3o no endpoint PUT. Se o Com isso, podemos remover a query feita no endpoint para encontrar o User, pois ela j\u00e1 est\u00e1 sendo feita no Agora, vamos aplicar a autentica\u00e7\u00e3o no endpoint DELETE. Semelhante ao PUT, se o Com essa nova depend\u00eancia, o FastAPI automaticamente garantir\u00e1 que um token de autentica\u00e7\u00e3o v\u00e1lido seja fornecido antes de permitir o acesso a esses endpoints. Se o token n\u00e3o for v\u00e1lido, ou se o usu\u00e1rio tentar modificar ou deletar um usu\u00e1rio diferente, um erro ser\u00e1 retornado. "},{"location":"05/#atualizando-os-testes","title":"Atualizando os Testes","text":"Os testes precisam ser atualizados para refletir essas mudan\u00e7as. Primeiro, precisamos criar uma nova fixture que gere um token para um usu\u00e1rio de teste. tests/conftest.py Agora, podemos atualizar os testes para o endpoint PUT e DELETE para incluir a autentica\u00e7\u00e3o. tests/test_app.py Finalmente, podemos rodar todos os testes para garantir que tudo esteja funcionando corretamente. $ Execu\u00e7\u00e3o no terminal! Com essas altera\u00e7\u00f5es, nossos endpoints agora est\u00e3o seguramente protegidos pela autentica\u00e7\u00e3o. Apenas os usu\u00e1rios autenticados podem alterar ou deletar seus pr\u00f3prios dados. Isso traz uma camada adicional de seguran\u00e7a e integridade para o nosso aplicativo. "},{"location":"05/#commit","title":"Commit","text":"Depois de finalizar a prote\u00e7\u00e3o dos endpoints e atualizar os testes, \u00e9 hora de fazer commit das altera\u00e7\u00f5es. N\u00e3o se esque\u00e7a de revisar as altera\u00e7\u00f5es antes de fazer o commit. $ Execu\u00e7\u00e3o no terminal! "},{"location":"05/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, demos um passo importante para aumentar a seguran\u00e7a da nossa API. Implementamos a autentica\u00e7\u00e3o e a autoriza\u00e7\u00e3o para os endpoints PUT e DELETE, garantindo que apenas usu\u00e1rios autenticados possam alterar ou excluir seus pr\u00f3prios dados. Tamb\u00e9m atualizamos os testes para incluir a autentica\u00e7\u00e3o. Na pr\u00f3xima aula, continuaremos a expandir a funcionalidade da nossa API. At\u00e9 l\u00e1! "},{"location":"06/","title":"Refatorando a Estrutura do Projeto","text":""},{"location":"06/#refatorando-a-estrutura-do-projeto","title":"Refatorando a Estrutura do Projeto","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Ao longo da evolu\u00e7\u00e3o de um projeto, \u00e9 natural que sua estrutura inicial necessite de ajustes para manter a legibilidade, a facilidade de manuten\u00e7\u00e3o e a organiza\u00e7\u00e3o do c\u00f3digo. Nesta aula, faremos exatamente isso em nosso projeto FastAPI: vamos refatorar partes dele para melhorar sua estrutura e, em seguida, ampliar a cobertura de nossos testes para garantir que todos os cen\u00e1rios poss\u00edveis sejam tratados corretamente. Vamos come\u00e7ar! "},{"location":"06/#criando-routers","title":"Criando Routers","text":"O FastAPI oferece uma ferramenta poderosa conhecida como routers, que facilita a organiza\u00e7\u00e3o e agrupamento de diferentes rotas em uma aplica\u00e7\u00e3o. Pense em um router como um \"subaplicativo\" do FastAPI que pode ser integrado em uma aplica\u00e7\u00e3o principal. Isso n\u00e3o s\u00f3 mant\u00e9m o c\u00f3digo organizado e leg\u00edvel, mas tamb\u00e9m se mostra especialmente \u00fatil \u00e0 medida que a aplica\u00e7\u00e3o se expande e novas rotas s\u00e3o adicionadas. Esse tipo de organiza\u00e7\u00e3o nos oferece diversos benef\u00edcios:
Vamos iniciar criando uma nova estrutura de diret\u00f3rios chamada Esta organiza\u00e7\u00e3o facilita a expans\u00e3o do seu projeto e a manuten\u00e7\u00e3o de uma estrutura clara. "},{"location":"06/#implementando-um-router-para-usuarios","title":"Implementando um Router para Usu\u00e1rios","text":"No arquivo Com essa simples configura\u00e7\u00e3o, estamos prontos para definir rotas espec\u00edficas para usu\u00e1rios neste router, em vez de sobrecarregar o aplicativo principal. Utilizamos Desta forma podemos migrar todos os nossos imports e nossas fun\u00e7\u00f5es de endpoints para o arquivo Com o prefixo definido no router, os paths dos endpoints se tornam mais simples e diretos. Ao inv\u00e9s de '/users/{user_id}', por exemplo, usamos apenas '/{user_id}'. Exemplo do arquivofast_zero/routes/users.py completo fast_zero/routes/users.py Por termos criados as tags, isso reflete na organiza\u00e7\u00e3o do swagger "},{"location":"06/#criando-um-router-para-auth","title":"Criando um router para Auth","text":"No momento, temos rotas para O router para autentica\u00e7\u00e3o ser\u00e1 criado no arquivo Neste bloco de c\u00f3digo, n\u00f3s criamos um novo router que lidar\u00e1 exclusivamente com a rota de obten\u00e7\u00e3o de token ( \u00c9 crucial abordar um aspecto relacionado \u00e0 modifica\u00e7\u00e3o do router: o uso do par\u00e2metro Esse problema fica evidente ao clicar no bot\u00e3o Percebe-se que o caminho para a autoriza\u00e7\u00e3o est\u00e1 incorreto. Como consequ\u00eancia, ao tentar autenticar atrav\u00e9s do Swagger, nos deparamos com um erro na interface: No entanto, o erro n\u00e3o \u00e9 suficientemente descritivo para identificarmos a origem do problema, retornando apenas uma mensagem gen\u00e9rica de A solu\u00e7\u00e3o para este problema \u00e9 relativamente simples. Precisamos ajustar o par\u00e2metro Ap\u00f3s essa altera\u00e7\u00e3o, ao utilizar o Swagger, a autoriza\u00e7\u00e3o ser\u00e1 direcionada corretamente para o endpoint apropriado. "},{"location":"06/#plugando-as-rotas-em-app","title":"Plugando as rotas em app","text":"O FastAPI oferece uma maneira f\u00e1cil e direta de incluir routers em nossa aplica\u00e7\u00e3o principal. Isso nos permite organizar nossos endpoints de maneira eficiente e manter nosso arquivo Para incluir os routers em nossa aplica\u00e7\u00e3o principal, precisamos import\u00e1-los e usar a fun\u00e7\u00e3o Como voc\u00ea pode ver, nosso arquivo Depois de refatorar nosso c\u00f3digo, \u00e9 crucial verificar se tudo ainda est\u00e1 funcionando como esperado. Para isso, executamos nossos testes novamente. $ Execu\u00e7\u00e3o no terminal! Como voc\u00ea pode ver, todos os testes passaram. Isso significa que as altera\u00e7\u00f5es que fizemos no nosso c\u00f3digo n\u00e3o afetaram o funcionamento do nosso aplicativo. O router manteve todos os endpoints nas mesmas rotas, garantindo a continuidade do comportamento esperado. Agora, para melhor alinhar nossos testes com a nova estrutura do nosso c\u00f3digo, devemos reorganizar os arquivos de teste de acordo. Ou seja, tamb\u00e9m devemos criar arquivos de teste espec\u00edficos para cada router, em vez de manter todos os testes no arquivo Para acompanhar a nova estrutura routers, podemos desacoplar os testes do m\u00f3dulo
Vamos adaptar os testes para se encaixarem nessa nova estrutura. "},{"location":"06/#ajustando-os-testes-para-auth","title":"Ajustando os testes para Auth","text":"Vamos come\u00e7ar criando o arquivo \u00c9 importante notar que com a cria\u00e7\u00e3o do router usando Em seguida, vamos mover os testes relacionados ao dom\u00ednio do usu\u00e1rio para o arquivo Para a constru\u00e7\u00e3o desse arquivo, nenhum teste foi modificado. Eles foram somente movidos para o dom\u00ednio espec\u00edfico do router. Importante, por\u00e9m, notar que alguns destes testes usam a fixture token ","text":"A altera\u00e7\u00e3o da fixture de Fazendo assim com que os testes que dependem dessa fixture passem a funcionar. "},{"location":"06/#executando-os-testes_1","title":"Executando os testes","text":"Ap\u00f3s essa reestrutura\u00e7\u00e3o, \u00e9 importante garantir que tudo ainda est\u00e1 funcionando corretamente. Vamos executar os testes novamente para confirmar isso. $ Execu\u00e7\u00e3o no terminal! Como podemos ver, todos os testes continuam passando com sucesso, mesmo ap\u00f3s terem sido movidos para arquivos diferentes. Isso \u00e9 uma confirma\u00e7\u00e3o de que nossa reestrutura\u00e7\u00e3o foi bem-sucedida e que nossa aplica\u00e7\u00e3o continua funcionando como esperado. "},{"location":"06/#refinando-a-definicao-de-rotas-com-annotated","title":"Refinando a Defini\u00e7\u00e3o de Rotas com Annotated","text":"O FastAPI suporta um recurso fascinante da biblioteca nativa Ao definir uma anota\u00e7\u00e3o de tipo, seguimos a seguinte formata\u00e7\u00e3o: O tipo Veja o exemplo a seguir: fast_zero/routes/users.py Desse modo, conseguimos refinar a defini\u00e7\u00e3o dos endpoints para que se tornem mais concisos, sem alterar seu funcionamento: fast_zero/routes/users.py Da mesma forma, podemos otimizar o roteador de autentica\u00e7\u00e3o: fast_zero/routers/auth.py Atrav\u00e9s do uso de tipos Conforme mencionamos na aula sobre os 12 fatores, \u00e9 uma boa pr\u00e1tica manter as constantes que podem mudar dependendo do ambiente em vari\u00e1veis de ambiente. Isso torna o seu projeto mais seguro e modular, pois voc\u00ea pode alterar essas constantes sem ter que modificar o c\u00f3digo-fonte. Por exemplo, temos estas constantes em nosso m\u00f3dulo Estes valores n\u00e3o devem estar diretamente no c\u00f3digo-fonte, ent\u00e3o vamos mov\u00ea-los para nossas vari\u00e1veis de ambiente e represent\u00e1-los na nossa classe J\u00e1 temos uma classe ideal para fazer isso em Agora, precisamos adicionar estes valores ao nosso arquivo Com isso, podemos alterar o nosso c\u00f3digo em Primeiramente, vamos carregar as configura\u00e7\u00f5es da classe Com isso, todos os lugares onde as constantes eram usadas devem ser substitu\u00eddos por Desta forma, eliminamos todas as constantes do c\u00f3digo-fonte e passamos a usar as configura\u00e7\u00f5es a partir da classe Depois de todas essas mudan\u00e7as, \u00e9 muito importante garantir que tudo ainda est\u00e1 funcionando corretamente. Para isso, vamos rodar todos os testes que temos at\u00e9 agora. $ Execu\u00e7\u00e3o no terminal! Se tudo estiver certo, todos os testes devem passar. Lembre-se de que a refatora\u00e7\u00e3o n\u00e3o deve alterar a funcionalidade do nosso c\u00f3digo - apenas torn\u00e1-lo mais f\u00e1cil de ler e manter. "},{"location":"06/#commit","title":"Commit","text":"Para finalizar, vamos criar um commit para registrar todas as altera\u00e7\u00f5es que fizemos na nossa aplica\u00e7\u00e3o. Como essa \u00e9 uma grande mudan\u00e7a que envolve reestruturar a forma como lidamos com as rotas e mover as constantes para vari\u00e1veis de ambiente, podemos usar uma mensagem de commit descritiva que explique todas as principais altera\u00e7\u00f5es: $ Execu\u00e7\u00e3o no terminal! "},{"location":"06/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, vimos como refatorar a estrutura do nosso projeto FastAPI para torn\u00e1-lo mais manuten\u00edvel. Organizamos nosso c\u00f3digo em diferentes arquivos e usamos o sistema de roteadores do FastAPI para separar diferentes partes da nossa API. Tamb\u00e9m mudamos algumas constantes para o arquivo de configura\u00e7\u00e3o, tornando nosso c\u00f3digo mais seguro e flex\u00edvel. Finalmente, atualizamos nossos testes para refletir a nova estrutura do projeto. Refatorar \u00e9 um processo cont\u00ednuo - sempre h\u00e1 espa\u00e7o para melhorias. No entanto, com a estrutura que estabelecemos hoje, estamos em uma boa posi\u00e7\u00e3o para continuar a expandir nossa API no futuro. Na pr\u00f3xima aula, vamos explorar mais sobre autentica\u00e7\u00e3o e como gerenciar tokens de acesso e de atualiza\u00e7\u00e3o em nossa API FastAPI. Fique ligado! "},{"location":"07/","title":"Tornando o sistema de autentica\u00e7\u00e3o robusto","text":""},{"location":"07/#tornando-o-sistema-de-autenticacao-robusto","title":"Tornando o sistema de autentica\u00e7\u00e3o robusto","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Na aula de hoje, vamos aprofundar nosso sistema de autentica\u00e7\u00e3o. J\u00e1 vimos em aulas anteriores como criar um sistema de autentica\u00e7\u00e3o b\u00e1sico, mas h\u00e1 muitas \u00e1reas em que podemos torn\u00e1-lo mais robusto. Por exemplo, como podemos lidar com situa\u00e7\u00f5es em que as coisas d\u00e3o errado? Como podemos garantir que nosso sistema seja seguro mesmo em cen\u00e1rios adversos? Essas s\u00e3o algumas das quest\u00f5es que vamos explorar hoje. Vamos come\u00e7ar examinando mais de perto os testes para autentica\u00e7\u00e3o. At\u00e9 agora, s\u00f3 testamos os casos que d\u00e3o certo - ou seja, quando o usu\u00e1rio sempre existe. Mas \u00e9 igualmente importante testar o que acontece quando as coisas d\u00e3o errado. Afinal, n\u00e3o podemos simplesmente assumir que tudo sempre vai correr bem. Por isso, vamos aprender como testar esses casos negativos. Em seguida, vamos implementar um recurso importante em qualquer sistema de autentica\u00e7\u00e3o: o refresh do token. Isso nos permite manter a sess\u00e3o do usu\u00e1rio ativa, mesmo se o token original expirar. "},{"location":"07/#testes-para-autenticacao","title":"Testes para autentica\u00e7\u00e3o","text":"Antes de mergulharmos nos testes, vamos falar um pouco sobre por que eles s\u00e3o t\u00e3o importantes. Na programa\u00e7\u00e3o, \u00e9 f\u00e1cil cair na armadilha de pensar que, se algo funciona na maioria das vezes, ent\u00e3o est\u00e1 tudo bem. Mas a verdade \u00e9 que \u00e9 nos casos marginais que os bugs mais dif\u00edceis de encontrar e corrigir costumam se esconder. Por exemplo, o que acontece se tentarmos autenticar um usu\u00e1rio que n\u00e3o existe? Ou se tentarmos autenticar com as credenciais erradas? Se n\u00e3o testarmos esses cen\u00e1rios, podemos acabar com um sistema que parece funcionar na superf\u00edcie, mas que na verdade est\u00e1 cheio de falhas de seguran\u00e7a. No c\u00f3digo apresentado, se observarmos atentamente, vemos que o erro Essa lacuna em nossos testes representa um risco potencial, pois n\u00e3o estamos verificando como nosso sistema se comporta quando algu\u00e9m tenta, por exemplo, alterar os detalhes de um usu\u00e1rio sem ter permiss\u00f5es adequadas. Embora possamos assumir que nosso sistema se comportar\u00e1 corretamente, a falta de testes nos deixa sem uma confirma\u00e7\u00e3o concreta. "},{"location":"07/#testando-a-alteracao-de-um-usuario-nao-autorizado","title":"Testando a altera\u00e7\u00e3o de um usu\u00e1rio n\u00e3o autorizado","text":"Agora, vamos come\u00e7ar a escrever alguns testes para esses casos. Vamos come\u00e7ar com um cen\u00e1rio simples: o que acontece quando um usu\u00e1rio tenta alterar as informa\u00e7\u00f5es de outro usu\u00e1rio? Para testar isso, vamos criar um novo teste chamado test_update_user_with_wrong_user. tests/test_users.py Este teste vai simular um usu\u00e1rio tentando alterar as informa\u00e7\u00f5es de outro usu\u00e1rio. Se nosso sistema estiver funcionando corretamente, ele dever\u00e1 rejeitar essa tentativa e retornar um erro."},{"location":"07/#criando-modelos-por-demanda-com-factory-boy","title":"Criando modelos por demanda com factory-boy","text":"Embora o teste que escrevemos esteja tecnicamente correto, ele ainda n\u00e3o funcionar\u00e1 adequadamente porque, atualmente, s\u00f3 temos um usu\u00e1rio em nosso banco de dados de testes. Precisamos de uma maneira de criar m\u00faltiplos usu\u00e1rios de teste facilmente, e \u00e9 a\u00ed que entra o O Para come\u00e7ar, precisamos instalar o Depois de instalar o Explicando linha a linha, esse c\u00f3digo faz o seguinte:
Essa f\u00e1brica pode ser usada em testes para criar inst\u00e2ncias de A seguir, podemos usar essa nova f\u00e1brica para criar m\u00faltiplos usu\u00e1rios de teste. Para fazer isso, modificamos nossa fixture de usu\u00e1rio existente para usar a UserFactory. Assim, sempre que executarmos nossos testes, teremos usu\u00e1rios diferentes dispon\u00edveis. tests/conftest.py A cria\u00e7\u00e3o de outra fixture chamada Um aspecto interessante no uso das f\u00e1bricas \u00e9 que, sempre que forem chamadas, elas retornar\u00e3o um novo Com essa nova configura\u00e7\u00e3o, podemos finalmente testar o cen\u00e1rio de um usu\u00e1rio tentando alterar as informa\u00e7\u00f5es de outro usu\u00e1rio. E como voc\u00ea pode ver, nossos testes passaram com sucesso, o que indica que nosso sistema est\u00e1 lidando corretamente com essa situa\u00e7\u00e3o. tests/test_users.py Neste caso, n\u00e3o estamos usando a fixture Para enfatizar, a fixture Com o teste implementado, vamos execut\u00e1-lo para ver se nosso sistema est\u00e1 protegido contra essa a\u00e7\u00e3o indevida: $ Execu\u00e7\u00e3o no terminal! Se todos os testes passaram com sucesso, isso indica que nosso sistema est\u00e1 se comportando como esperado, inclusive no caso de tentativas indevidas de deletar um usu\u00e1rio. "},{"location":"07/#testando-o-delete-com-o-usuario-errado","title":"Testando o DELETE com o usu\u00e1rio errado","text":"Continuando nossos testes, agora vamos testar o que acontece quando tentamos deletar um usu\u00e1rio com um usu\u00e1rio errado. Talvez voc\u00ea esteja se perguntando, por que precisamos fazer isso? Bem, lembre-se de que a seguran\u00e7a \u00e9 uma parte crucial de qualquer sistema de autentica\u00e7\u00e3o. Precisamos garantir que um usu\u00e1rio n\u00e3o possa deletar a conta de outro usu\u00e1rio - apenas a pr\u00f3pria conta. Portanto, \u00e9 importante que testemos esse cen\u00e1rio para garantir que nosso sistema est\u00e1 seguro. Aqui est\u00e1 o teste que vamos usar: tests/test_users.py Como voc\u00ea pode ver, esse teste tenta deletar o user de um id diferente usando o token do user. Se nosso sistema estiver funcionando corretamente, ele dever\u00e1 rejeitar essa tentativa e retornar um status 400 com uma mensagem de erro indicando que o usu\u00e1rio n\u00e3o tem permiss\u00f5es suficientes para realizar essa a\u00e7\u00e3o. Vamos executar esse teste agora e ver o que acontece: $ Execu\u00e7\u00e3o no terminal! \u00d3timo, nosso teste passou! Isso significa que nosso sistema est\u00e1 corretamente impedindo um usu\u00e1rio de deletar a conta de outro usu\u00e1rio. Agora que terminamos de testar a autoriza\u00e7\u00e3o, vamos passar para o pr\u00f3ximo desafio: testar tokens expirados. Lembre-se, em um sistema de autentica\u00e7\u00e3o robusto, um token deve expirar ap\u00f3s um certo per\u00edodo de tempo por motivos de seguran\u00e7a. Portanto, \u00e9 importante que testemos o que acontece quando tentamos usar um token expirado. Vamos ver isso na pr\u00f3xima se\u00e7\u00e3o. "},{"location":"07/#testando-a-expiracao-do-token","title":"Testando a expira\u00e7\u00e3o do token","text":"Continuando com nossos testes de autentica\u00e7\u00e3o, a pr\u00f3xima coisa que precisamos testar \u00e9 a expira\u00e7\u00e3o do token. Tokens de autentica\u00e7\u00e3o s\u00e3o normalmente projetados para expirar ap\u00f3s um certo per\u00edodo de tempo por motivos de seguran\u00e7a. Isso evita que algu\u00e9m que tenha obtido um token possa us\u00e1-lo indefinidamente se ele for roubado ou perdido. Portanto, \u00e9 importante que verifiquemos que nosso sistema esteja tratando corretamente a expira\u00e7\u00e3o dos tokens. Para realizar esse teste, vamos usar uma biblioteca chamada Primeiro, vamos precisar instalar a biblioteca: Agora vamos criar nosso teste. Vamos come\u00e7ar pegando um token para um usu\u00e1rio, congelando o tempo, esperando pelo tempo de expira\u00e7\u00e3o do token e, em seguida, tentando usar o token para acessar um endpoint que requer autentica\u00e7\u00e3o. Ao elaborarmos o teste, usaremos a funcionalidade de congelamento de tempo do Lembre-se de que configuramos nosso token para expirar ap\u00f3s 30 minutos. Portanto, n\u00f3s avan\u00e7amos o tempo em 31 minutos para garantir que o token tenha expirado. Agora, vamos executar nosso teste e ver o que acontece: $ Execu\u00e7\u00e3o no terminal! \u00d3timo, nosso teste passou! Isso confirma que nosso sistema est\u00e1 lidando corretamente com a expira\u00e7\u00e3o dos tokens. No entanto, ainda h\u00e1 uma coisa que precisamos implementar: a atualiza\u00e7\u00e3o de tokens. Atualmente, quando um token expira, o usu\u00e1rio teria que fazer login novamente para obter um novo token. Isso n\u00e3o \u00e9 uma \u00f3tima experi\u00eancia para o usu\u00e1rio. Em vez disso, gostar\u00edamos de oferecer a possibilidade de o usu\u00e1rio atualizar seu token quando ele estiver prestes a expirar. Vamos ver como fazer isso na pr\u00f3xima se\u00e7\u00e3o. "},{"location":"07/#testando-o-usuario-nao-existente-e-senha-incorreta","title":"Testando o usu\u00e1rio n\u00e3o existente e senha incorreta","text":"Na constru\u00e7\u00e3o de qualquer sistema de autentica\u00e7\u00e3o, \u00e9 crucial garantir que os casos de erro sejam tratados corretamente. Isso n\u00e3o s\u00f3 previne poss\u00edveis falhas de seguran\u00e7a, mas tamb\u00e9m permite fornecer feedback \u00fatil aos usu\u00e1rios. Em nossa implementa\u00e7\u00e3o atual, temos duas situa\u00e7\u00f5es espec\u00edficas que devem retornar um erro: quando um usu\u00e1rio inexistente tenta fazer login e quando uma senha incorreta \u00e9 fornecida. Vamos abordar esses casos de erro em nossos pr\u00f3ximos testes. Embora possa parecer redundante testar esses casos j\u00e1 que ambos resultam no mesmo erro, \u00e9 importante verificar que ambos os cen\u00e1rios est\u00e3o corretamente tratados. Isso nos permitir\u00e1 manter a robustez do nosso sistema conforme ele evolui e muda ao longo do tempo. "},{"location":"07/#testando-a-excecao-para-um-usuario-inexistente","title":"Testando a exce\u00e7\u00e3o para um usu\u00e1rio inexistente","text":"Para este cen\u00e1rio, precisamos enviar um request para o endpoint de token com um e-mail que n\u00e3o existe no banco de dados. A resposta esperada \u00e9 um HTTP 400 com a mensagem de detalhe 'Incorrect email or password'. tests/test_auth.py "},{"location":"07/#testando-a-excecao-para-uma-senha-incorreta","title":"Testando a exce\u00e7\u00e3o para uma senha incorreta","text":"Aqui, precisamos enviar um request para o endpoint de token com uma senha incorreta para um usu\u00e1rio existente. A resposta esperada \u00e9 um HTTP 400 com a mensagem de detalhe 'Incorrect email or password'. tests/test_auth.py Com esses testes, garantimos que nossas exce\u00e7\u00f5es est\u00e3o sendo lan\u00e7adas corretamente. Essa \u00e9 uma parte importante da constru\u00e7\u00e3o de um sistema de autentica\u00e7\u00e3o robusto, pois nos permite ter confian\u00e7a de que estamos tratando corretamente os casos de erro. "},{"location":"07/#implementando-o-refresh-do-token","title":"Implementando o refresh do token","text":"O processo de renova\u00e7\u00e3o de token \u00e9 uma parte essencial na implementa\u00e7\u00e3o de autentica\u00e7\u00e3o JWT. Em muitos sistemas, por raz\u00f5es de seguran\u00e7a, os tokens de acesso t\u00eam um tempo de vida relativamente curto. Isso significa que eles expiram ap\u00f3s um determinado per\u00edodo de tempo, e quando isso acontece, o cliente precisa obter um novo token para continuar acessando os recursos do servidor. Aqui \u00e9 onde o processo de renova\u00e7\u00e3o de token entra: permite que um cliente obtenha um novo token de acesso sem a necessidade de autentica\u00e7\u00e3o completa (por exemplo, sem ter que fornecer novamente o nome de usu\u00e1rio e senha). Agora vamos implementar a fun\u00e7\u00e3o de renova\u00e7\u00e3o de token em nosso c\u00f3digo. fast_zero/routes/auth.py Vamos tamb\u00e9m implementar um teste para verificar se a fun\u00e7\u00e3o de renova\u00e7\u00e3o de token est\u00e1 funcionando corretamente. tests/test_auth.py Ainda \u00e9 importante garantir que nosso sistema trate corretamente os tokens expirados. Para isso, vamos adicionar um teste que verifica se um token expirado n\u00e3o pode ser usado para renovar um token. tests/test_auth.py Agora, se executarmos nossos testes, todos eles devem passar, incluindo os novos testes que acabamos de adicionar. $ Execu\u00e7\u00e3o no terminal! Com esses testes, podemos ter certeza de que cobrimos alguns casos importantes relacionados \u00e0 autentica\u00e7\u00e3o de usu\u00e1rios em nossa API. "},{"location":"07/#commit","title":"Commit","text":"Agora, vamos fazer um commit com as altera\u00e7\u00f5es que fizemos. $ Execu\u00e7\u00e3o no terminal! "},{"location":"07/#conclusao","title":"Conclus\u00e3o","text":"Nesta aula, abordamos uma grande quantidade de t\u00f3picos cruciais para a constru\u00e7\u00e3o de uma aplica\u00e7\u00e3o web segura e robusta. Come\u00e7amos com a implementa\u00e7\u00e3o da funcionalidade de renova\u00e7\u00e3o do token JWT, uma pe\u00e7a fundamental na arquitetura de autentica\u00e7\u00e3o baseada em token. Este processo garante que os usu\u00e1rios possam continuar acessando a aplica\u00e7\u00e3o, mesmo ap\u00f3s o token inicial ter expirado, sem a necessidade de fornecer suas credenciais novamente. Por\u00e9m, a implementa\u00e7\u00e3o do c\u00f3digo foi apenas a primeira parte do que fizemos. Uma parte significativa da nossa aula foi dedicada a testar de maneira exaustiva a nossa aplica\u00e7\u00e3o. Escrevemos testes para verificar o comportamento b\u00e1sico das nossas rotas de autentica\u00e7\u00e3o, mas n\u00e3o paramos por a\u00ed. Tamb\u00e9m consideramos v\u00e1rios casos de borda que podem surgir durante a autentica\u00e7\u00e3o de um usu\u00e1rio. Testamos, por exemplo, o que acontece quando se tenta obter um token com credenciais incorretas. Verificamos o comportamento da nossa aplica\u00e7\u00e3o quando um token expirado \u00e9 utilizado. Esses testes nos ajudam a garantir que nossa aplica\u00e7\u00e3o se comporte de maneira adequada n\u00e3o apenas nas situa\u00e7\u00f5es mais comuns, mas tamb\u00e9m quando algo sai do esperado. Al\u00e9m disso, ao implementar esses testes, n\u00f3s garantimos que futuras altera\u00e7\u00f5es no nosso c\u00f3digo n\u00e3o ir\u00e3o quebrar funcionalidades j\u00e1 existentes. Testes automatizados s\u00e3o uma parte fundamental de qualquer aplica\u00e7\u00e3o de alta qualidade, e o que fizemos hoje vai al\u00e9m do b\u00e1sico, mostrando como lidar com cen\u00e1rios complexos e realistas. A implementa\u00e7\u00e3o e os testes que fizemos hoje nos levam um passo adiante no desenvolvimento da nossa aplica\u00e7\u00e3o, deixando-a mais pr\u00f3xima de estar pronta para um ambiente de produ\u00e7\u00e3o. Na pr\u00f3xima aula, vamos utilizar a infraestrutura de autentica\u00e7\u00e3o que criamos hoje para permitir que os usu\u00e1rios criem, leiam, atualizem e deletem suas pr\u00f3prias listas de tarefas. Isso vai nos permitir explorar ainda mais as funcionalidades do FastAPI e do SQLAlchemy, al\u00e9m de continuar a expandir a nossa su\u00edte de testes. Esperamos ver voc\u00ea na pr\u00f3xima aula! "},{"location":"08/","title":"Criando Rotas CRUD para Gerenciamento de Tarefas em FastAPI","text":""},{"location":"08/#criando-rotas-crud-para-gerenciamento-de-tarefas-em-fastapi","title":"Criando Rotas CRUD para Gerenciamento de Tarefas em FastAPI","text":"Objetivos da Aula:
Aula Slides C\u00f3digo Ol\u00e1 a todos! Estamos de volta com mais uma aula. Hoje vamos mergulhar na cria\u00e7\u00e3o das rotas CRUD para as nossas tarefas utilizando FastAPI. Essas opera\u00e7\u00f5es s\u00e3o fundamentais para qualquer aplica\u00e7\u00e3o de gerenciamento de tarefas e s\u00e3o o cora\u00e7\u00e3o do nosso sistema. Al\u00e9m disso, garantiremos que apenas o usu\u00e1rio que criou a tarefa possa acess\u00e1-la e modific\u00e1-la, garantindo a seguran\u00e7a e a privacidade dos dados. Vamos come\u00e7ar! "},{"location":"08/#estrutura-inicial-do-codigo","title":"Estrutura inicial do c\u00f3digo","text":"Primeiro, vamos criar um novo arquivo chamado Neste c\u00f3digo, criamos uma nova inst\u00e2ncia da classe A op\u00e7\u00e3o A op\u00e7\u00e3o Depois de definir o roteador, precisamos inclu\u00ed-lo em nossa aplica\u00e7\u00e3o principal. Vamos atualizar o arquivo Neste c\u00f3digo, chamamos o m\u00e9todo Agora, iremos implementar a tabela 'Todos' no nosso banco de dados. Esta tabela estar\u00e1 diretamente relacionada \u00e0 tabela 'User', pois toda tarefa pertence a um usu\u00e1rio. Esta rela\u00e7\u00e3o \u00e9 crucial para garantir que s\u00f3 o usu\u00e1rio dono da tarefa possa acessar e modificar suas tarefas. fast_zero/models.py Neste ponto, \u00e9 importante compreender o conceito de O uso do tipo Enum em Estes conceitos podem parecer um pouco complexos agora, mas ficar\u00e3o mais claros quando come\u00e7armos a implementar os testes. "},{"location":"08/#testando-as-novas-implementacoes-do-banco-de-dados","title":"Testando as novas implementa\u00e7\u00f5es do banco de dados","text":"Embora tenhamos 100% de cobertura de c\u00f3digo, isso n\u00e3o garante que tudo esteja funcionando corretamente. S\u00f3 implementamos a estrutura do banco de dados, mas n\u00e3o testamos a l\u00f3gica de como as tabelas e as rela\u00e7\u00f5es funcionam na pr\u00e1tica. Para isso, criamos um teste para verificar se a rela\u00e7\u00e3o entre tarefas e usu\u00e1rios est\u00e1 funcionando corretamente. Este teste cria uma nova tarefa para um usu\u00e1rio e verifica se essa tarefa aparece na lista de tarefas desse usu\u00e1rio. tests/test_db.py Com isso, voc\u00ea pode executar os testes: $ Execu\u00e7\u00e3o no terminal! Isso mostra que os testes foram bem-sucedidos. Mesmo sem testes mais extensivos, agora vamos come\u00e7ar a criar os esquemas para esse modelo e, em seguida, os endpoints. "},{"location":"08/#schemas-para-todos","title":"Schemas para Todos","text":"Vamos criar dois esquemas para nosso modelo de tarefas (todos):
Criamos o primeiro endpoint para a cria\u00e7\u00e3o de tarefas. Este \u00e9 um endpoint POST na rota '/todos'. \u00c9 importante destacar que, para criar uma tarefa, um usu\u00e1rio precisa estar autenticado e s\u00f3 esse usu\u00e1rio autenticado ser\u00e1 o propriet\u00e1rio da tarefa. fast_zero/routes/todos.py Neste endpoint, fazemos uso da depend\u00eancia Para garantir que nosso endpoint est\u00e1 funcionando corretamente, criamos um teste para ele. Este teste verifica se o endpoint '/todos' est\u00e1 criando tarefas corretamente. tests/test_todos.py No teste, fazemos uma requisi\u00e7\u00e3o POST para o endpoint '/todos' passando um token de autentica\u00e7\u00e3o v\u00e1lido e um JSON com os dados da tarefa a ser criada. Em seguida, verificamos se a resposta cont\u00e9m os dados corretos da tarefa criada. Para executar este teste, voc\u00ea deve usar o comando abaixo no terminal: $ Execu\u00e7\u00e3o no terminal! Com essa implementa\u00e7\u00e3o, os testes devem passar. Por\u00e9m, apesar do sucesso dos testes, nosso c\u00f3digo ainda n\u00e3o est\u00e1 completamente pronto. Ainda \u00e9 necess\u00e1rio criar uma migra\u00e7\u00e3o para a tabela de tarefas no banco de dados. "},{"location":"08/#criando-a-migracao-da-nova-tabela","title":"Criando a migra\u00e7\u00e3o da nova tabela","text":"Agora que temos nosso modelo de tarefas definido, precisamos criar uma migra\u00e7\u00e3o para adicionar a tabela de tarefas ao nosso banco de dados. Usamos o Alembic para criar e gerenciar nossas migra\u00e7\u00f5es. $ Execu\u00e7\u00e3o no terminal! Este comando gera um arquivo de migra\u00e7\u00e3o, que se parece com o c\u00f3digo abaixo: migrations/versions/de865434f506_create_todos_table.py Depois que a migra\u00e7\u00e3o for criada, precisamos aplic\u00e1-la ao nosso banco de dados. Execute o comando Agora que a migra\u00e7\u00e3o foi aplicada, nosso banco de dados deve ter uma nova tabela de tarefas. Para verificar, voc\u00ea pode abrir o banco de dados com o comando Finalmente, agora que temos a tabela de tarefas em nosso banco de dados, podemos testar nosso endpoint de cria\u00e7\u00e3o de tarefas no Swagger. Para fazer isso, execute nosso servidor FastAPI e abra o Swagger no seu navegador. "},{"location":"08/#endpoint-de-listagem","title":"Endpoint de listagem","text":"Agora que criamos a nossa migra\u00e7\u00e3o e temos o endpoint de cria\u00e7\u00e3o de Todos, temos que criar nosso endpoint de listagem de tarefas. Ele deve listar todas as tarefas de acordo com o Algumas coisas adicionais e que podem ser importantes na hora de recuperar as tarefas \u00e9 fazer um filtro de busca. Em alguns momentos queremos buscar uma tarefa por t\u00edtulo, em outro momento por descri\u00e7\u00e3o, \u00e0s vezes s\u00f3 pelo estado. Por exemplo, somente tarefas conclu\u00eddas. Para fazer isso, podemos contar com um recurso do FastAPI chamado Por exemplo, uma query string simples pode ser: Uma caracter\u00edstica importante das queries do FastAPI \u00e9 que podemos juntar mais de um atributo em uma busca. Por exemplo, podemos buscar somente as tarefas a fazer que contenham no t\u00edtulo \"trabalho\". Dessa forma, temos um endpoint mais eficiente, j\u00e1 que podemos realizar buscas complexas e refinadas com uma \u00fanica chamada. A combina\u00e7\u00e3o poderia ser algo como: A combina\u00e7\u00e3o de diferentes par\u00e2metros de query n\u00e3o s\u00f3 torna o endpoint mais flex\u00edvel, mas tamb\u00e9m permite que os usu\u00e1rios obtenham os dados de que precisam de maneira mais r\u00e1pida e conveniente. Isso contribui para uma melhor experi\u00eancia do usu\u00e1rio e otimiza a intera\u00e7\u00e3o com o banco de dados. O c\u00f3digo a seguir ilustra como o endpoint de listagem \u00e9 definido utilizando a Essa abordagem equilibra a flexibilidade e a efici\u00eancia, tornando o endpoint capaz de atender a uma variedade de necessidades de neg\u00f3cio. Utilizando os recursos do FastAPI, conseguimos implementar uma solu\u00e7\u00e3o robusta e f\u00e1cil de manter, que ser\u00e1 testada posteriormente para garantir sua funcionalidade e integridade. No c\u00f3digo acima, estamos utilizando filtros do SQLAlchemy, uma biblioteca ORM (Object-Relational Mapping) do Python, para adicionar condi\u00e7\u00f5es \u00e0 nossa consulta. Esses filtros correspondem aos par\u00e2metros que o usu\u00e1rio pode passar na URL.
Essas condi\u00e7\u00f5es s\u00e3o traduzidas em cl\u00e1usulas SQL pelo SQLAlchemy, permitindo que o banco de dados filtre os resultados de acordo com os crit\u00e9rios especificados pelo usu\u00e1rio. Essa integra\u00e7\u00e3o entre FastAPI e SQLAlchemy torna o processo de filtragem eficiente e a codifica\u00e7\u00e3o mais expressiva e clara. "},{"location":"08/#criando-uma-factory-para-simplificar-os-testes","title":"Criando uma factory para simplificar os testes","text":"Criar uma factory para o endpoint facilitaria os testes por diversas raz\u00f5es, especialmente quando se trata de testar o nosso endpoint de listagem que faz uso de m\u00faltiplas queries. Primeiro, a factory ajuda a encapsular a l\u00f3gica de cria\u00e7\u00e3o dos objetos necess\u00e1rios para o teste, como no caso dos objetos Com a complexidade das queries que nosso endpoint permite, precisamos cobrir todos os usos poss\u00edveis dessas queries. A factory vai nos ajudar a criar muitos casos de testes de forma pr\u00e1tica e eficiente, j\u00e1 que podemos gerar diferentes combina\u00e7\u00f5es de t\u00edtulos, descri\u00e7\u00f5es, estados, entre outros atributos, simulando diversas situa\u00e7\u00f5es de uso. Al\u00e9m disso, ao utilizar bibliotecas como o A fixture acima pode ser usada em diversos testes, reduzindo a duplica\u00e7\u00e3o de c\u00f3digo e melhorando a manuten\u00e7\u00e3o. Por exemplo, em um teste que precisa criar v\u00e1rios objetos A utiliza\u00e7\u00e3o de f\u00e1bricas tamb\u00e9m promove uma melhor separa\u00e7\u00e3o entre a l\u00f3gica de cria\u00e7\u00e3o do objeto e a l\u00f3gica do teste, tornando os testes mais leg\u00edveis e f\u00e1ceis de seguir. Com a Ao trabalhar com o endpoint de listagem de tarefas, temos v\u00e1rias varia\u00e7\u00f5es de query strings que precisam ser testadas. Cada uma dessas varia\u00e7\u00f5es representa um caso de uso diferente, e queremos garantir que o sistema funcione corretamente em todos eles. Vamos separar os testes em pequenos blocos e explicar cada um deles. "},{"location":"08/#testando-a-listagem-de-todos","title":"Testando a Listagem de Todos","text":"Primeiro, vamos criar um teste b\u00e1sico que verifica se o endpoint est\u00e1 listando todos os objetos Este teste valida que todos os 5 objetos Em seguida, vamos testar a pagina\u00e7\u00e3o para garantir que o offset e o limite estejam funcionando corretamente. tests/test_todos.py Este teste verifica que, quando aplicado o offset de 1 e o limite de 2, apenas 2 objetos Tamb\u00e9m queremos verificar se a filtragem por t\u00edtulo est\u00e1 funcionando conforme esperado. tests/test_todos.py Este teste garante que quando o filtro de t\u00edtulo \u00e9 aplicado, apenas as tarefas com o t\u00edtulo correspondente s\u00e3o retornadas. "},{"location":"08/#testando-o-filtro-por-descricao","title":"Testando o Filtro por Descri\u00e7\u00e3o","text":"Da mesma forma, queremos testar o filtro de descri\u00e7\u00e3o. tests/test_todos.py Este teste verifica que, quando filtramos pela descri\u00e7\u00e3o, apenas as tarefas com a descri\u00e7\u00e3o correspondente s\u00e3o retornadas. "},{"location":"08/#testando-o-filtro-por-estado","title":"Testando o Filtro por Estado","text":"Finalmente, precisamos testar o filtro de estado. tests/test_todos.py Este teste garante que quando filtramos pelo estado, apenas as tarefas com o estado correspondente s\u00e3o retornadas. "},{"location":"08/#testando-a-combinacao-de-filtros-de-estado-titulo-e-descricao","title":"Testando a Combina\u00e7\u00e3o de Filtros de Estado, T\u00edtulo e Descri\u00e7\u00e3o","text":"Em nosso conjunto de testes, tamb\u00e9m \u00e9 importante verificar se o endpoint \u00e9 capaz de lidar com m\u00faltiplos par\u00e2metros de consulta simultaneamente. Para isso, vamos criar um teste que combine os filtros de estado, t\u00edtulo e descri\u00e7\u00e3o. Isso assegurar\u00e1 que, quando esses par\u00e2metros s\u00e3o usados juntos, o endpoint retornar\u00e1 apenas as tarefas que correspondem a todas essas condi\u00e7\u00f5es. Este teste \u00e9 vital para garantir que os usu\u00e1rios podem realizar buscas complexas usando v\u00e1rios crit\u00e9rios ao mesmo tempo, e que o endpoint ir\u00e1 retornar os resultados esperados. A seguir, apresento o c\u00f3digo do teste: tests/test_todos.py Com esses testes, cobrimos todas as poss\u00edveis varia\u00e7\u00f5es de query strings para o nosso endpoint, garantindo que ele funciona corretamente em todas essas situa\u00e7\u00f5es. A abordagem modular para escrever esses testes facilita a leitura e a manuten\u00e7\u00e3o, al\u00e9m de permitir uma cobertura de teste abrangente e robusta. "},{"location":"08/#executando-os-testes","title":"Executando os testes","text":"Importante para que n\u00e3o esque\u00e7amos \u00e9 de executar os testes para ver se tudo corre bem: "},{"location":"08/#endpoint-de-alteracao","title":"Endpoint de Altera\u00e7\u00e3o","text":"Para fazer a altera\u00e7\u00e3o de uma tarefa, precisamos de um modelo onde tudo seja opcional, j\u00e1 que poder\u00edamos querer atualizar apenas um ou alguns campos da tarefa. Vamos criar o esquema Para podermos alterar somente os valores que recebemos no modelo, temos que fazer um A linha O par\u00e2metro Depois de obter a chave e o valor de cada campo definido, a linha Dessa forma, garantimos que somente os campos enviados ao schema sejam atualizados no objeto. "},{"location":"08/#testes-para-o-endpoint-de-alteracao","title":"Testes para o Endpoint de Altera\u00e7\u00e3o","text":"Os testes aqui incluem o caso de atualiza\u00e7\u00e3o bem-sucedida e o caso de erro quando a tarefa n\u00e3o \u00e9 encontrada: fast_zero/tests/test_todos.py Agora precisamos executar os testes para ver se est\u00e1 tudo correto: $ Execu\u00e7\u00e3o no terminal! Com tudo funcionando, podemos partir para o nosso endpoint de DELETE. "},{"location":"08/#endpoint-de-delecao","title":"Endpoint de Dele\u00e7\u00e3o","text":"A rota para deletar uma tarefa \u00e9 simples e direta. Caso o todo exista, vamos deletar ele com a "},{"location":"08/#testes-para-o-endpoint-de-delecao","title":"Testes para o Endpoint de Dele\u00e7\u00e3o","text":"Esses testes verificam tanto a remo\u00e7\u00e3o bem-sucedida quanto o caso de erro quando a tarefa n\u00e3o \u00e9 encontrada: fast_zero/tests/test_todos.py Por fim, precisamos executar os testes para ver se est\u00e1 tudo correto: $ Execu\u00e7\u00e3o no terminal! "},{"location":"08/#commit","title":"Commit","text":"Agora que voc\u00ea finalizou a implementa\u00e7\u00e3o desses endpoints, \u00e9 um bom momento para fazer um commit das suas mudan\u00e7as. Para isso, voc\u00ea pode seguir os seguintes passos:
Nesta aula exploramos os aspectos essenciais para construir uma API completa e funcional para gerenciar tarefas, integrando-se ao sistema de autentica\u00e7\u00e3o que j\u00e1 t\u00ednhamos desenvolvido. Iniciamos criando a estrutura de banco de dados para as tarefas, incluindo tabelas e migra\u00e7\u00f5es, e em seguida definimos os schemas necess\u00e1rios. A partir da\u00ed, trabalhamos na cria\u00e7\u00e3o dos endpoints para as opera\u00e7\u00f5es CRUD: cria\u00e7\u00e3o, leitura (listagem com filtragem), atualiza\u00e7\u00e3o (edi\u00e7\u00e3o) e exclus\u00e3o (dele\u00e7\u00e3o). Em cada est\u00e1gio, focamos na qualidade e na robustez, utilizando testes rigorosos para assegurar que os endpoints se comportassem conforme esperado. Exploramos tamb\u00e9m t\u00e9cnicas espec\u00edficas como atualiza\u00e7\u00e3o parcial e filtragem avan\u00e7ada, tornando a API flex\u00edvel e poderosa. O resultado foi um sistema integrado de gerenciamento de tarefas, ou um \"todo list\", ligado aos usu\u00e1rios e \u00e0 autentica\u00e7\u00e3o que j\u00e1 hav\u00edamos implementado. Esta aula refor\u00e7ou a import\u00e2ncia de um design cuidadoso e uma implementa\u00e7\u00e3o criteriosa, ilustrando como a FastAPI pode ser usada para criar APIs eficientes e profissionais. Agora que a nossa aplica\u00e7\u00e3o est\u00e1 crescendo e ganhando mais funcionalidades, na pr\u00f3xima aula, vamos mergulhar no mundo da dockeriza\u00e7\u00e3o. Iremos aprender a colocar a nossa aplica\u00e7\u00e3o dentro de um container Docker, facilitando o deploy e o escalonamento. Este \u00e9 um passo vital no desenvolvimento moderno de aplica\u00e7\u00f5es e estou ansioso para gui\u00e1-lo atrav\u00e9s dele. At\u00e9 l\u00e1! "},{"location":"09/","title":"Dockerizando a nossa aplica\u00e7\u00e3o e introduzindo o PostgreSQL","text":""},{"location":"09/#dockerizando-a-nossa-aplicacao-e-introduzindo-o-postgresql","title":"Dockerizando a nossa aplica\u00e7\u00e3o e introduzindo o PostgreSQL","text":"Objetivos da aula:
Aula Slides C\u00f3digo Depois de implementar nosso gerenciador de tarefas na aula anterior, temos uma primeira vers\u00e3o est\u00e1vel da nossa aplica\u00e7\u00e3o. Nesta aula, al\u00e9m de aprendermos a \"dockerizar\" nossa aplica\u00e7\u00e3o FastAPI, tamb\u00e9m abordaremos a migra\u00e7\u00e3o do banco de dados SQLite para o PostgreSQL. "},{"location":"09/#o-docker-e-a-nossa-aplicacao","title":"O Docker e a nossa aplica\u00e7\u00e3o","text":"Docker \u00e9 uma plataforma aberta que permite automatizar o processo de implanta\u00e7\u00e3o, escalonamento e opera\u00e7\u00e3o de aplica\u00e7\u00f5es dentro de cont\u00eaineres. Ele serve para \"empacotar\" uma aplica\u00e7\u00e3o e suas depend\u00eancias em um cont\u00eainer virtual que pode ser executado em qualquer sistema operacional que suporte Docker. Isso facilita a implanta\u00e7\u00e3o, o desenvolvimento e o compartilhamento de aplica\u00e7\u00f5es, al\u00e9m de proporcionar um ambiente isolado e consistente. "},{"location":"09/#criando-nosso-dockerfile","title":"Criando nosso Dockerfile","text":"Para criar um container Docker, escrevemos uma lista de passos de como construir o ambiente para execu\u00e7\u00e3o da nossa aplica\u00e7\u00e3o em um arquivo chamado Uma das coisas interessantes sobre Docker \u00e9 que existe um Hub de containers prontos onde a comunidade hospeda imagens \"prontas\", que podemos usar como ponto de partida. Por exemplo, a comunidade de python mant\u00e9m um grupo de imagens com o ambiente python pronto para uso. Podemos partir dessa imagem com o python j\u00e1 instalado adicionar os passos para que nossa aplica\u00e7\u00e3o seja executada. Aqui est\u00e1 um exemplo de Aqui est\u00e1 o que cada linha faz:
Vamos entender melhor esse \u00faltimo comando:
Para criar uma imagem Docker a partir do Dockerfile, usamos o comando Este comando l\u00ea o Dockerfile no diret\u00f3rio atual (indicado pelo Vamos ent\u00e3o verificar se a imagem foi criada com sucesso usando o comando: $ Execu\u00e7\u00e3o no terminal! Este comando lista todas as imagens Docker dispon\u00edveis no seu sistema. "},{"location":"09/#executando-o-container","title":"Executando o container","text":"Para executar o cont\u00eainer, usamos o comando Este comando iniciar\u00e1 nossa aplica\u00e7\u00e3o dentro de um cont\u00eainer Docker, que estar\u00e1 escutando na porta 8000. Para testar se tudo est\u00e1 funcionando corretamente, voc\u00ea pode acessar Caso voc\u00ea fique preso no terminal Caso voc\u00ea tenha a aplica\u00e7\u00e3o travada no terminal e n\u00e3o consiga sair, voc\u00ea pode teclar Ctrl+C para parar a execu\u00e7\u00e3o do container. "},{"location":"09/#gerenciando-containers-docker","title":"Gerenciando Containers docker","text":"Quando voc\u00ea trabalha com Docker, \u00e9 importante saber como gerenciar os cont\u00eaineres. Aqui est\u00e3o algumas opera\u00e7\u00f5es b\u00e1sicas para gerenci\u00e1-los:
Ambos os comandos (stop e rm) usam o nome do cont\u00eainer que definimos anteriormente com a flag O PostgreSQL \u00e9 um Sistema de Gerenciamento de Banco de Dados Objeto-Relacional (ORDBMS) poderoso e de c\u00f3digo aberto. Ele \u00e9 amplamente utilizado em produ\u00e7\u00e3o em muitos projetos devido \u00e0 sua robustez, escalabilidade e conjunto de recursos extensos. Mudar para um banco de dados como PostgreSQL tem v\u00e1rios benef\u00edcios:
Al\u00e9m disso, SQLite tem algumas limita\u00e7\u00f5es que podem torn\u00e1-lo inadequado para produ\u00e7\u00e3o em alguns casos. Por exemplo, ele n\u00e3o suporta alta concorr\u00eancia e pode ter problemas de performance com grandes volumes de dados. Nota Embora para o escopo da nossa aplica\u00e7\u00e3o e os objetivos de aprendizado o SQLite pudesse ser suficiente, \u00e9 sempre bom nos prepararmos para cen\u00e1rios de produ\u00e7\u00e3o real. A ado\u00e7\u00e3o de PostgreSQL nos d\u00e1 uma pr\u00e9via das pr\u00e1ticas do mundo real e garante que nossa aplica\u00e7\u00e3o possa escalar sem grandes modifica\u00e7\u00f5es de infraestrutura. "},{"location":"09/#como-executar-o-postgres","title":"Como executar o postgres?","text":"Embora o PostgreSQL seja poderoso, sua instala\u00e7\u00e3o direta em uma m\u00e1quina real pode ser desafiadora e pode resultar em configura\u00e7\u00f5es diferentes entre os ambientes de desenvolvimento. Felizmente, podemos utilizar o Docker para resolver esse problema. No Docker Hub, est\u00e3o dispon\u00edveis imagens pr\u00e9-constru\u00eddas do PostgreSQL, permitindo-nos executar o PostgreSQL com um \u00fanico comando. Confira a imagem oficial do PostgreSQL. Para executar um cont\u00eainer do PostgreSQL, use o seguinte comando: $ Execu\u00e7\u00e3o no terminal! "},{"location":"09/#explicando-as-flags-e-configuracoes","title":"Explicando as Flags e Configura\u00e7\u00f5es","text":"
Esta flag \u00e9 usada para definir vari\u00e1veis de ambiente no cont\u00eainer. No contexto do PostgreSQL, essas vari\u00e1veis s\u00e3o essenciais. Elas configuram o nome de usu\u00e1rio, nome do banco de dados, e senha durante a primeira execu\u00e7\u00e3o do cont\u00eainer. Sem elas, o PostgreSQL pode n\u00e3o iniciar da forma esperada. \u00c9 uma forma pr\u00e1tica de configurar o PostgreSQL sem interagir manualmente ou criar arquivos de configura\u00e7\u00e3o.
O PostgreSQL, por padr\u00e3o, escuta por conex\u00f5es na porta Sobre as vari\u00e1veis Os valores acima ( Para garantir a persist\u00eancia dos dados entre execu\u00e7\u00f5es do cont\u00eainer, utilizamos volumes. Um volume mapeia um diret\u00f3rio do sistema host para um diret\u00f3rio no cont\u00eainer. Isso \u00e9 crucial para bancos de dados, pois sem um volume, ao remover o cont\u00eainer, todos os dados armazenados dentro dele se perderiam. No PostgreSQL, o diret\u00f3rio padr\u00e3o para armazenamento de dados \u00e9 O par\u00e2metro do volume \u00e9 passado ao cont\u00eainer usando o par\u00e2metro Para que o SQLAlchemy suporte o PostgreSQL, precisamos instalar uma depend\u00eancia chamada Para instalar essa depend\u00eancia, utilize o seguinte comando: $ Execu\u00e7\u00e3o no terminal! Uma das vantagens do SQLAlchemy enquanto ORM \u00e9 a flexibilidade. Com apenas algumas altera\u00e7\u00f5es m\u00ednimas, como a atualiza\u00e7\u00e3o da string de conex\u00e3o, podemos facilmente transicionar para um banco de dados diferente. Assim, ap\u00f3s ajustar o arquivo Para ajustar a conex\u00e3o com o PostgreSQL, modifique seu arquivo Caso tenha alterado as vari\u00e1veis de ambiente do cont\u00eainer Se voc\u00ea alterou Migra\u00e7\u00f5es s\u00e3o como vers\u00f5es para seu banco de dados, permitindo que voc\u00ea atualize sua estrutura de forma ordenada e controlada. Sempre que mudamos de banco de dados, ou at\u00e9 mesmo quando alteramos sua estrutura, as migra\u00e7\u00f5es precisam ser executadas para garantir que a base de dados esteja em sincronia com nosso c\u00f3digo. No contexto de cont\u00eaineres, rodar as migra\u00e7\u00f5es se torna ainda mais simples. Quando mudamos de banco de dados, como \u00e9 o caso de termos sa\u00eddo de um SQLite (por exemplo) para um PostgreSQL, as migra\u00e7\u00f5es s\u00e3o essenciais. O motivo \u00e9 simples: o novo banco de dados n\u00e3o ter\u00e1 a estrutura e os dados do antigo, a menos que migremos. As migra\u00e7\u00f5es ir\u00e3o garantir que o novo banco de dados tenha a mesma estrutura e rela\u00e7\u00f5es que o anterior. Antes de executar o proximo comandoAssegure-se de que ambos os cont\u00eaineres, tanto da aplica\u00e7\u00e3o quanto do banco de dados, estejam ativos. O cont\u00eainer do banco de dados deve estar rodando para que a aplica\u00e7\u00e3o possa se conectar a ele. Assegure-se de que o cont\u00eainer da aplica\u00e7\u00e3o esteja ativo. Estamos usando a flag Para aplicar migra\u00e7\u00f5es em um ambiente com cont\u00eaineres, frequentemente temos comandos espec\u00edficos associados ao servi\u00e7o. Vejamos como executar migra\u00e7\u00f5es usando o Docker: $ Execu\u00e7\u00e3o no terminal! O comando Ap\u00f3s executar as migra\u00e7\u00f5es, voc\u00ea pode verificar a cria\u00e7\u00e3o das tabelas utilizando um sistema de gerenciamento de banco de dados. A seguir, apresentamos um exemplo com o Beekeeper Studio: Lembre-se: Embora as tabelas estejam agora criadas e estruturadas, o banco de dados ainda n\u00e3o cont\u00e9m os dados anteriormente presentes no SQLite ou em qualquer outro banco que voc\u00ea estivesse utilizando antes. "},{"location":"09/#simplificando-nosso-fluxo-com-docker-compose","title":"Simplificando nosso fluxo comdocker-compose ","text":"Docker Compose \u00e9 uma ferramenta que permite definir e gerenciar aplicativos multi-cont\u00eainer com Docker. \u00c9 como se voc\u00ea tivesse um maestro conduzindo uma orquestra: o maestro (ou Docker Compose) garante que todos os m\u00fasicos (ou cont\u00eaineres) toquem em harmonia. Definimos nossa aplica\u00e7\u00e3o e servi\u00e7os relacionados, como o PostgreSQL, em um arquivo Ao adotar o Docker Compose, facilitamos o desenvolvimento e a execu\u00e7\u00e3o da nossa aplica\u00e7\u00e3o com seus servi\u00e7os dependentes utilizando um \u00fanico comando. "},{"location":"09/#criacao-do-docker-composeyml","title":"Cria\u00e7\u00e3o dodocker-compose.yml ","text":"docker-compose.yaml Explica\u00e7\u00e3o linha a linha:
Sobre o docker-compose Para usar o Docker Compose, voc\u00ea precisa t\u00ea-lo instalado em seu sistema. Ele n\u00e3o est\u00e1 inclu\u00eddo na instala\u00e7\u00e3o padr\u00e3o do Docker, ent\u00e3o lembre-se de instal\u00e1-lo separadamente! O guia oficial de instala\u00e7\u00e3o pode ser encontrado aqui Com este arquivo Para parar os servi\u00e7os e manter os dados seguros nos volumes definidos, use: Esses comandos simplificam o fluxo de trabalho e garantem que os servi\u00e7os iniciem corretamente e se comuniquem conforme o esperado. Execu\u00e7\u00e3o em modo desanexado Voc\u00ea pode iniciar os servi\u00e7os em segundo plano com a flag Automatizar as migra\u00e7\u00f5es do banco de dados \u00e9 uma pr\u00e1tica recomendada para garantir que sua aplica\u00e7\u00e3o esteja sempre sincronizada com o estado mais atual do seu esquema de banco de dados. \u00c9 como preparar todos os ingredientes antes de come\u00e7ar a cozinhar: voc\u00ea garante que tudo o que \u00e9 necess\u00e1rio est\u00e1 pronto para ser usado. Para automatizar as migra\u00e7\u00f5es em nossos cont\u00eaineres Docker, utilizamos um Por que usar o Entrypoint? No Docker, o Implementando o Entrypoint Criamos um script chamado Explica\u00e7\u00e3o Detalhada do Script:
Como Funciona na Pr\u00e1tica? Quando o cont\u00eainer \u00e9 iniciado, o Docker executa o script de Visualizando o Processo: Voc\u00ea pode pensar no Adicionando o Entrypoint ao Docker Compose: Inclu\u00edmos o Reconstruindo e Executando com Novas Configura\u00e7\u00f5es: Para aplicar as altera\u00e7\u00f5es, reconstru\u00edmos e executamos os servi\u00e7os com a op\u00e7\u00e3o Observando o Comportamento Esperado: Quando o cont\u00eainer \u00e9 iniciado, voc\u00ea deve ver as migra\u00e7\u00f5es sendo aplicadas, seguidas pela inicializa\u00e7\u00e3o da aplica\u00e7\u00e3o: $ Exemplo do resultado no terminal! Este processo garante que as migra\u00e7\u00f5es do banco de dados s\u00e3o realizadas automaticamente, mantendo a base de dados alinhada com a aplica\u00e7\u00e3o e pronta para a\u00e7\u00e3o assim que o servidor Uvicorn entra em cena. Nota de revis\u00e3o sobre vari\u00e1veis de ambienteUtilizar vari\u00e1veis de ambiente definidas em um arquivo Ainda assim, \u00e9 valioso mencionar como essa configura\u00e7\u00e3o mais segura seria realizada, especialmente para aqueles que planejam utilizar o Docker Compose em produ\u00e7\u00e3o. Em ambientes de produ\u00e7\u00e3o com Docker Compose, \u00e9 uma boa pr\u00e1tica gerenciar vari\u00e1veis de ambiente sens\u00edveis, como credenciais, por meio de um arquivo As vari\u00e1veis de ambiente podem ser definidas em nosso arquivo Para aplicar essas vari\u00e1veis, referencie o arquivo Adotar essa abordagem evita a exposi\u00e7\u00e3o das vari\u00e1veis de ambiente no arquivo de configura\u00e7\u00e3o. Esta n\u00e3o foi a abordagem padr\u00e3o no curso devido \u00e0 complexidade adicional e \u00e0 inten\u00e7\u00e3o de evitar confus\u00f5es. Dependendo do ambiente estabelecido pela equipe de DevOps/SRE em um projeto real, essa gest\u00e3o pode variar entre vari\u00e1veis de ambiente, arquivos Se optar por utilizar um arquivo Com essa configura\u00e7\u00e3o, o Pydantic ir\u00e1 ignorar quaisquer vari\u00e1veis no Agradecimentos especiais a @vcwild e @williangl pelas revis\u00f5es valiosas nesta aula que me fizeram criar essa nota. "},{"location":"09/#testes-com-docker","title":"Testes com Docker","text":"Agora que temos o Para executar os testes, utilizamos o comando: $ Execu\u00e7\u00e3o no terminal! Vamos entender melhor o que cada parte do comando faz:
Ao utilizar esse comando, o Docker Compose cuidar\u00e1 de iniciar os servi\u00e7os dos quais Se executarmos o comando podemos ver que ele inicia o banco de dados, inicia o container da aplica\u00e7\u00e3o e na sequ\u00eancia executa o comando que passamos no \u00c9 importante notar que, embora o Assim tendo o ambiente limpo novamente. "},{"location":"09/#executando-os-testes-no-postgresql","title":"Executando os testes no PostgreSQL","text":"Embora nosso Por conta disso, os testes t\u00eam sido executados no SQLite, mesmo com a presen\u00e7a do PostgreSQL no ambiente do Docker. No entanto, \u00e9 importante que os testes sejam executados no mesmo ambiente que o que rodar\u00e1 em produ\u00e7\u00e3o, para que n\u00e3o encontremos problemas relacionados a incompatibilidade de opera\u00e7\u00f5es no banco de dados. A altera\u00e7\u00e3o \u00e9 relativamente simples, temos que tornar a nossa fixture o mais pr\u00f3ximo poss\u00edvel do cliente da sess\u00e3o de produ\u00e7\u00e3o. Para fazer isso, precisamos alterar somente a chamada Com essa modifica\u00e7\u00e3o, agora estamos apontando para o banco de dados PostgreSQL, conforme definido nas configura\u00e7\u00f5es da nossa aplica\u00e7\u00e3o ( Agora, com a nova configura\u00e7\u00e3o, os testes utilizar\u00e3o o PostgreSQL, proporcionando um ambiente de testes mais fiel ao ambiente de produ\u00e7\u00e3o e, consequentemente, aumentando a confiabilidade dos testes executados: $ Execu\u00e7\u00e3o no terminal! Dessa forma temos um ambiente mais coeso e podemos reproduzir nossas configura\u00e7\u00f5es de forma bastante simples em qualquer ambiente. "},{"location":"09/#commit","title":"Commit","text":"Ap\u00f3s criar nosso arquivo
Dockerizar nossa aplica\u00e7\u00e3o FastAPI, junto com o PostgreSQL, nos permite garantir consist\u00eancia em diferentes ambientes. A combina\u00e7\u00e3o de Docker e Docker Compose simplifica o processo de desenvolvimento e implanta\u00e7\u00e3o. Na pr\u00f3xima aula, vamos aprender como levar nossa aplica\u00e7\u00e3o para o pr\u00f3ximo n\u00edvel executando os testes de forma remota com a integra\u00e7\u00e3o cont\u00ednua do GitHub Actions. "},{"location":"10/","title":"[WIP] Automatizando os testes com Integra\u00e7\u00e3o Cont\u00ednua","text":""},{"location":"10/#wip-automatizando-os-testes-com-integracao-continua","title":"[WIP] Automatizando os testes com Integra\u00e7\u00e3o Cont\u00ednua","text":"T\u00f3pico de manuten\u00e7\u00e3o: https://github.com/dunossauro/fastapi-do-zero/issues/34 Objetivos da aula:
Na aula passada, preparamos nossa aplica\u00e7\u00e3o para execu\u00e7\u00e3o em containers Docker. Nesta aula, focaremos em garantir que nossa aplica\u00e7\u00e3o continue funcionando conforme o esperado a cada atualiza\u00e7\u00e3o de c\u00f3digo. Para isso, introduziremos o conceito de Integra\u00e7\u00e3o Cont\u00ednua (CI). "},{"location":"10/#integracao-continua-ci","title":"Integra\u00e7\u00e3o Cont\u00ednua (CI)","text":"Integra\u00e7\u00e3o Cont\u00ednua (CI) \u00e9 uma pr\u00e1tica de desenvolvimento que envolve a integra\u00e7\u00e3o frequente de c\u00f3digo ao projeto principal. Com cada integra\u00e7\u00e3o - geralmente um commit - \u00e9 disparado um processo automatizado que constr\u00f3i e testa o c\u00f3digo. Isso permite detectar e corrigir problemas rapidamente, contribuindo para a manuten\u00e7\u00e3o da qualidade do software. "},{"location":"10/#github-actions","title":"GitHub Actions","text":"GitHub Actions \u00e9 um servi\u00e7o fornecido pelo GitHub que permite a automatiza\u00e7\u00e3o de workflows, incluindo a execu\u00e7\u00e3o de testes e implanta\u00e7\u00e3o de software, diretamente em seu reposit\u00f3rio GitHub. Cada tarefa \u00e9 definida como uma \"a\u00e7\u00e3o\", e a\u00e7\u00f5es podem ser combinadas para criar um \"workflow\" que atende a necessidades espec\u00edficas de desenvolvimento. "},{"location":"10/#configurando-o-workflow-de-ci","title":"Configurando o workflow de CI","text":"Vamos configurar um workflow de CI para nossa aplica\u00e7\u00e3o utilizando o GitHub Actions. Crie um novo arquivo em seu reposit\u00f3rio, sob o diret\u00f3rio Vamos analisar este arquivo:
Ap\u00f3s adicionar e configurar o arquivo do workflow, voc\u00ea deve commitar as mudan\u00e7as em seu reposit\u00f3rio. Siga os passos: $ Execu\u00e7\u00e3o no terminal! "},{"location":"10/#conclusao","title":"Conclus\u00e3o","text":"A Integra\u00e7\u00e3o Cont\u00ednua \u00e9 uma pr\u00e1tica fundamental no desenvolvimento moderno de software, e o GitHub Actions \u00e9 uma ferramenta poderosa para implementar essa pr\u00e1tica. Ele n\u00e3o apenas ajuda a manter a qualidade do c\u00f3digo ao garantir que todos os testes sejam executados a cada commit, mas tamb\u00e9m permite detectar e corrigir problemas mais cedo no ciclo de desenvolvimento. Al\u00e9m disso, monitorar a cobertura de testes com o Codecov nos permite manter um alto padr\u00e3o de qualidade, garantindo que todas as partes do nosso c\u00f3digo sejam testadas. Na pr\u00f3xima aula, vamos levar nossa aplica\u00e7\u00e3o ao pr\u00f3ximo n\u00edvel, preparando-a para o deployment em produ\u00e7\u00e3o! "},{"location":"11/","title":"[WIP] Fazendo deploy no Fly.io e configurando o PostgreSQL","text":""},{"location":"11/#wip-fazendo-deploy-no-flyio-e-configurando-o-postgresql","title":"[WIP] Fazendo deploy no Fly.io e configurando o PostgreSQL","text":"Objetivos da aula:
Na aula anterior, n\u00f3s automatizamos nossos testes e integramos tudo em um pipeline de integra\u00e7\u00e3o e deploy cont\u00ednuos. Agora, vamos aprender a fazer o deploy da nossa aplica\u00e7\u00e3o em um ambiente de produ\u00e7\u00e3o usando o Fly.io. O Fly.io \u00e9 uma plataforma de deploy que nos permite lan\u00e7ar nossas aplica\u00e7\u00f5es Docker na nuvem. Ele tamb\u00e9m fornece uma s\u00e9rie de recursos, como balanceamento de carga, cria\u00e7\u00e3o de inst\u00e2ncias de banco de dados e configura\u00e7\u00e3o de vari\u00e1veis de ambiente. Para iniciar, precisamos instalar a CLI do Fly.io, chamada Este comando ir\u00e1 abrir o navegador para voc\u00ea entrar com suas credenciais do Fly.io. "},{"location":"11/#criando-a-aplicacao-no-flyio-e-fazendo-o-deploy","title":"Criando a aplica\u00e7\u00e3o no Fly.io e fazendo o deploy","text":"Depois de logado, podemos criar uma nova aplica\u00e7\u00e3o no Fly.io usando o comando: $ Execu\u00e7\u00e3o no terminal! Este comando ir\u00e1 perguntar algumas informa\u00e7\u00f5es sobre sua aplica\u00e7\u00e3o e ent\u00e3o criar\u00e1 uma nova aplica\u00e7\u00e3o no Fly.io. Com nossa aplica\u00e7\u00e3o criada, podemos agora fazer o deploy da nossa imagem Docker usando o comando: $ Execu\u00e7\u00e3o no terminal! A op\u00e7\u00e3o Antes de avan\u00e7armos, \u00e9 importante mencionar uma especificidade do Fly.io: para criar uma inst\u00e2ncia do PostgreSQL, a plataforma requer que um cart\u00e3o de cr\u00e9dito seja fornecido. Esta \u00e9 uma medida de seguran\u00e7a adotada para evitar o uso indevido de seus servi\u00e7os, como a execu\u00e7\u00e3o de ferramentas de minera\u00e7\u00e3o. Apesar dessa exig\u00eancia, o servi\u00e7o de PostgreSQL \u00e9 oferecido de forma gratuita. Mais detalhes podem ser encontrados neste artigo. Agora, vamos criar uma inst\u00e2ncia do PostgreSQL. O Fly.io fornece um servi\u00e7o PostgreSQL que podemos usar para criar uma nova inst\u00e2ncia do PostgreSQL com apenas alguns comandos. "},{"location":"11/#configurando-as-variaveis-de-ambiente-e-rodando-as-migracoes-do-alembic","title":"Configurando as vari\u00e1veis de ambiente e rodando as migra\u00e7\u00f5es do Alembic","text":"Com a inst\u00e2ncia criada, algumas vari\u00e1veis de ambiente ser\u00e3o automaticamente definidas para n\u00f3s. Para que o Alembic possa executar as migra\u00e7\u00f5es, precisamos configurar a vari\u00e1vel Substitua Finalmente, podemos executar nossas migra\u00e7\u00f5es Alembic. Usaremos a CLI do Fly.io para executar o comando dentro de um cont\u00eainer do nosso aplicativo: $ Execu\u00e7\u00e3o no terminal! Substitua Agora que temos nosso aplicativo funcionando no Fly.io, podemos configurar o Github Actions para fazer o deploy autom\u00e1tico sempre que fizermos um push no nosso reposit\u00f3rio. Para isso, precisaremos adicionar alguns passos ao nosso arquivo de pipeline do Github Actions: Com isso, nossa aplica\u00e7\u00e3o est\u00e1 pronta para uso no Fly.io! "},{"location":"11/#conclusao","title":"Conclus\u00e3o","text":"Ao longo desta aula, n\u00f3s mergulhamos no mundo do deploy de aplica\u00e7\u00f5es com o Fly.io, uma plataforma que facilita imensamente a tarefa de colocar nossas aplica\u00e7\u00f5es para funcionar na nuvem. Al\u00e9m disso, tamb\u00e9m tivemos a chance de entender como gerenciar vari\u00e1veis de ambiente de forma segura e eficiente, permitindo a nossa aplica\u00e7\u00e3o se adaptar a diferentes contextos de execu\u00e7\u00e3o. Aprendemos como subir nossa imagem Docker no Fly.io e como este processo pode ser simplificado e automatizado. Tamb\u00e9m vimos como \u00e9 poss\u00edvel ter nosso banco de dados rodando no mesmo ambiente da nossa aplica\u00e7\u00e3o, facilitando a manuten\u00e7\u00e3o e a escalabilidade. Configuramos e utilizamos o PostgreSQL no Fly.io, o que nos deu uma vis\u00e3o pr\u00e1tica de como gerenciar bancos de dados em um ambiente de produ\u00e7\u00e3o. Ao fazer isso, pudemos integrar ainda mais a nossa aplica\u00e7\u00e3o ao ambiente em que ela est\u00e1 rodando. Al\u00e9m disso, exploramos a import\u00e2ncia das migra\u00e7\u00f5es e como elas podem ser gerenciadas usando o Alembic, que nos permitiu atualizar nosso banco de dados de forma controlada e rastre\u00e1vel. Finalmente, vimos como podemos automatizar todo o processo de deploy usando o Github Actions. Esta \u00e9 uma pr\u00e1tica extremamente \u00fatil e poderosa, pois permite que a nossa aplica\u00e7\u00e3o esteja sempre atualizada com as \u00faltimas altera\u00e7\u00f5es que fizemos, sem a necessidade de qualquer interven\u00e7\u00e3o manual. Com todas essas pe\u00e7as, temos agora uma aplica\u00e7\u00e3o robusta e pronta para escalar, com todos os elementos necess\u00e1rios para ser operada em um ambiente de produ\u00e7\u00e3o real. Estas s\u00e3o ferramentas e pr\u00e1ticas que est\u00e3o no cora\u00e7\u00e3o do desenvolvimento de software moderno, e domin\u00e1-las nos permitir\u00e1 construir aplica\u00e7\u00f5es cada vez mais complexas e eficientes. Na pr\u00f3xima aula, faremos uma recapitula\u00e7\u00e3o de tudo o que aprendemos neste curso e discutiremos os pr\u00f3ximos passos. Continue acompanhando para fortalecer ainda mais seus conhecimentos em desenvolvimento de aplica\u00e7\u00f5es com FastAPI, Docker, CI/CD e muito mais. At\u00e9 a pr\u00f3xima aula! "},{"location":"12/","title":"Despedida do curso","text":""},{"location":"12/#wip-despedida","title":"[WIP] Despedida","text":"Objetivos da aula:
Estamos chegando ao final de nossa jornada juntos neste curso. Durante esse tempo, tivemos a oportunidade de explorar uma s\u00e9rie de conceitos e tecnologias essenciais para o desenvolvimento de aplica\u00e7\u00f5es web modernas e escal\u00e1veis. \u00c9 importante lembrar que o que vimos aqui \u00e9 apenas a ponta do iceberg. Ainda h\u00e1 muitos aspectos e detalhes que n\u00e3o pudemos cobrir neste curso, como tratamento de logs, observabilidade, seguran\u00e7a avan\u00e7ada, otimiza\u00e7\u00f5es de desempenho, entre outros. Encorajo a todos que continuem explorando e aprendendo. "},{"location":"12/#revisao","title":"Revis\u00e3o","text":"Ao longo deste curso, cobrimos uma s\u00e9rie de t\u00f3picos essenciais para o desenvolvimento de aplica\u00e7\u00f5es web modernas e robustas:
J\u00e1 cobrimos alguns temas n\u00e3o citados neste curso usando FastAPI em Algumas Lives de Python. Voc\u00ea pode assistir para aprender mais tamb\u00e9m. "},{"location":"12/#templates-e-websockets","title":"Templates e WebSockets","text":"Na Live de Python #164 conversamos sobre websockets com Python e usamos FastAPI para exemplificar o comportamento. Durante essa live criamos uma aplica\u00e7\u00e3o de chat e usamos os templates com Jinja2 e Brython. "},{"location":"12/#graphql-strawberry","title":"GraphQL (Strawberry)","text":"Na Live de Python #185 conversamos sobre GraphQL um padr\u00e3o alternativo a REST APIs. Todos os exemplos foram aplicados usando Strawberrye FastAPI "},{"location":"12/#sqlmodel","title":"SQLModel","text":"Na Live de Python #235 conversamos sobre SQLModel um ORM alternativo ao SQLAlchemy que se integra com o Pydantic. O SQLModel tamb\u00e9m foi desenvolvido pelo Sebastian (criador do FastAPI). Caminhando ao final dessa aula, podemos ver a implementa\u00e7\u00e3o do SQLModel em uma aplica\u00e7\u00e3o b\u00e1sica com FastAPI. "},{"location":"12/#proximos-passos","title":"Pr\u00f3ximos passos","text":"[WIP] "},{"location":"12/#conclusao","title":"Conclus\u00e3o","text":"Todos esses conceitos e pr\u00e1ticas s\u00e3o componentes fundamentais no desenvolvimento de aplica\u00e7\u00f5es web modernas e escal\u00e1veis. Eles nos permitem criar aplica\u00e7\u00f5es robustas, confi\u00e1veis e eficientes, que podem ser facilmente mantidas e escaladas. Gostaria de agradecer a todos que acompanharam essa s\u00e9rie de aulas. Espero que tenham encontrado valor nas informa\u00e7\u00f5es e pr\u00e1ticas que compartilhamos aqui. Lembre-se, a jornada do aprendizado \u00e9 cont\u00ednua e cada passo conta. Continue explorando, aprendendo e crescendo. At\u00e9 a pr\u00f3xima! "}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 77040ddc..f54f9583 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ |