Python Framework

Flask

Flask é o micro-framework minimalista para Python. Você tem roteamento, um objeto de requisição e templates Jinja em um único arquivo, podendo adicionar exatamente as extensões necessárias conforme o app cresce.

beginner14 min readUpdated 16 de set. de 2026
app.py
python
# app.py
from flask import Flask, jsonify

app = Flask(__name__)


@app.get("/health")
def health():
    return jsonify(status="ok")


if __name__ == "__main__":
    app.run(debug=True)
Lançado
2010
Linguagem
Python
Estilo
Minimalista, unopinionated
Ideia central
Um objeto app
Templates
Jinja2
Versão atual
3.x

Por que importa

Por que o Flask é um ótimo primeiro framework

Um único arquivo para começar

Importe o Flask, crie um app, adicione um decorator e execute. Não há gerador de projeto nem layout de pastas obrigatório para aprender primeiro.

Você escolhe as peças

O núcleo é pequeno propositalmente. Banco de dados, autenticação, formulários e migrações vêm de extensões bem mantidas que você escolhe adotar.

Python puro em tudo

Rotas são funções, a configuração é um dicionário e a requisição é um objeto que você pode ler. Nada fica escondido atrás de uma API de framework complexa.

O panorama completo

App, rota, blueprint

Um objeto app, decorators que mapeiam URLs para funções e blueprints que agrupam rotas conforme o projeto cresce.

O objeto app

Inicializar

Flask(__name__) cria a aplicação. Ele armazena a configuração, registra rotas e é o objeto que o servidor WSGI chama.

Rotas

Corresponder

O decorator @app.route vincula uma regra de URL a uma função. Conversores capturam valores tipados, como inteiros e caminhos.

Blueprints

Compor

Um Blueprint agrupa rotas, templates e arquivos estáticos em um módulo que pode ser registrado no app, muitas vezes mais de uma vez.

HTML5 de uma olhada

A caixa de ferramentas do Flask

Roteamento por Decorator

@app.get("/posts/<int:id>") vincula um padrão de URL a uma função de view.

Requisição e resposta

Leia request.args, request.form e request.get_json(); retorne strings, dicts ou objetos Response.

Templates Jinja2

render_template preenche o HTML com dados e escapa variáveis por padrão.

Blueprints

Divida um app grande em módulos com prefixos e seus próprios templates.

Extensões

SQLAlchemy, Login, Migrate e outros adicionam as peças que o núcleo deixa de fora.

Cliente de teste

app.test_client() faz requisições no processo, sem a necessidade de um servidor rodando.

Uma breve historia

De uma piada de 1º de abril a um pilar da web

  1. 2010

    Uma piada vira um framework

    Armin Ronacher lança o Flask, construído sobre Werkzeug e Jinja2, após um projeto de 1º de abril despertar interesse real.

    10
  2. 2011

    O ecossistema de extensões

    Flask-SQLAlchemy e outras extensões estabelecem o modelo de "núcleo pequeno, peças opcionais".

    11
  3. 2018

    Flask 1.0

    Após anos de versões 0.x, o Flask assume o compromisso com uma API pública estável.

    18
  4. 2021

    Flask 2.0

    Views assíncronas e decorators curtos como @app.get chegam, modernizando a experiência do desenvolvedor.

    21
  5. 2023

    Flask 3.0

    Um núcleo mais enxuto remove APIs obsoletas há muito tempo e refina o contexto de requisição.

    23
  6. Hoje

    Ainda a escolha minimalista

    O Flask continua sendo a escolha ideal para pequenos serviços, protótipos e ensino de fundamentos web.

    Hoje

O guia completo

Flask: Tudo que voce precisa saber

O que é Flask?

Flask é um framework web minimalista para Python. Ele fornece um objeto de aplicação, uma maneira de mapear URLs para funções e um modelo de requisição e resposta. Todo o restante — bancos de dados, autenticação, formulários, jobs em segundo plano — é uma decisão que você toma posteriormente, geralmente adicionando uma extensão.

Essa simplicidade é justamente o objetivo. O Django tem uma opinião sobre como um projeto deve ser estruturado; o Flask quase não tem. Para uma API pequena, uma ferramenta interna ou um primeiro projeto web, essa liberdade é um recurso. Você consegue ler o framework inteiro em uma tarde e entender cada linha do seu próprio app.

O Flask é construído sobre duas bibliotecas consolidadas: Werkzeug para a infraestrutura WSGI e Jinja2 para templates. Ambas são maduras e bem documentadas, e saber que elas estão por baixo explica a maior parte do comportamento do Flask.

