Front-end é a parte que roda no navegador e que você vê. Back-end é o programa no servidor que aplica as regras e conversa com outros sistemas. Banco de dados é onde as informações ficam guardadas quando ninguém está olhando. A divisão fica clara de verdade quando você vê as três peças em um aplicativo que funciona.
O exemplo abaixo é um buscador de CEP que guarda o histórico das consultas. São três arquivos curtos, testados em outubro de 2026 com Python 3.12, Flask 3.1.3 e a API pública da ViaCEP.
O aplicativo e suas três peças
| Camada | Arquivo | Roda onde | Faz o quê |
|---|---|---|---|
| Front-end | static/index.html | No navegador do usuário | Mostra o campo, envia o CEP, exibe a resposta |
| Back-end | app.py | No servidor | Valida o CEP, consulta a ViaCEP, grava e lê o histórico |
| Banco de dados | consultas.db (SQLite) | No disco do servidor | Guarda cada consulta com data e hora |
Para rodar no seu computador: instale o Python, crie a pasta do projeto, rode pip install flask requests, salve os dois arquivos abaixo e execute flask --app app run. O endereço http://127.0.0.1:5000 abre o aplicativo.
Front-end: a página que o usuário toca
<!doctype html>
<html lang="pt-BR">
<head><meta charset="utf-8"><title>Buscador de CEP</title></head>
<body>
<input id="cep" placeholder="Só números, ex.: 01001000" maxlength="8">
<button id="buscar">Buscar</button>
<p id="resultado"></p>
<script>
document.querySelector("#buscar").addEventListener("click", async () => {
const cep = document.querySelector("#cep").value.trim();
const saida = document.querySelector("#resultado");
const resposta = await fetch(`/api/cep/${cep}`);
const dados = await resposta.json();
saida.textContent = resposta.ok
? `${dados.logradouro}, ${dados.cidade}/${dados.uf}`
: dados.erro;
});
</script>
</body>
</html>
O HTML define a estrutura (campo, botão, parágrafo), e o JavaScript dá o comportamento: ao clicar, faz uma requisição ao back-end com fetch e escreve a resposta na tela, sem recarregar a página. Em um projeto real, o CSS cuidaria da aparência e um framework como React organizaria telas mais complexas.
Dois detalhes têm a ver com segurança. O maxlength="8" ajuda o usuário, mas não protege nada, porque qualquer pessoa pode editar a página pelo navegador ou chamar o endereço direto; a validação de verdade está no back-end. E o resultado é escrito com textContent, não com innerHTML, para que um texto vindo de fora nunca seja interpretado como código na página.
Back-end: as regras que ninguém pode burlar
import sqlite3
import requests
from flask import Flask, jsonify
app = Flask(__name__)
BANCO = "consultas.db"
def conectar():
con = sqlite3.connect(BANCO)
con.execute(
"CREATE TABLE IF NOT EXISTS consultas ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" cep TEXT NOT NULL,"
" cidade TEXT,"
" criado_em TEXT DEFAULT CURRENT_TIMESTAMP)"
)
return con
@app.get("/")
def inicio():
return app.send_static_file("index.html")
@app.get("/api/cep/<cep>")
def buscar_cep(cep):
if not (cep.isdigit() and len(cep) == 8):
return jsonify(erro="O CEP deve ter 8 dígitos"), 400
dados = requests.get(f"https://viacep.com.br/ws/{cep}/json/", timeout=10).json()
if dados.get("erro"):
return jsonify(erro="CEP não encontrado"), 404
with conectar() as con:
con.execute(
"INSERT INTO consultas (cep, cidade) VALUES (?, ?)",
(cep, dados["localidade"]),
)
return jsonify(logradouro=dados["logradouro"], cidade=dados["localidade"], uf=dados["uf"])
@app.get("/api/historico")
def historico():
with conectar() as con:
linhas = con.execute(
"SELECT cep, cidade, criado_em FROM consultas ORDER BY id DESC LIMIT 10"
).fetchall()
return jsonify([{"cep": c, "cidade": cid, "quando": q} for c, cid, q in linhas])
Cada função com @app.get é um endpoint, ou seja, um endereço que o front-end pode chamar. Juntos, eles formam a API do aplicativo; o conceito é explicado com outros exemplos em o que é API.
O back-end faz quatro trabalhos que não podem ficar no navegador: valida a entrada (só 8 dígitos), conversa com um serviço externo (a ViaCEP), decide o código de resposta (400 para formato errado, 404 para CEP inexistente, 200 para sucesso) e grava no banco. Se amanhã você precisar de uma chave de API paga, ela fica aqui, no servidor, onde o usuário não consegue lê-la.
Banco de dados: a memória do sistema
O SQLite guarda tudo em um único arquivo, consultas.db, sem precisar instalar um servidor de banco. A tabela tem quatro colunas, e o banco preenche sozinho id (número sequencial) e criado_em (data e hora em UTC, o padrão do CURRENT_TIMESTAMP no SQLite).
Repare nos pontos de interrogação em VALUES (?, ?). Essa é a consulta parametrizada: o valor do usuário é enviado separado do comando SQL, e por isso nunca pode ser executado como código. Montar a consulta juntando textos, como "... VALUES ('" + cep + "')", abre a porta para a injeção de SQL, falha para a qual o OWASP mantém um guia de prevenção específico.
Depois de uma consulta, o endpoint /api/historico devolveu, no teste:
[{"cep": "01001000", "cidade": "São Paulo", "quando": "2026-10-17 02:17:54"}]
O caminho de um clique, do botão ao disco
- O usuário digita
01001000e clica em Buscar. - O JavaScript envia
GET /api/cep/01001000ao servidor. - O Flask recebe, confere que são 8 dígitos e chama a ViaCEP pela internet.
- A ViaCEP devolve o endereço da Praça da Sé em JSON.
- O back-end grava
01001000eSão Paulona tabelaconsultas. - O back-end responde ao navegador com logradouro, cidade e UF.
- O JavaScript escreve "Praça da Sé, São Paulo/SP" na página.
Em produção, entram mais peças nesse caminho: DNS, HTTPS, um proxy reverso como o Nginx na frente do Flask, cache e às vezes uma CDN para os arquivos estáticos. O trajeto entre o navegador e o servidor é detalhado em como funciona a internet.
O que dá errado em cada camada
| Sintoma | Camada provável | Causa comum |
|---|---|---|
| Botão não faz nada; erro vermelho no console (F12) | Front-end | Erro de JavaScript ou endereço errado no fetch |
| Erro de CORS no console | Front-end chamando outro domínio | O servidor de destino não autorizou aquela origem |
| Status 500 na aba Rede | Back-end | Exceção no código Python, visível no terminal do Flask |
| Resposta lenta | Back-end ou serviço externo | API de terceiros demorando; por isso o timeout=10 |
database is locked | Banco de dados | Muitas gravações simultâneas no SQLite |
| Dados somem ao reiniciar | Infraestrutura | Arquivo do banco em pasta temporária ou contêiner sem volume |
O SQLite é ótimo para aprender, protótipos e aplicativos com pouco acesso simultâneo; segundo a própria documentação, ele aceita apenas uma gravação por vez em cada arquivo de banco. Quando muitos usuários gravam ao mesmo tempo, o caminho é um banco cliente-servidor como PostgreSQL ou MySQL, e a mudança no código é pequena se as consultas forem parametrizadas.
As tecnologias mais usadas em 2026
A pesquisa de desenvolvedores do Stack Overflow de 2026, com 30.903 participantes, mostra os bancos mais usados: PostgreSQL (57,9%), SQLite (42,1%) e MySQL (35,1%). Entre as linguagens, JavaScript lidera com 62,0%, seguido por SQL (58,4%), HTML/CSS (58,2%) e Python (58,0%).
| Camada | Opções comuns | Versão de referência (outubro de 2026) |
|---|---|---|
| Front-end | HTML, CSS, JavaScript, TypeScript; React, Vue, Angular, Svelte | React 19.3.1 (npm) |
| Back-end | Node.js, Python (Flask, Django, FastAPI), Java, C#, PHP, Go | Node.js 24 LTS; Python 3.15.0; Flask 3.1.3 |
| Banco relacional | PostgreSQL, MySQL, SQLite, SQL Server | PostgreSQL 18.6 (o 19 está em RC desde 15/10/2026) |
| Não relacional | MongoDB, Redis, Cassandra | Varia por projeto |
Para escolher a primeira linguagem do back-end, o comparativo Python ou JavaScript ajuda. Quando o projeto sair do seu computador, ele vai precisar de um servidor; as opções estão em VPS, hospedagem compartilhada ou nuvem.
Como evoluir este exemplo
Algumas tarefas que transformam o buscador em um projeto de portfólio, cada uma tocando uma camada:
- Front-end: mostrar o histórico na página chamando
/api/historicoe estilizar com CSS. - Back-end: guardar em cache os CEPs já consultados para não chamar a ViaCEP de novo, respeitando o aviso do serviço contra uso massivo.
- Banco: criar um índice na coluna
cepe trocar o SQLite por PostgreSQL. - Dados pessoais: se um dia guardar nome ou e-mail junto com o CEP, as regras da LGPD passam a valer.
Versione cada etapa com Git; o tutorial Git e GitHub na prática mostra como publicar o projeto.
Fontes
- Pallets. Documentação do Flask: Quickstart e Flask no PyPI.
- SQLite. Usos apropriados para o SQLite.
- Python Software Foundation. Módulo sqlite3: consultas com placeholders.
- OWASP. SQL Injection Prevention Cheat Sheet.
- Mozilla. Element.textContent e CORS.
- ViaCEP. Documentação.
- PostgreSQL. Política de versões.
- Stack Overflow. Developer Survey 2026: Technology.




