Criando sua primeira API REST de tarefas com Python e Flask

Uma API de tarefas completa (listar, criar, buscar, atualizar e apagar) em um único arquivo Python, com os códigos de status certos, validação básica e testes pelo cURL.

Fala pessoal, tudo bem?

Neste post vamos criar uma API simples de gerenciamento de tarefas, que roda localmente em qualquer sistema operacional. Vamos usar o Flask, um framework leve para Python. No final, você terá uma API funcional que serve de base para os seus próprios projetos.

Esta é uma versão revisada: além do básico, a API agora gera o id sozinha, valida o que recebe e responde com os códigos HTTP corretos, que são justamente os detalhes que fazem diferença quando outra aplicação vai consumir a sua API.

1. Ambiente

Você precisa do Python 3 instalado (python3 --version para conferir). Crie um ambiente virtual e instale o Flask. Se ainda não conhece o venv, expliquei neste post.

mkdir api-tarefas && cd api-tarefas
python3 -m venv .venv
source .venv/bin/activate        # no Windows: .venv\Scripts\Activate.ps1
pip install flask

2. A aplicação

Crie o arquivo app.py:

from flask import Flask, jsonify, request, abort

app = Flask(__name__)

# "banco de dados" em memória: some quando o servidor reinicia
tasks = []
next_id = 1


def find_task(task_id):
    task = next((t for t in tasks if t["id"] == task_id), None)
    if task is None:
        abort(404, description="Tarefa não encontrada")
    return task


@app.get("/api/tasks")
def list_tasks():
    return jsonify(tasks)


@app.get("/api/tasks/<int:task_id>")
def get_task(task_id):
    return jsonify(find_task(task_id))


@app.post("/api/tasks")
def create_task():
    global next_id
    data = request.get_json(silent=True) or {}
    if not data.get("title"):
        abort(400, description="O campo 'title' é obrigatório")
    task = {"id": next_id, "title": data["title"], "done": False}
    next_id += 1
    tasks.append(task)
    return jsonify(task), 201


@app.put("/api/tasks/<int:task_id>")
def update_task(task_id):
    task = find_task(task_id)
    data = request.get_json(silent=True) or {}
    task["title"] = data.get("title", task["title"])
    task["done"] = bool(data.get("done", task["done"]))
    return jsonify(task)


@app.delete("/api/tasks/<int:task_id>")
def delete_task(task_id):
    tasks.remove(find_task(task_id))
    return "", 204


@app.errorhandler(400)
@app.errorhandler(404)
def error(e):
    return jsonify(error=e.description), e.code


if __name__ == "__main__":
    app.run(debug=True)
💡 O que mudou em relação à primeira versão: o cliente não precisa mais mandar o id (a API gera), criar sem título devolve 400, buscar, atualizar ou apagar uma tarefa que não existe devolve 404, e apagar devolve 204 (sucesso, sem conteúdo). Todas as respostas, inclusive os erros, saem em JSON.

3. Rodando

python app.py

Acesse http://127.0.0.1:5000/api/tasks no navegador. Como ainda não criamos nenhuma tarefa, a resposta é uma lista vazia:

4. Testando com cURL

# criar
curl -X POST http://127.0.0.1:5000/api/tasks \
     -H "Content-Type: application/json" \
     -d '{"title": "Estudar Flask"}'

# listar
curl http://127.0.0.1:5000/api/tasks

# marcar como concluída
curl -X PUT http://127.0.0.1:5000/api/tasks/1 \
     -H "Content-Type: application/json" \
     -d '{"done": true}'

# apagar (devolve 204, sem corpo)
curl -i -X DELETE http://127.0.0.1:5000/api/tasks/1

# testar o erro: criar sem título devolve 400
curl -i -X POST http://127.0.0.1:5000/api/tasks \
     -H "Content-Type: application/json" -d '{}'
💡 Dica: o -i do cURL mostra o código HTTP e os cabeçalhos da resposta. É o jeito mais rápido de conferir se a API está devolvendo 201, 204, 400 e 404 nos lugares certos. Se preferir interface gráfica, ferramentas como Postman, Insomnia ou Bruno fazem o mesmo.

Próximos passos

  • Persistência: trocar a lista em memória por um banco SQLite, para as tarefas não sumirem a cada reinício;
  • Testes automatizados: o Flask tem um cliente de testes (app.test_client()) que funciona muito bem com o pytest;
  • Versionamento: colocar o projeto no Git, com o .venv no .gitignore e as dependências em um requirements.txt.

Considerações Finais

Com pouco mais de 50 linhas, temos uma API REST funcional, com os verbos e os códigos de status certos. É um ótimo exercício para entender como as APIs funcionam por dentro, antes de partir para frameworks maiores ou para um banco de dados de verdade.

Se tiver alguma dúvida, deixa nos comentários!

Abs e até a próxima.

:wq!

Post publicado originalmente em outubro de 2024 e atualizado em novembro de 2026.

Comentários