O menor app Flask

Uma aplicação Flask completa pode caber em poucas linhas.

# app.py
from flask import Flask

app = Flask(__name__)


@app.get("/")
def index():
    return "Hello, world"


if __name__ == "__main__":
    app.run(debug=True)

Flask(__name__) cria a aplicação e informa onde encontrar templates e arquivos estáticos. O decorator vincula uma URL à função abaixo dele. app.run() inicia o servidor de desenvolvimento. O guard if __name__ == "__main__" evita que o servidor seja iniciado quando o módulo é importado por um teste ou por um servidor WSGI.

Roteamento com decorators

O decorator @app.route é o coração do Flask. Ele registra uma regra de URL e a função que responde a ela.

@app.route("/posts")
def list_posts():
    return "All posts"


@app.route("/posts/<int:post_id>")
def get_post(post_id):
    return f"Post {post_id}"

As regras podem conter partes variáveis escritas entre colchetes angulares. O Flask faz a correspondência da URL, converte o valor e o passa para a função como um argumento nomeado (keyword argument). Se nenhuma regra for correspondida, o Flask retorna automaticamente um erro 404.

Variáveis de URL e métodos HTTP

Os conversores tornam as variáveis type-safe antes da execução do seu código. Os mais comuns são string (o padrão), int, float, path (que permite barras) e uuid.

@app.get("/users/<username>")
def profile(username):
    return f"Profile for {username}"


@app.get("/files/<path:filepath>")
def serve_file(filepath):
    return f"File at {filepath}"

O Flask 2.0 adicionou atalhos para cada método. @app.get, @app.post, @app.put, @app.patch e @app.delete são mais claros do que passar uma lista de methods, e uma única função pode responder a vários métodos.

@app.route("/posts", methods=["GET", "POST"])
def posts():
    if request.method == "POST":
        return create_post()
    return list_posts()

O objeto request

Os dados recebidos ficam no objeto global request, que o Flask preenche para a requisição atual. Ele é um proxy, portanto, importá-lo apenas uma vez no topo do módulo é seguro.

from flask import request

@app.get("/search")
def search():
    query = request.args.get("q", "")
    page = request.args.get("page", 1, type=int)
    return {"query": query, "page": page}


@app.post("/posts")
def create_post():
    payload = request.get_json()
    title = payload["title"]
    return {"title": title}, 201

request.args contém os valores da query-string, request.form contém os posts de formulários, request.get_json() analisa o corpo JSON, request.files contém os uploads e request.headers fornece os headers brutos. Como esses são mapeamentos de múltiplos valores, use .get() e forneça valores padrão em vez de tentar acessar os índices cegamente.

Respostas e JSON

Uma view pode retornar uma string, um dicionário (que o Flask serializa para JSON), uma tupla de (body, status) ou (body, status, headers), ou um objeto Response completo.

from flask import jsonify, make_response

@app.post("/posts")
def create_post():
    post = {"id": 1, "title": "Hello"}
    return jsonify(post), 201


@app.get("/ping")
def ping():
    response = make_response({"pong": True})
    response.headers["Cache-Control"] = "no-store"
    return response

jsonify é a maneira explícita de retornar JSON: ele serializa os dados e define o content type. Retornar um dicionário também funciona, mas jsonify deixa a intenção mais clara e lida com mais tipos de dados.

Templates com Jinja2

render_template carrega um arquivo da pasta templates/ e o renderiza com os dados que você passa. O Jinja2 escapa variáveis por padrão, o que protege contra XSS.

from flask import render_template

@app.get("/posts/<int:post_id>")
def get_post(post_id):
    post = {"id": post_id, "title": "Hello", "body": "First post"}
    return render_template("post.html", post=post)
<!-- templates/post.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>{{ post.title }}</title>
  </head>
  <body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.body }}</p>
  </body>
</html>

O Jinja suporta {% extends %} para herança, {% include %} para fragmentos, {% for %} e {% if %} para lógica, e filtros como {{ value|upper }}. Mantenha os templates focados na apresentação; se a lógica crescer, mova-a para a view.

Arquivos estáticos

Por padrão, o Flask serve qualquer conteúdo na pasta static/ em /static/.... Referencie-a com url_for, que constrói a URL a partir do nome do endpoint.

<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />

Em produção, utilize um reverse proxy ou CDN para servir arquivos estáticos em vez do processo Python. Isso é mais rápido e libera o app para processar as requisições.

Blueprints

Um Blueprint é uma coleção de rotas e templates que podem ser registrados em um app. É assim que um app Flask de módulo único cresce para se tornar um projeto sem virar uma bagunça.

