Criando o primeiro CRUD com FastAPI \| TreinaWeb               Promoção no ar   Matricule-se agora e garanta **54% de desconto**  

  Termina em   Tempo restante da oferta: 01 Horas 

 19 Min 

 59 Seg 

   Tempo restante da oferta: 01 19 59 

  [Assinar →](https://www.treinaweb.com.br/checkout/pagamento/plano/anual?cupom=TW-SCLIENTE26) 

   [                ](https://www.treinaweb.com.br)  Explorar  

 [ Quanto custa? ](#assinatura) [ FAQ ](https://www.treinaweb.com.br#faq) [Matricule-se](#assinatura) [ Entrar ](https://www.treinaweb.com.br/login) 

     [ Início ](https://www.treinaweb.com.br) [ Artigos ](https://www.treinaweb.com.br/blog) Criando o primeiro CRUD com FastAPI  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) [ Python ](https://www.treinaweb.com.br/blog/categoria/python) 

# Criando o primeiro CRUD com FastAPI

 Neste artigo nós iremos aprender um pouco mais sobre o FastAPI durante o desenvolvimento de uma API com todas as operações de CRUD.

 [ Cleyson Lima ](https://www.treinaweb.com.br/blog/autor/cleyson-lima) 4 anos atrás

  ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/03/o-que-e-fastapi.png)  

No [artigo anterior](https://www.treinaweb.com.br/blog/o-que-e-fastapi) 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](https://www.treinaweb.com.br/blog/o-que-e-uma-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](https://www.treinaweb.com.br/blog/o-que-e-orm) que será o [SQLAlchemy](https://www.treinaweb.com.br/blog/o-que-e-o-sqlalchemy) e como banco de dados vamos utilizar o SQLite.

[  ![Desenvolvedor Python](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/python.svg)   Formação Desenvolvedor Python Conhecer a formação ](https://www.treinaweb.com.br/formacao/desenvolvedor-python)

## Criando 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](https://www.treinaweb.com.br/blog/instalacao-do-python-e-nosso-primeiro-ola-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](https://www.treinaweb.com.br/blog/criando-ambientes-virtuais-para-projetos-python-com-o-virtualenv) com os seguintes comandos:

- No Linux e MacOS

```bash
python3 -m venv .venv
source .venv/bin/activate

```

- No Windows:

```powershell
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](https://www.treinaweb.com.br/blog/gerenciando-pacotes-em-projetos-python-com-o-pip).

```bash
pip install fastapi uvicorn[standard] sqlalchemy

```

Após a execução do comando acima teremos feito a instalação das seguintes bibliotecas:

- [FastAPI](https://fastapi.tiangolo.com/): Microframework voltada para o desenvolvimento de Web APIs;
- [Uvicorn](https://www.uvicorn.org/): Web server [ASGI](https://asgi.readthedocs.io/en/latest/) implementado em Python, necessário para a execução do nosso projeto;
- [SQLAlchemy](https://www.sqlalchemy.org/): 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:

```python
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.

[  ![Python - SQLAlchemy ORM](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/python.svg)   Curso Python - SQLAlchemy ORM Conhecer o curso ](https://www.treinaweb.com.br/curso/python-sqlalchemy)

## Criando 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:

```python
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](https://docs.python.org/3/library/typing.html), esse recurso serve basicamente para que possamos trabalhar com tipagem em nosso código Python, isso é bem similar ao que o [TypeScript](https://www.treinaweb.com.br/blog/tipagem-no-typescript) faz para o [JavaScript](https://www.treinaweb.com.br/blog/o-que-e-e-como-comecar-com-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:

```python
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 do `id` do curso;
- `exists_by_id(db: Session, id: int) -> bool`: método responsável por verificar se existe algum curso cadastrado com base no `id` do curso;
- `delete_by_id(db: Session, id: int) -> None`: método responsável por excluir um curso com base no seu `id`.

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.

[  ![HTTP - Fundamentos para desenvolvedores](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/http.svg)   Curso HTTP - Fundamentos para desenvolvedores Conhecer o curso ](https://www.treinaweb.com.br/curso/http-fundamentos-para-desenvolvedores)

## Criando 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](https://pydantic-docs.helpmanual.io/) 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:

```python
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:

```python
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`.

[  ![APIs Rest - Fundamentos](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/apis-rest.svg)   Curso APIs Rest - Fundamentos Conhecer o curso ](https://www.treinaweb.com.br/curso/apis-rest-fundamentos)

### Rota 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`.

```python
# 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.

```python
# 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.

[  ![Desenvolvedor Flask Full-Stack](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/flask.svg)   Formação Desenvolvedor Flask Full-Stack Conhecer a formação ](https://www.treinaweb.com.br/formacao/desenvolvedor-flask-full-stack)

### Rota 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.

```python
# 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.

```python
@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.

```python
@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:

```python
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](https://insomnia.rest/), 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:

```bash
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](https://www.treinaweb.com.br/blog/o-que-e-json):

```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:

![Print do Insomnia testando a rota de cadastro de cursos da nossa API com FastAPI e SQLAlchemy](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/print-insomnia-teste-rota-de-cadastro.png)

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`.

![Print do Insomnia testando a rota de listagem de cursos da nossa API com FastAPI e SQLAlchemy](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/print-insomnia-teste-rota-de-listagem.png)

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`.

![Print do Insomnia testando a rota de busca de curso por id da nossa API com FastAPI e SQLAlchemy](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/print-insomnia-teste-rota-de-busca-por-id.png)

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:

```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:

![Print do Insomnia testando a rota de atualização de curso da nossa API com FastAPI e SQLAlchemy](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/print-insomnia-teste-rota-de-atualizacao.png)

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`.

![Print do Insomnia testando a rota de exclusão de curso da nossa API com FastAPI e SQLAlchemy](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/print-insomnia-teste-rota-de-exclusao.png)

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.

[  ![OpenAPI - Documentando APIs](https://d2knvm16wkt3ia.cloudfront.net/assets/svg-icon/openapi.svg)   Curso OpenAPI - Documentando APIs Conhecer o curso ](https://www.treinaweb.com.br/curso/openapi-documentando-apis)

## Conclusão

Caso queira ver o código-fonte do projeto desenvolvido nesse artigo ele está disponível nesse [repositório do GitHub](https://github.com/treinaweb/treinaweb-artigo-api-com-fastapi-e-sqlalchemy).

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](https://www.treinaweb.com.br/) nós temos o curso [Python - SQLAlchemy ORM](https://www.treinaweb.com.br/curso/python-sqlalchemy) que possui **04h11 de vídeos** e um total de **33 exercícios**. Conheça também nossos outros [cursos de Python](https://www.treinaweb.com.br/cursos?q=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.

- [ #API ](https://www.treinaweb.com.br/blog/tag/api)
- [ #Python ](https://www.treinaweb.com.br/blog/tag/python)
- [ #API REST ](https://www.treinaweb.com.br/blog/tag/api-rest)
- [ #SQLAlchemy ](https://www.treinaweb.com.br/blog/tag/sqlalchemy)
- [ #ORM ](https://www.treinaweb.com.br/blog/tag/orm)
- [ #FastAPI ](https://www.treinaweb.com.br/blog/tag/fastapi)

Autor

 ![Cleyson Lima](//secure.gravatar.com/avatar/db30efb102ffa5d5204cebe12060b111?d=mm)

## Cleyson Lima

 Professor, programador, fã de One Piece e finge saber cozinhar. Cleyson é graduando em Licenciatura em Informática pelo IFPI - Campus Teresina Zona Sul, nos anos de 2019 e 2020 esteve envolvido em vários projetos coordenados pela secretaria municipal de educação da cidade de Teresina, onde o foco era introduzir alunos da rede pública no mundo da programação e robótica. Hoje é instrutor dos cursos de Spring na TreinaWeb, mas diz que seu coração sempre pertencerá ao Python. 

 [Todos os artigos](https://www.treinaweb.com.br/blog/autor/cleyson-lima)  

## Continue estudando

 Transforme leitura em prática com os cursos e formações da TreinaWeb.

 [Ver cursos](https://www.treinaweb.com.br/cursos)   

  Continue lendo

## Artigos relacionados

- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2018/08/07161700/gerenciamento-python-400x280.png)  [ Python ](https://www.treinaweb.com.br/blog/categoria/python) 

     4 anos atrás

    ##  [ Empacotando e publicando pacotes Python no PyPi ](https://www.treinaweb.com.br/blog/empacotando-e-publicando-pacotes-python-no-pypi) 

     Neste artigo iremos aprender como funciona o processo de empacotar e distribuir nossos próprios pacotes Python no PyPi.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/07/CRIANDO-PRIMEIRO-CRUD-COM-NESTJS-400x280.png)  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) 

     4 anos atrás

    ##  [ Criando o primeiro CRUD com NestJS ](https://www.treinaweb.com.br/blog/criando-o-primeiro-crud-com-nestjs) 

     Neste artigo veremos como criar um CRUD com NestJS, o passo a passo utilizando ferramentas como o TypeORM e o banco de dados SQLite.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2023/05/NEST-JS-UPLOAD-ARQUIVOS-400x280.png)  [ Node ](https://www.treinaweb.com.br/blog/categoria/node-js) 

     3 anos atrás

    ##  [ Criando CRUD com Prisma ORM e NestJS ](https://www.treinaweb.com.br/blog/criando-crud-com-prisma-orm-e-nestjs) 

     Neste artigo vamos implementar um CRUD com Prisma ORM e NestJS, abordando as principais operações básicas com o banco de dados.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/01/criando-uma-api-com-spring-data-jpa-e-spring-web-400x280.png)  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) 

     5 anos atrás

    ##  [ Criando uma API com Spring Data JPA e Spring Web ](https://www.treinaweb.com.br/blog/criando-uma-api-com-spring-data-jpa-e-spring-web) 

     Neste artigo vamos aprender como criar uma API com persistência de dados utilizando o Spring Web MVC e o Spring Data JPA.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2020/05/criando-api-restful-jax-rs-api-400x280.png)  [ Java ](https://www.treinaweb.com.br/blog/categoria/java) 

     7 anos atrás

    ##  [ Criando uma API RESTful com a JAX-RS API ](https://www.treinaweb.com.br/blog/criando-uma-api-restful-com-a-jax-rs-api) 

     O Java define a especificação JAX-RS API para a criação de aplicações API RESTful, que pode ser facilmente utilizada com a implementação do Jersey.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2019/12/03143715/CARTER-400x280.png)  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) 

     7 anos atrás

    ##  [ Criando uma API RESTful com o Carter e .NET Core ](https://www.treinaweb.com.br/blog/criando-uma-api-restful-com-o-carter-e-net-core) 

     O Carter é um framework para .NET Core, que permite criar API RESTful de forma rápida, simples e elegante. O conheça neste artigo.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2019/03/11153540/DJANGO-ADMIN-400x280.png)  [ Django ](https://www.treinaweb.com.br/blog/categoria/django) 

     8 anos atrás

    ##  [ Criando uma aplicação administrativa com Django Admin ](https://www.treinaweb.com.br/blog/criando-uma-aplicacao-administrativa-com-django-admin) 

     Neste artigo veremos como criar uma aplicação administrativa com Django.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2019/07/05112250/SPRING-BOOT-400x280.png)  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) 

     7 anos atrás

    ##  [ Criando uma API REST com o Spring Boot ](https://www.treinaweb.com.br/blog/criando-uma-api-rest-com-o-spring-boot) 

     Todos os passos da criação de uma API REST com o framework Spring Boot do Java, no Visual Studio Code.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2018/10/01110647/strapi-400x280.png)  [ Node ](https://www.treinaweb.com.br/blog/categoria/node-js) 

     8 anos atrás

    ##  [ Criando APIs com o Strapi.io ](https://www.treinaweb.com.br/blog/criando-apis-com-o-strapi-io) 

     Veja neste artigo como criar APIs de forma rápida e fácil utilizando o Strapi.io.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2021/05/opera--es-crud-400x280.jpg)  [ C# ](https://www.treinaweb.com.br/blog/categoria/csharp) 

     10 anos atrás

    ##  [ Operações CRUD no ASP.NET MVC 5 com o ADO.NET ](https://www.treinaweb.com.br/blog/operacoes-crud-no-asp-net-mvc-5-com-o-ado-net) 

     Exemplo de como criar uma aplicação ASP.NET MVC utilizando ADO.NET como framework de acesso aos dados.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2019/06/11153116/symfony-projeto-e-crud-400x280.png)  [ PHP ](https://www.treinaweb.com.br/blog/categoria/php) 

     8 anos atrás

    ##  [ Iniciando com Symfony: Como criar o projeto e seu primeiro CRUD ](https://www.treinaweb.com.br/blog/iniciando-com-symfony-como-criar-o-projeto-e-seu-primeiro-crud) 

     Nesse artigo veremos como criar o primeiro projeto no Symfony. Falaremos sobre qual tipo de estrutura escolher dependendo da aplicação e como criar o primeiro CRUD utilizando o comando make.

     Ler artigo
- ![](https://dkrn4sk0rn31v.cloudfront.net/uploads/2022/08/NEST-JS-CLASS-VALIDATOR-CLASS-TRANSFORMER-400x280.png)  [ Desenvolvimento Back-end ](https://www.treinaweb.com.br/blog/categoria/desenvolvimento-back-end) 

     4 anos atrás

    ##  [ Validação e Serialização no NestJS: class-validator e class-transformer ](https://www.treinaweb.com.br/blog/validacao-e-serializacao-no-nestjs-class-validator-e-class-transformer) 

     Vamos abordar a utilização de duas biblitecas de serialização e validação fundamentais no NestJS: class-validator e class-transformer

     Ler artigo

## Um ano de acesso total para você viver de programação.

 Todos os cursos e formações da TreinaWeb numa única assinatura, com professores que atuam no mercado, suporte direto e certificados. Do primeiro `console.log` ao mercado de trabalho.

- **1 ano** de acesso a **todos os cursos**
- Cursos novos **toda semana**
- Suporte **direto com professores**
- Plano de carreira **personalizado**
- **Certificados digitais** inclusos
- **Aplicativos** para Android e iOS, com **acesso offline**

    **Garantia incondicional de satisfação** 

 Teste por 7 dias e, se não fizer sentido para você, devolvemos seu dinheiro.

### Acesso completo a todos os cursos e dezenas de Formações.

 12 meses 

  Economize R$ 840,00  54% OFF 

 de 12x R$ 129,00 por apenas

 12x  R$ 59,00

 sem juros no cartão de crédito ou R$ 599,00 à vista

  Oferta termina em  **01h 19min 59s**

- Acesso imediato e ilimitado durante 1 ano.
- Plano de carreira personalizado.
- Suporte direto com professores.
- E muito mais!

 [Matricule-se agora](https://www.treinaweb.com.br/checkout/pagamento/plano/anual?cupom=TW-SCLIENTE26)

     [                ](https://www.treinaweb.com.br)

 Escola online de programação, tecnologia e IA.

- [  ](https://www.facebook.com/TreinaWeb/)
- [  ](https://www.instagram.com/treinaweb/)
- [  ](https://www.youtube.com/user/TreinaWeb)
- [  ](https://twitter.com/treinaweb)
- [  ](https://www.linkedin.com/company/treinaweb)
- [  ](https://github.com/treinaweb)

## Inscreva-se em nossa newsletter

Receba lançamentos, promoções e novidades.

   Seu nome     Seu WhatsApp     Seu melhor e-mail   Inscrever-se 

## Cursos

- [ Base para Devs ](https://www.treinaweb.com.br/categoria/base-para-devs)
- [ Front-end ](https://www.treinaweb.com.br/categoria/desenvolvimento-front-end)
- [ Back-end ](https://www.treinaweb.com.br/categoria/desenvolvimento-back-end)
- [ Mobile ](https://www.treinaweb.com.br/categoria/desenvolvimento-mobile)
- [ Bancos de Dados ](https://www.treinaweb.com.br/categoria/bancos-de-dados)
- [ Cloud ](https://www.treinaweb.com.br/categoria/cloud-computing)
- [ IA ](https://www.treinaweb.com.br/categoria/ia)
- [ E mais ](https://www.treinaweb.com.br/categoria/e-mais)
- [ Ver todos os cursos ](https://www.treinaweb.com.br/cursos)

## Conteúdo

- [ Formações ](https://www.treinaweb.com.br/formacoes)
- [ Projetos práticos ](https://www.treinaweb.com.br/projetos-praticos)
- [ Direto ao ponto ](https://www.treinaweb.com.br/direto-ao-ponto)
- [ Planos ](https://www.treinaweb.com.br#assinatura)
- [ Depoimentos ](https://www.treinaweb.com.br#depoimentos)
- [ Comunidade ](https://www.treinaweb.com.br#comunidade)
- [ Corpo docente ](https://www.treinaweb.com.br#docente)

## A empresa

- [ Sobre a TreinaWeb ](https://www.treinaweb.com.br#sobre)
- [ Perguntas frequentes ](https://www.treinaweb.com.br#faq)
- [ Vantagens de estudar na TreinaWeb ](https://www.treinaweb.com.br/vantagens-de-estudar-na-treinaweb)
- [ Cursos para empresas ](https://www.treinaweb.com.br/empresas)
- [ Sugira um novo curso ](https://www.treinaweb.com.br/sugerir-curso)
- [ Trabalhe conosco ](https://www.treinaweb.com.br/trabalhe-conosco)
- [ Artigos ](https://www.treinaweb.com.br/blog)
- [ Desenvolvedores e agentes ](https://www.treinaweb.com.br/developers)
- [ Contato ](https://www.treinaweb.com.br/contato)
- [ Política de privacidade ](https://www.treinaweb.com.br/politica-de-privacidade)
- [ Termos de uso ](https://www.treinaweb.com.br/termos-de-uso)

## Baixe nosso aplicativo

 [   Disponível na **App Store**  ](https://apps.apple.com/us/app/treinaweb/id1322729447) [   Disponível no **Google Play**  ](https://play.google.com/store/apps/details?id=br.com.treinaweb.mobile) 

  Av. Paulista, 1765, Conj 71 e 72 · Bela Vista · São Paulo · SP · 01311-200   © 2004 – 2026 TreinaWeb Tecnologia LTDA · CNPJ 06.156.637/0001-58
