No artigo anterior conhecemos um pouco sobre o FastAPI e suas principais características, agora vamos ver na prática como utilizar as funcionalidades desse framework durante o desenvolvimento de uma Web API com as operações básicas de CRUD (Create, Read, Update e Delete).
Durante o desenvolvimento desse projeto nós iremos precisar acessar o banco de dados e para isso vamos utilizar um ORM que será o SQLAlchemy e como banco de dados vamos utilizar o SQLite.
Formação Desenvolvedor Python
Conhecer a formaçãoCriando o projeto
Para que possamos desenvolver nosso projeto é necessário que você esteja com o Python devidamente instalado em seu sistema operacional, caso não saiba como realizar a instalação do Python aqui mesmo no blog da TreinaWeb temos o artigo Instalação do Python e nosso primeiro Olá Mundo que mostra o processo de instalação da linguagem nos sistemas Linux, Windows e MacOS.
Outro ponto importante é que todo o código que será produzido durante esse artigo foi feito utilizando o Python 3.10, então caso possua uma versão diferente pode se fazer necessário algumas modificações para que o código funcione na sua versão do Python.
Primeiramente vamos criar uma pasta onde ficarão os arquivos do nosso projeto, para isso abra o terminal do seu sistema operacional e crie um diretório com o comando mkdir tw-cursos
, logo em seguida precisamos entrar dentro desse diretório com o comando cd tw-cursos
, uma vez dentro do diretório do projeto nós vamos criar e ativar um ambiente virtual com os seguintes comandos:
- No Linux e MacOS
python3 -m venv .venv
source .venv/bin/activate
- No Windows:
python -m venv .venv
.\.venv\Scripts\activate
Agora que estamos com o nosso ambiente virtual ativo podemos realizar a instalação das bibliotecas necessárias utilizando o PIP.
pip install fastapi uvicorn[standard] sqlalchemy
Após a execução do comando acima teremos feito a instalação das seguintes bibliotecas:
- FastAPI: Microframework voltada para o desenvolvimento de Web APIs;
- Uvicorn: Web server ASGI implementado em Python, necessário para a execução do nosso projeto;
- SQLAlchemy: ORM bastante popular dentro do ecossistema Python.
Agora nós já estamos com o nosso ambiente preparado e podemos dar prosseguimento com o desenvolvimento de nossa API.
Configurando a conexão como banco de dados
Agora vamos configurar a conexão do SQLALchemy com o banco de dados, primeiramente vamos criar um novo arquivo chamado database.py
, arquivo esse que vamos colocar as configurações necessárias do SQLAlchemy para que o mesmo consiga se conectar ao banco de dados.
Neste arquivo vamos colocar o seguinte conteúdo:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///db.sqlite3"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
No código acima nós inicialmente temos as importações necessárias vindas do SQLAlchemy, logo em seguida temos a criação da constante SQLALCHEMY_DATABASE_URL
que conterá a string de conexão com o banco de dados.
Logo em seguida temos a criação da engine do SQLAlchemy, essa engine é o ponto de partida para qualquer aplicação que utilize o SQLAlchemy, é a partir dela que o SQLAlchemy conseguirá criar as conexões com o banco de dados. Para criar a engine nós utilizamos a função create_engine
e passamos para essa função dois argumentos, sendo o primeiro a string de conexão com o banco e a segunda é o argumento nomeado connect_arg
que é um dicionário com opções adicionais para essa configuração. Importante frisar que o argumento connect_args={"check_same_thread": False}
só é necessário para quando estamos utilizando o SQLite.
Depois, nós criamos a classe SessionLocal
com o auxílio da função sessionmaker
, essa classe irá representar uma sessão com o nosso banco de dados.
Após a criação da classe SessionLocal
nós temos a criação da classe Base
com a ajuda do método declarative_base
, nós iremos utilizar essa classe mais na frente para realizar a criação das classes de modelo do nosso banco de dados.
E por fim temos a criação da função get_db
que será utilizada para nos disponibilizar uma instância da classe SessionLocal
, ou seja, será através da função get_db
que iremos criar novas sessões com o banco de dados. Essa função será utilizada mais a frente para que possamos utilizar a funcionalidade de injeção de dependências provida pelo próprio FastAPI.
Curso Python - SQLAlchemy ORM
Conhecer o cursoCriando a camada de modelo
Agora que já realizamos a configuração necessária para realizarmos a conexão com o banco de dados através do SQLAlchemy, nós vamos criar a camada de modelo de nossa aplicação, essa camada conterá classes que irão representar as tabelas do banco de dados e que o SQLAlchemy irá utilizar para gerar essas tabelas de forma automática.
Vamos criar um novo arquivo chamado models.py
e dentro desse arquivo iremos colocar o seguinte conteúdo:
from sqlalchemy import Column, Integer, String
from database import Base
class Curso(Base):
__tablename__ = "cursos"
id: int = Column(Integer, primary_key=True, index=True)
titulo: str = Column(String(100), nullable=False)
descricao: str = Column(String(255), nullable=False)
carga_horaria: int = Column(Integer, nullable=False)
qtd_exercicios: int = Column(Integer, nullable=False)
Nessa código nós inicialmente importamos as classes do SQLAlchemy que serão necessárias para a declaração do nosso model, são as classes Column
utilizada para definir uma coluna de uma tabela e as classes Integer
e String
utilizadas para definir o tipo de dados utilizado nas colunas de uma tabela.
Logo em seguida também realizamos a importação da classe Base
criada na seção anterior, classe essa a qual todas as nossas classes de modelo devem herdar.
E por fim temos a criação da classe Curso
que é de fato a nossa classe de modelo que irá representar a nossa tabela no banco de dados, nessa classe nós temos o atributo __tablename__
que serve para informar ao SQLAlchemy qual o nome deve utilizado no momento da criação dessa tabela no banco de dados e também os atributos id
, titulo
, descricao
, carga_horaria
e qtd_exercicios
que irão representar as colunas dessa tabela.
Veja que em cada um dos atributos que representam as colunas da tabela nós estamos utilizando o recurso de Type Hints, esse recurso serve basicamente para que possamos trabalhar com tipagem em nosso código Python, isso é bem similar ao que o TypeScript faz para o JavaScript. O uso de tipagem no Python não é obrigatório, mas é altamente recomendado durante o desenvolvimento de projetos com o FastAPI.
Criando a camada de repositório
Nesse projeto nós vamos utilizar um padrão chamado de repositório ou repository pattern, esse padrão de projeto tem como objetivo isolar as especificidades de acesso a base de dados em uma única camada, então todo o código necessário para realizar as operações de leitura e escrita no banco de dados estarão dentro dessa camada.
Então nós vamos criar um novo arquivo chamado repositories.py
e dentro desse arquivo iremos ter uma classe chamada CursoRepository
e essa classe terá métodos estáticos que irão realizar as operações no banco de dados com o auxílio do SQLAlchemy. Esse arquivo terá o seguinte conteúdo:
from sqlalchemy.orm import Session
from models import Curso
class CursoRepository:
@staticmethod
def find_all(db: Session) -> list[Curso]:
return db.query(Curso).all()
@staticmethod
def save(db: Session, curso: Curso) -> Curso:
if curso.id:
db.merge(curso)
else:
db.add(curso)
db.commit()
return curso
@staticmethod
def find_by_id(db: Session, id: int) -> Curso:
return db.query(Curso).filter(Curso.id == id).first()
@staticmethod
def exists_by_id(db: Session, id: int) -> bool:
return db.query(Curso).filter(Curso.id == id).first() is not None
@staticmethod
def delete_by_id(db: Session, id: int) -> None:
curso = db.query(Curso).filter(Curso.id == id).first()
if curso is not None:
db.delete(curso)
db.commit()
Nesse código nós temos inicialmente a importação da classe Session
do SQLAlchemy que será usada apenas para propósitos de tipagem e logo em seguida temos também a importação da classe Curso
que a nossa classe da camada de modelo.
E por fim temos a criação da classe CursoRepository
que contém os seguintes métodos estáticos:
-
find_all(db: Session) -> list[Curso]
: método responsável por buscar todos os cursos cadastrados; -
save(db: Session, curso: Curso) -> Curso
: método responsável por salvar um curso no banco de dados, esse método foi escrito de forma que ele possa ser utilizado tanto para cadastro de um novo curso, quanto para a edição de um curso já existente; -
find_by_id(db: Session, id: int) -> Curso
: método responsável por buscar um curso no banco de dados com base doid
do curso; -
exists_by_id(db: Session, id: int) -> bool
: método responsável por verificar se existe algum curso cadastrado com base noid
do curso; -
delete_by_id(db: Session, id: int) -> None
: método responsável por excluir um curso com base no seuid
.
Um ponto importante para observarmos é que todos os métodos da camada de repositório recebem como primeiro argumento uma instância de Session
que chamamos de db
, ou seja, quem chamar os métodos da camada de repositório terá a responsabilidade de prover a sessão do banco de dados.
Curso HTTP - Fundamentos para desenvolvedores
Conhecer o cursoCriando a camada de schemas
Agora nós vamos trabalhar em uma camada na qual vamos chamar de schemas, essa camada irá conter classes que irão representar os dados que serão recebidos e retornados no corpo de uma requisição ou resposta HTTP, para criação dessas classes nós vamos utilizar o Pydantic que é uma biblioteca que vem junto com a instalação do FastAPI. O Pydantic tem como objetivo prover uma maneira mais simples e direta para realizar validação de dados através do uso da funcionalidade de Type Hints do Python.
O FastAPI utiliza bastante o Pydantic não só para o processo de validação dos dados, mas também para realizar a tipagem dos dados que são recebidos e retornados nas requisições e respostas HTTP, além disso o FastAPI utiliza essa tipagem na hora de gerar a documentação do projeto.
Só um ponto de observação, essa camada a qual iremos chamar de schemas também recebe o nome de model, porém para não confundirmos os models do SQLAlchemy com os models do Pydantic nós vamos chamar de schemas.
Então vamos começar com a codificação dessa camada, para isso vamos criar um novo arquivo chamado schemas.py
e nele iremos colocar o seguinte conteúdo:
from pydantic import BaseModel
class CursoBase(BaseModel):
titulo: str
descricao: str
carga_horaria: int
qtd_exercicios: int
class CursoRequest(CursoBase):
...
class CursoResponse(CursoBase):
id: int
class Config:
orm_mode = True
Nesse código nós inicialmente importamos a classe BaseModel
do Pydantic, que é a classe a qual todas as classe de modelo do Pydantic devem herdar, logo em seguida nós criamos três novas classes, a classe CursoBase
, a classe CursoRequest
e a classe CursoResponse
.
A classe CursoBase
irá herdar de de BaseModel
e nela iremos definir tudo aquilo que é comum para as classes CursoRequest
e CursoResponse
não evitamos duplicação de código.
Já na classe CursoRequest
nós temos tudo aquilo que esperamos receber no corpo das nossas requisições HTTP, como não colocamos nenhum atributo á mais ela só terá aquilo que foi definido na classe CursoBase
E por fim na classe CursoResponse
nós temos tudo aquilo que queremos retornar do corpo de nossas respostas HTTP, como definimos apenas o atributo id
ela terá os atributos definidos em CursoBase
mais o atributo id
. Além disso nós também criamos dentro da classe CursoResponse
uma classe chamada Config
que serve para passarmos configurações adicionais para o nosso modelo do Pydantic e a configuração que fizemos foi colocar como True
a opção orm_mode
, essa configuração habilita um método estático dentro da classe chamado from_orm
que permite a criação de uma instância do modelo do Pydantic a partir de uma classe de modelo da nossa ORM.
Operações de CRUD em uma API com FastAPI e SQLAlchemy
Agora vamos de fato trabalhar no código que vai lidar com as requisições HTTP, incialmente iremos criar um novo arquivo chamado main.py
que será o arquivo principal do projeto e onde iremos instanciar a nossa app do FastAPI, nesse arquivo iremos colocar esse conteúdo por enquanto:
from fastapi import FastAPI, Depends, HTTPException, status, Response
from sqlalchemy.orm import Session
from models import Curso
from database import engine, Base, get_db
from repositories import CursoRepository
from schemas import CursoRequest, CursoResponse
Base.metadata.create_all(bind=engine)
app = FastAPI()
Nesse código nós estamos primeiramente importando algumas classes do FastAPI que serão necessárias durante a criação das nossas rotas:
-
FastAPI
: classe que instanciamos para criar a app do FastAPI; -
Depends
: classe que é utilizada para realizar o processo de injeção de dependências; -
HTTPException
: classe de exceção que utilizamos para retornar um erro HTTP; -
status
: é um módulo do FastAPI que contém constantes que representam os diferentes status codes do protocolo HTTP; -
Response
: classe do FastAPI que representa uma resposta HTTP.
Além das importações do FastAPI também estamos importando a classe Session
do SQLAlchemy que novamente iremos utilizar apenas para tipagem.
Também fazemos as importações da classe Curso
da nossa camada de modelo, a classe CursoRepository
da nossa camada de repositório, as classes CursoRequest
e CursoResponse
da nossa camada de schemas, a variável engine
, a classe Base
e a função get_db
da camada de database.
Após realizada todas as importações necessárias temos a instrução Base.metadata.create_all(bind=engine)
que vai fazer com que o SQLAlchemy crie o arquivo db.sqlite3
que é o nosso banco de dados SQLite e também crie as tabelas no banco, que no caso é apenas uma, a tabela cursos.
E por fim temos a criação da variável app
que é uma instância da classe FastAPI
.
Curso APIs Rest - Fundamentos
Conhecer o cursoRota de cadastro de curso
Vamos criar a nossa primeira rota que será a rota responsável por realizar o cadastro de novos cursos em nosso banco de dados, para isso vamos criar uma função que será responsável por tratar a requisição, essa requisição terá que ser feita com o verbo HTTP POST para a rota /api/cursos
.
# Código inicial do arquivo omitido para facilitação da leitura
@app.post("/api/cursos", response_model=CursoResponse, status_code=status.HTTP_201_CREATED)
def create(request: CursoRequest, db: Session = Depends(get_db)):
curso = CursoRepository.save(db, Curso(**request.dict()))
return CursoResponse.from_orm(curso)
Nesse código temos a função create
que será responsável por lidar com as requisições para cadastro de novos usuários, essa função é decorada com o decorator @app.post
para informar que é uma rota que lida com requisição do tipo POST. Além disso passamos como primeiro argumento do decorator uma string que informa qual a rota para qual esperamos que seja feita a requisição, depois passamos um parâmetro nomeado chamado response_model
que informa qual o tipo de dado estará contido no corpo da resposta, no caso será um dado do tipo CursoResponse
e por fim informamos qual o status code da resposta através do parâmetro nomeado status_code
.
Já no método create
nós definimos um parâmetro chamado request
que é do tipo CursoRequest
, esse parâmetro recebe os dados que foram passados no corpo da requisição e também temos um segundo parâmetro chamada db
do tipo Session
que possui como valor padrão a instrução Depends(get_db)
, nesse momento estamos realizando a injeção de dependências, o FastAPI irá executar a função get_db
que irá retornar uma instância da classe LocalSession
que é a sessão do banco de dados e irá então passar essa sessão para a nossa função create
.
O mais interessante desse processo de injeção de dependências é que ao término do processamento da requisição a sessão será fechada, logo, temos apenas uma sessão do banco de dados por requisição.
Dentro do escopo da função create
nós estamos chamando o método estático save
da classe CursoRepository
e passando para esse método a sessão do banco e os dados do curso a ser criado, logo em seguida nós recebemos os dados curso recém cadastrado e então criamos e retornamos um nova instância de CursoResponse
que é criada com o auxílio do método estático from_orm
.
Rota de listagem de cursos
Agora vamos implementar a rota que será responsável por listar os cursos cadastrados na aplicação, a requisição a ser feita será para a rota /api/cursos
só que agora com o verbo HTTP GET.
# Código inicial do arquivo omitido para facilitação da leitura
@app.get("/api/cursos", response_model=list[CursoResponse])
def find_all(db: Session = Depends(get_db)):
cursos = CursoRepository.find_all(db)
return [CursoResponse.from_orm(curso) for curso in cursos]
Agora nós criamos uma função chamada get_all
que irá processar as requisições feitas para a rota /api/cursos
com o verbo HTTP GET, como o verbo HTTP que iremos trabalhar é o GET então nós utilizamos o decorator @app.get
passando qual a rota e qual o tipo que será retornado no corpo da resposta, como iremos retornar uma lista de cursos, então o parâmetro response_model
foi definido como list[CursoResponse]
.
Na função get_all
recebemos como parâmetro apenas a sessão do banco de dados via injeção de dependências e então no escopo da função nós chamamos o método estático find_all
da classe CursoRepository
para que seja feita a busca no banco de dados e logo em seguida criamos e retornamos uma lista de CursoResponse
utilizando o recurso de list comprehension do Python.
Veja que nesse caso não utilizamos o parâmetro status_code
no decorator @app.get
isso por que queremos retornar o status code 200, que significa que a requisição foi processada com sucesso e o status code 200 já é o padrão, então não é necessário informar o status code.
Formação Desenvolvedor Flask Full-Stack
Conhecer a formaçãoRota de busca de curso por id
Agora vamos implementar a rota responsável por buscar um curso por id, nessa rota vamos precisar receber o id do curso a ser buscado diretamente pela rota, então a requisição será feita para a rota /api/cursos/{id}
onde {id}
será substituído pelo id do curso que estamos querendo fazer a busca, ou seja, caso estejamos realizando a busca pelo curso de id 1 a rota será /api/cursos/1
e essa requisição deverá ser realizada com o verbo HTTP GET.
# Código inicial do arquivo omitido para facilitação da leitura
@app.get("/api/cursos/{id}", response_model=CursoResponse)
def find_by_id(id: int, db: Session = Depends(get_db)):
curso = CursoRepository.find_by_id(db, id)
if not curso:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
return CursoResponse.from_orm(curso)
No código acima nós criamos a função find_by_id
que irá tratar a requisição, como será uma requisição com verbo GET a função foi decorada com o decorator @app.get
onde passamos como primeiro argumento a string informando a rota que é /api/cursos/{id}
, onde {id}
significa que é uma parte da rota que é variável e definimos o response_model
como CursoResponse
.
Já a função find_by_id
recebe o parâmetro id
que será a parte variável da rota, ou seja, o id do curso que estamos buscando e a injeção de dependências da sessão do banco de dados.
No escopo da função nós realizamos a busca no banco utilizando o método estático find_by_id
da classe CursoRepository
e então verificamos se algum curso foi encontrado, em caso negativo nós lançamos uma exceção do tipo HTTPException
, que é uma exceção do FastAPI para quando queremos informar que alguma coisa deu errado e na exceção informamos qual o status code a ser retornado e uma mensagem de erro, já em caso positivo retornamos uma nova instância de CursoResponse
com os dados do curso que foi encontrado.
Rota de exclusão de curso por id
Agora vamos para a nossa rota de exclusão de curso por id, essa rota será bem semelhante com a rota de busca de curso por id, a diferença é que ao invés da requisição utilizar o verbo HTTP GET será utilizado o verbo HTTP DELETE.
@app.delete("/api/cursos/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_by_id(id: int, db: Session = Depends(get_db)):
if not CursoRepository.exists_by_id(db, id):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
CursoRepository.delete_by_id(db, id)
return Response(status_code=status.HTTP_204_NO_CONTENT)
Dessa vez como queremos que a requisição seja realizada com o verbo DELETE nós decoramos a função delete_by_id
com o decorator @app.delete
e passamos como status code a constante HTTP_204_NO_CONTENT
, que irá retornar o status code 204, que quer dizer que a requisição foi processada com sucesso, porém não existe nada a ser retornado para quem realizou a requisição.
Já no escopo da função delete_by_id
nós verificamos se existe algum curso cadastrado com o id que foi solicitado e em caso negativo lançamos uma exceção do tipo HTTPException com o status code 404 e em caso positivo como não temos nada para retornar no corpo da resposta utilizamos a classe Response
do FastAPI para montarmos a resposta de forma manual e apenas dizemos qual o status code da resposta sem nenhum conteúdo.
Rota de atualização de curso
Agora para finalizarmos as nossas operações de CRUD vamos implementar a última rota da nossa API, rota essa que será responsável por realizar a atualização de um curso no banco de dados. Essa rota será bem semelhante às rotas de busca por id e exclusão por id, a diferença será que vamos utilizar o verbo HTTP PUT.
@app.put("/api/cursos/{id}", response_model=CursoResponse)
def update(id: int, request: CursoRequest, db: Session = Depends(get_db)):
if not CursoRepository.exists_by_id(db, id):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
curso = CursoRepository.save(db, Curso(id=id, **request.dict()))
return CursoResponse.from_orm(curso)
Nossa função update
responsável por tratar a requisição será decorada com o decorator @app.put
para que assim a requisição seja feita com o verbo HTTP PUT, definimos a rota como /api/cursos/{id}
e definimos o response_model
como CursoResponse
.
Já a função update
recebe três parâmetros, sendo eles o id do curso que está sendo atualizado, os dados que estarão contidos no corpo da requisição, dados esses que serão utilizados para atualizar os dados do curso em questão e por fim a injeção de dependências da sessão do banco de dados.
No escopo da função nós primeiramente verificamos se o curso que queremos atualizar existe, em caso negativo lançamos a exceção HTTPException com o status code 404 e a mensagem de erro e em caso positivo nós realizamos a operação de atualização com o método estatico save
da classe CursoRepository
, lembrando que o método save
foi feito de forma que possa ser utilizado tanto para uma operação de criação quanto de atualização e por fim retornamos uma nova instância de CursoResponse
com os dados do curso já atualizado.
Resultado final
Após a criação de todas as rotas nós finalizamos a criação da nossa API com FastAPI e SQLAlchemy, o resultado final do código do arquivo main.py
ficará da seguinte maneira:
from fastapi import FastAPI, Depends, HTTPException, status, Response
from sqlalchemy.orm import Session
from models import Curso
from database import engine, Base, get_db
from repositories import CursoRepository
from schemas import CursoRequest, CursoResponse
Base.metadata.create_all(bind=engine)
app = FastAPI()
@app.post("/api/cursos", response_model=CursoResponse, status_code=status.HTTP_201_CREATED)
def create(request: CursoRequest, db: Session = Depends(get_db)):
curso = CursoRepository.save(db, Curso(**request.dict()))
return CursoResponse.from_orm(curso)
@app.get("/api/cursos", response_model=list[CursoResponse])
def find_all(db: Session = Depends(get_db)):
cursos = CursoRepository.find_all(db)
return [CursoResponse.from_orm(curso) for curso in cursos]
@app.get("/api/cursos/{id}", response_model=CursoResponse)
def find_by_id(id: int, db: Session = Depends(get_db)):
curso = CursoRepository.find_by_id(db, id)
if not curso:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
return CursoResponse.from_orm(curso)
@app.delete("/api/cursos/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_by_id(id: int, db: Session = Depends(get_db)):
if not CursoRepository.exists_by_id(db, id):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
CursoRepository.delete_by_id(db, id)
return Response(status_code=status.HTTP_204_NO_CONTENT)
@app.put("/api/cursos/{id}", response_model=CursoResponse)
def update(id: int, request: CursoRequest, db: Session = Depends(get_db)):
if not CursoRepository.exists_by_id(db, id):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Curso não encontrado"
)
curso = CursoRepository.save(db, Curso(id=id, **request.dict()))
return CursoResponse.from_orm(curso)
Testando nossa API
Agora que temos todas as rotas que realizam as operações de CRUD em nossa API vamos testar para ver se está tudo funcionando corretamente. Para realizar esses testes precisamos de algum programa que consiga realizar requisições HTTP, eu particularmente gosto de utilizar o Insomnia, porém se você possui preferência por outro programa fique à vontade para utilizá-lo.
Primeiro precisamos executar a nossa aplicação, para isso basta executar o seguinte comando no terminal:
uvicorn main:app --reload
Lembrando que esse comando deve ser executado com o ambiente virtual ativado. Alguns instantes após a execução do comando acima você verá uma mensagem parecida com a seguinte:
INFO: Application startup complete.
Isso significa dizer que aplicação executou com sucesso e já podemos realizar requisições para a nossa API.
Primeiro vamos realizar um cadastro, para isso vamos enviar uma requisição com o verbo HTTP POST para a url http://localhost:8000/api/cursos
e no corpo da requisição iremos colocar os dados do curso a ser cadastrado.
No corpo da requisição irei colocar o seguinte JSON:
{
"titulo": "Python - Fundamentos",
"descricao": "Python é uma linguagem de altíssimo nível, orientada a objeto, de tipagem dinâmica, fortemente interpretada e interativa.",
"carga_horaria": 101,
"qtd_exercicios": 40
}
E ao testar a requisição no Insomnia teremos o seguinte resultado:
Agora vamos realizar a listagem de todos os cursos cadastrados, para isso basta enviar uma requisição com o verbo HTTP GET para a url http://localhost:8000/api/cursos
.
Vamos testar também a rota de busca de curso por id, para isso precisamos enviar uma requisição com o verbo HTTP GET para a url http://localhost:8000/api/cursos/1
.
Agora vamos testar a rota de atualização de curso por id, para isso vamos enviar uma requisição com o verbo HTTP PUT para url http://localhost:8000/api/cursos/1
e no corpo da requisição vamos enviar os novos dados a serem atualizados.
No corpo da requisição irei colocar o seguinte JSON:
{
"titulo": "Python - SQLAlchemy ORM",
"descricao": "Saia do básico e aprenda como realizar um dos melhores ORM do Python",
"carga_horaria": 251,
"qtd_exercicios": 33
}
E ao testar a requisição no Insomnia teremos o seguinte resultado:
E por fim vamos testar a nossa rota de exclusão de curso por id, para isso vamos enviar uma requisição com o verbo HTTP DELETE para a url http://localhost:8000/api/cursos/1
.
E com isso finalizamos os nossos testes, a API está funcionando perfeitamente. Recomendo que faça mais alguns testes e tente explorar outras possibilidades, como por exemplo, o que será retornado caso passe um id que não existe nas rotas de exclusão, busca por id e de atualização? E o que será que acontece caso deixe de informar alguns dos dados do corpo da requisição nas rotas de cadastro e de atualização?
Outro ponto importante é que o FastAPI gera a documentação do projeto de forma automatica, então não esqueça de acessar o endereço http://localhost:8000/docs
para ver como ficou a documentação gerada pelo FastAPI.
Curso OpenAPI - Documentando APIs
Conhecer o cursoConclusão
Caso queira ver o código-fonte do projeto desenvolvido nesse artigo ele está disponível nesse repositório do GitHub.
Neste artigo vimos como podemos criar uma API com todas as operações de CRUD utilizando o FastAPI e o SQLALchemy.
Nesse artigo vimos vários recursos do SQLAlchemy, mas isso é apenas o começo, existem muitos outros recursos e funcionalidades disponíveis nesta incrível ORM, caso queira aprender mais sobre SQLALchemy saiba que aqui na TreinaWeb nós temos o curso Python - SQLAlchemy ORM que possui 04h11 de vídeos e um total de 33 exercícios. Conheça também nossos outros cursos de Python.
Veja quais são os tópicos abordados durante o curso de Python - SQLAlchemy ORM:
- O que é o SQLAlchemy e como o ORM funciona;
- Como instalar o SQLAlchemy utilizando o PIP em qualquer sistema operacional através do PyCharm;
- Como utilizar o SQLAlchemy para conexão com bancos de dados MySQL;
- Como mapear entidades utilizando o modo declarativo;
- Como mapear entidades utilizando os Schemas;
- Como adicionar, editar e remover registros do banco de dados;
- Como listar, filtrar, buscar e ordenar registros do banco de dados;
- Como implementar os relacionamentos 1/N e N/N com o SQLALchemy;
- Como utilizar o Eager Loading na listagem de dados com relacionamentos;
- Como configurar o modo CASCADE para garantir a integridade do banco de dados na exclusão de registros.