# blog/views.py
from flask import Blueprint, jsonify

bp = Blueprint("blog", __name__, url_prefix="/blog")


@bp.get("/")
def index():
    return jsonify(posts=[])


@bp.get("/<int:post_id>")
def detail(post_id):
    return jsonify(id=post_id)
# app.py
from blog.views import bp as blog_bp

app.register_blueprint(blog_bp)

Blueprints possuem seu próprio prefixo de URL, templates e arquivos estáticos, e um único blueprint pode ser registrado várias vezes com prefixos diferentes. Eles também facilitam os testes e a revisão de código, pois cada funcionalidade é autocontida.

A factory de aplicação

Assim que você começa a usar blueprints, o próximo passo natural é criar uma função factory para construir o app. Esta é a estrutura padrão para qualquer projeto que seja maior que uma demo.

# app.py
from flask import Flask

from blog.views import bp as blog_bp


def create_app(config_object="config.Config"):
    app = Flask(__name__)
    app.config.from_object(config_object)

    app.register_blueprint(blog_bp)

    return app
# wsgi.py
from app import create_app

app = create_app()

A factory torna os testes triviais: você constrói um app com uma configuração de teste, executa o cliente e depois o descarta. Ela também evita a importação de um app global parcialmente configurado, que é a raiz da maioria dos problemas de importação circular no Flask.

Extensões

Como o núcleo é minimalista, o ecossistema fornece o restante. Algumas extensões cobrem a maioria dos projetos:

  • Flask-SQLAlchemy integra o ORM do SQLAlchemy e gerencia a sessão por requisição.
  • Flask-Migrate encapsula o Alembic para que as alterações de schema sejam versionadas.
  • Flask-Login gerencia sessões de usuário e o proxy current_user.
  • Flask-WTF adiciona formulários, proteção CSRF e upload de arquivos.
  • Flask-CORS controla requisições cross-origin para APIs.
# extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()
# app.py
from extensions import db, migrate


def create_app():
    app = Flask(__name__)
    app.config.from_object("config.Config")

    db.init_app(app)
    migrate.init_app(app, db)

    return app

O padrão init_app permite que uma extensão seja criada sem um app e vinculada posteriormente, o que mantém a factory limpa e a extensão importável por models.

Configuração

A configuração é um mapeamento simples em app.config. Carregue-a a partir de uma classe, de um arquivo ou do ambiente, e mantenha segredos fora do controle de versão.

# config.py
import os


class Config:
    SECRET_KEY = os.environ["SECRET_KEY"]
    SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
    JSON_SORT_KEYS = False
app.config.from_object("config.Config")
app.config.from_prefixed_env()

from_prefixed_env() lê variáveis de ambiente de FLASK_*, o que é conveniente em containers. Use classes ou arquivos diferentes por ambiente e nunca faça commit da SECRET_KEY real — ela assina sessões e tokens CSRF.

Tratamento de erros

Registre manipuladores de erro (error handlers) para retornar respostas consistentes em todo o app. Os handlers podem ser globais ou limitados a um blueprint.

from flask import jsonify
from werkzeug.exceptions import HTTPException


@app.errorhandler(404)
def not_found(error):
    return jsonify(error="not_found"), 404


@app.errorhandler(Exception)
def unhandled(error):
    if isinstance(error, HTTPException):
        return error
    app.logger.exception(error)
    return jsonify(error="internal_error"), 500

O handler para HTTPException é importante: os erros nativos do Flask também são exceções e, caso contrário, um handler genérico de Exception transformaria um erro 404 em um 500.

Testes

O cliente de teste do Flask faz requisições no mesmo processo, sem a necessidade de um servidor ou de rede.

import pytest

from app import create_app


@pytest.fixture
def client():
    app = create_app("config.TestConfig")
    with app.test_client() as client:
        yield client


def test_health(client):
    response = client.get("/health")
    assert response.status_code == 200
    assert response.get_json() == {"status": "ok"}


def test_missing_post(client):
    response = client.get("/posts/999")
    assert response.status_code == 404

A factory e o test_client() trabalham juntos: cada teste recebe uma instância nova do app, evitando que o estado vaze entre os testes. Sobrescreva a configuração para os testes e utilize um banco de dados em memória quando for adicionar um.

WSGI e deployment

O Flask é uma aplicação WSGI, o que significa que qualquer servidor WSGI pode executá-lo. O servidor de desenvolvimento serve apenas para fins de desenvolvimento.

pip install gunicorn
gunicorn --workers 4 --bind 0.0.0.0:8000 "app:create_app()"

