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

CamadaArquivoRoda ondeFaz o quê
Front-endstatic/index.htmlNo navegador do usuárioMostra o campo, envia o CEP, exibe a resposta
Back-endapp.pyNo servidorValida o CEP, consulta a ViaCEP, grava e lê o histórico
Banco de dadosconsultas.db (SQLite)No disco do servidorGuarda 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

  1. O usuário digita 01001000 e clica em Buscar.
  2. O JavaScript envia GET /api/cep/01001000 ao servidor.
  3. O Flask recebe, confere que são 8 dígitos e chama a ViaCEP pela internet.
  4. A ViaCEP devolve o endereço da Praça da Sé em JSON.
  5. O back-end grava 01001000 e São Paulo na tabela consultas.
  6. O back-end responde ao navegador com logradouro, cidade e UF.
  7. 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

SintomaCamada provávelCausa comum
Botão não faz nada; erro vermelho no console (F12)Front-endErro de JavaScript ou endereço errado no fetch
Erro de CORS no consoleFront-end chamando outro domínioO servidor de destino não autorizou aquela origem
Status 500 na aba RedeBack-endExceção no código Python, visível no terminal do Flask
Resposta lentaBack-end ou serviço externoAPI de terceiros demorando; por isso o timeout=10
database is lockedBanco de dadosMuitas gravações simultâneas no SQLite
Dados somem ao reiniciarInfraestruturaArquivo 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%).

CamadaOpções comunsVersão de referência (outubro de 2026)
Front-endHTML, CSS, JavaScript, TypeScript; React, Vue, Angular, SvelteReact 19.3.1 (npm)
Back-endNode.js, Python (Flask, Django, FastAPI), Java, C#, PHP, GoNode.js 24 LTS; Python 3.15.0; Flask 3.1.3
Banco relacionalPostgreSQL, MySQL, SQLite, SQL ServerPostgreSQL 18.6 (o 19 está em RC desde 15/10/2026)
Não relacionalMongoDB, Redis, CassandraVaria 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/historico e 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 cep e 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