Em produção, execute o Gunicorn ou uWSGI atrás do Nginx, finalize o TLS no proxy e sirva arquivos estáticos a partir do proxy ou de um CDN. Configure DEBUG=False, configure o logging e leia os secrets do ambiente. Containerizar a aplicação torna o deployment portátil entre diferentes hosts.

Melhores práticas

  • Comece com um único módulo, depois migre para factories e blueprints conforme o projeto crescer.
  • Use url_for para construir URLs em vez de fixar caminhos no código (hardcoding).
  • Mantenha as configurações em classes ou variáveis de ambiente, nunca diretamente no código.
  • Use jsonify e códigos de status explícitos para respostas de API.
  • Valide cada dado de entrada antes que ele chegue à sua lógica de negócio.
  • Registre manipuladores de erro (error handlers) para que as falhas retornem um formato consistente.
  • Use o cliente de teste e uma instância nova do app para cada teste.
  • Nunca execute o servidor de desenvolvimento em produção.

Erros comuns

  • Deixar o debug=True ativado em produção, o que expõe um debugger interativo.
  • Importar um app global em todos os lugares, criando importações circulares.
  • Ler o request.json sem tratar corpos malformados ou ausentes.
  • Colocar URLs fixas (hardcoded) nos templates em vez de usar url_for.
  • Construir um app.py monolítico com centenas de rotas.
  • Esquecer a proteção CSRF ao utilizar formulários.
  • Armazenar segredos no config.py e fazer o commit do arquivo.
  • Presumir que o Flask possui um ORM ou sistema de autenticação nativo e acabar reinventando ambos de forma ineficiente.

Próximos passos

O Flask é a maneira mais rápida de aprender o que um web framework realmente faz, pois quase nada fica oculto. Quando você precisar de um ORM, migrations, auth e um painel administrativo sem ter que montar tudo do zero, leia o guia do Django. Se quiser validação automática e documentação de API a partir de type hints, mude para o FastAPI. Para projetar os endpoints que você acabou de criar, o guia de REST APIs é o companheiro ideal. Se você adicionar um banco de dados, o roadmap de backend cobre SQL, a linguagem que sustenta o ORM.

Retornando JSON

jsonify serializa dados Python, define o content type como application/json e manipula códigos de status. json.dumps deixa você construir toda a resposta manualmente.

Preferir
from flask import jsonify

@app.get("/posts/<int:id>")
def get_post(id):
    return jsonify(id=id, title="Hello")
Evitar
import json
from flask import Response

@app.get("/posts/<int:id>")
def get_post(id):
    body = json.dumps({"id": id, "title": "Hello"})
    return Response(body, mimetype="application/json")

Criando o app

Uma factory function permite que testes criem um app novo com configuração de teste, enquanto um app em nível de módulo é criado apenas uma vez na importação e é difícil de reconfigurar.

Preferir
def create_app(config=None):
    app = Flask(__name__)
    app.config.from_object(config or "config.Config")
    register_blueprints(app)
    return app
Evitar
app = Flask(__name__)
app.config["SECRET_KEY"] = "dev"

# imported everywhere; hard to test in isolation

Trade-offs

O Flask é a escolha certa?

O Flask prioriza a liberdade e uma superfície de API reduzida. Isso é libertador para apps pequenos, mas pode se tornar um fardo conforme o app cresce.

Strengths

  • Você entende tudo

    Há pouquíssima 'mágica'. Cada rota, valor de configuração e extensão é visível, o que torna o Flask um framework excelente para aprender como a web funciona.

  • Minimalista e flexível

    Você escolhe o banco de dados, a biblioteca de validação e a estratégia de autenticação, em vez de herdar decisões que não tomou.

  • Perfeito para pequenos serviços

    Um microserviço JSON ou uma ferramenta interna costuma ser um único arquivo, e nada no framework impede isso.

Trade-offs

  • Você monta a stack

    Autenticação, migrações, formulários e admin são sua responsabilidade. Cada extensão tem seus próprios padrões e sua própria curva de aprendizado.

  • As convenções são você quem inventa

    Nada impõe um layout de projeto, então dois apps Flask podem parecer completamente diferentes. Equipes grandes precisam de seu próprio guia de estilo.

  • Menos recursos nativos

    Headers de segurança, comportamento de ORM e validação são opcionais. Você deve lembrar de adicioná-los, em vez de estar protegido por padrão.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Flask?

Nosso tutorial interativo te guia por Flask passo a passo — com quizzes e codigo real que voce pode executar no navegador.