Featured image of post Não se deixe enganar pela informalidade do SQLite! Quais são as armadilhas dos tipos dinâmicos? Por que o ALTER TABLE é capenga? Como construir uma arquitetura de programação defensiva no Node.js para atualizações de esquema sem dor?

Não se deixe enganar pela informalidade do SQLite! Quais são as armadilhas dos tipos dinâmicos? Por que o ALTER TABLE é capenga? Como construir uma arquitetura de programação defensiva no Node.js para atualizações de esquema sem dor?

O SQLite adota um sistema de tipos dinâmico e fraco; colocar uma string em uma coluna INTEGER surpreendentemente não gerará erro. Comprenda as armadilhas de Type Affinity no SQLite, o impacto da falta de tipos Boolean e Date nativos, as limitações do ALTER TABLE junto com a estratégia de atualização segura de 'quatro passos para recriar e mover', e como construir arquiteturas de programação defensiva com ferramentas como TypeScript, Zod e Prisma.

Imagine uma lixeira de reciclagem onde você claramente colocou uma etiqueta dizendo "Apenas Garrafas Plásticas", mas quando alguém joga um pedaço de papel lá dentro, ela o aceita silenciosamente sem uma única palavra de protesto?

Esta é a experiência de arrepiar a espinha que os desenvolvedores vivem quando conhecem o sistema de tipos do SQLite pela primeira vez.

Se você está acostumado ao estilo rigoroso de agente de alfândega do PostgreSQL (onde os tipos incorretos são rejeitados diretamente), a informalidade do SQLite pode fazer você questionar sua vida.

Ainda mais assustador, quando você quer modificar a estrutura de uma tabela, ele lhe dirá:

"A tabela não pode ser modificada diretamente. Por favor, construa uma casa nova, mude os móveis e depois exploda a casa velha."

Sob o capô, o SQLite tem apenas 5 classes de armazenamento

Não importa quais nomes de tipo elegantes você declare em CREATE TABLE (VARCHAR(255), BIGINT, DECIMAL), o SQLite sob o capô reconhece apenas estas 5 classes de armazenamento:

Classe de armazenamento Descrição
NULL Valor nulo
INTEGER Inteiro (ocupa automaticamente de 1 a 8 bytes de acordo com a magnitude)
REAL Número de ponto flutuante (fixo de 8 bytes)
TEXT String de texto (codificação UTF-8 padrão)
BLOB Objeto binário grande (armazenado exatamente como inserido)

Os tipos que você define nas colunas são meramente "sugestões" para o SQLite, não "regras obrigatórias".

Isso se chama "Type Affinity" (Afinidade de tipos). O SQLite tentará converter seus dados para o tipo sugerido, mas se não puder, simplesmente colocará os dados originais de qualquer maneira sem lançar nenhum erro.

Você pode declarar uma coluna age INTEGER no SQLite e depois inserir a string 'para sempre dezoito'; ele o aceitará com prazer.

Três armadilhas de tipos mais fáceis de cair

Armadilha 1: Sem Boolean nativo

O SQLite não tem tipo booleano. True e False só podem ser representados pelos inteiros 1 e 0.

Quando você recupera dados do SQLite usando Node.js, obterá o número 1 ou 0, não true ou false.

Se você realizar diretamente uma verificação como if (user.is_admin === true), isso nunca será verdadeiro.

Armadilha 2: Sem tipo Date/Time

O SQLite não tem tipo de data/hora. Você só pode armazenar o tempo como:

Método de armazenamento Exemplo Prós e contras
TEXT (string ISO-8601) '2026-05-19T18:00:00Z' Mais recomendado, alta legibilidade, conversão perfeita ao passar para PostgreSQL no futuro
INTEGER (Unix Timestamp) 1747656000 Pegada pequena, mas não legível por humanos

Nunca armazene datas em formatos personalizados arbitrários como 19/05/2026 18:00, caso contrário, a migração de dados no futuro será un desastre.

Armadilha 3: Inserir uma string em uma coluna Integer não gerará erro

No PostgreSQL, inserir uma string em uma coluna INTEGER gera um erro imediatamente. Mas o SQLite apenas tentará convertê-la silenciosamente e, se falhar, a aceitará tal como está.

Isso significa que dados sujos podem entrar silenciosamente no seu banco de dados até que um dia seu programa falhe devido ao recebimento de um tipo inesperado, só então você descobrirá o problema.

Programação defensiva: Tratar um banco de dados informal com uma atitude rigorosa

Diante da informalidade do SQLite, você deve construir mecanismos de defesa rígidos no desenvolvimento com Node.js:

Nível de defesa Ferramenta Papel
Guarda em tempo de compilação TypeScript Capturar tipos incorretos no estágio de escrita do código
Validação de entrada de API Zod Validar os dados de entrada estritamente (garantir que age seja sempre um número)
Conversão implícita de tipos Prisma / Drizzle ORM Lidar automaticamente com as diferenças de tipos entre SQLite e PostgreSQL

Mover o "guardião da validação de dados" da camada do banco de dados para a camada da aplicação Node.js é uma estratégia-chave para aproveitar a velocidade de desenvolvimento do SQLite e, ao mesmo tempo, garantir a escalabilidade futura.

Ao usar um ORM, desde que você declare type: 'boolean' no seu código, o ORM o converte automaticamente para 1/0 ao salvar no SQLite, e o converte de volta para true/false ao ler, ocultando perfeitamente as diferenças de tipos subjacentes.

ALTER TABLE é capenga: O que pode e o que não pode ser modificado

O suporte do SQLite para modificar estruturas de tabelas é muito limitado:

Operação Suportado
Adicionar coluna (ADD COLUMN) Sim
Renomear coluna (RENAME COLUMN) Sim
Excluir coluna (DROP COLUMN) Sim (em versões mais novas)
Renomear tabela (RENAME TO) Sim
Alterar tipo de coluna Não
Adicionar/remover restrições UNIQUE, NOT NULL Não
Modificar chave primária Não
Modificar chave estrangeira Não

Uma vez que precise realizar qualquer uma das modificações "não suportadas", o SQLite exige que você execute a estratégia de "recriar e mover".

Os quatro passos para recriar e mover: O caminho de atualização de tabelas do SQLite

Como não pode ser modificado diretamente, a prática padrão recomendada pelos documentos oficiais é construir uma casa nova, mudar os móveis, explodir a casa velha e colocar a placa nova:

Passo Ação Descrição
1 Criar tabela nova CREATE TABLE users_new (...) usando a estrutura correta
2 Copiar dados INSERT INTO users_new SELECT ... FROM users
3 Excluir tabela velha DROP TABLE users
4 Renombrar tabela ALTER TABLE users_new RENAME TO users

Esses quatro passos devem ser executados de uma só vez; qualquer queda de energia ou crash da aplicação no meio do caminho resultará na perda de dados.

Garantir que as atualizações não percam dados: Duas linhas de defesa de segurança

Linha de defesa 1: Defesa física, copiar o arquivo diretamente

O SQLite é essencialmente apenas um arquivo. Antes de executar qualquer alteração de esquema, simplesmente faça uma cópia do arquivo .db como backup.

const fs = require('fs');
fs.copyFileSync('my_project.db', 'my_project_backup.db');

Se as coisas derem errado, substituir o arquivo restaura tudo em un instante. Esta é uma vantagem que outros bancos de dados grandes não podem oferecer.

Linha de defesa 2: Pacote de Transaction, a máquina do tempo do banco de dados

Envolva todos os passos de migração dentro de uma única Transaction; se algum passo falhar, todo o processo será revertido automaticamente (Rollback) como se nada tivesse acontecido.

const Database = require('better-sqlite3');
const db = new Database('my_project.db');

const migrateData = db.transaction(() => {
  db.prepare(`
    CREATE TABLE users_new (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      age INTEGER NOT NULL DEFAULT 18
    )
  `).run();

  db.prepare(`
    INSERT INTO users_new (id, name, age)
    SELECT id, name, COALESCE(age, 18) FROM users
  `).run();

  db.prepare('DROP TABLE users').run();
  db.prepare('ALTER TABLE users_new RENAME TO users').run();
});

try {
  migrateData();
  console.log('Tabela atualizada com sucesso');
} catch (error) {
  console.error('A atualização falhou, dados restaurados com segurança:', error.message);
}

"Arquivo de backup + Vinculação de Transaction" é o airbag da migração do seu banco de dados.

Controle a informalidade do SQLite com uma atitude rigorosa

Domine a informalidade do SQLite com uma arquitetura rígida na camada de aplicação para desfrutar da sua velocidade de desenvolvimento ultrarrápida e evitar dívidas técnicas futuras.

O sistema de tipos do SQLite é muito informal, e o ALTER TABLE tem muitas limitações.

No entanto, desde que você realize verificação de tipos com TypeScript + validação com Zod + abstração com ORM no lado do Node.js, combinado com a estratégia de segurança de backup físico + Transaction, você dapat desfrutar com segurança da eficiência de desenvolvimento oferecida pelo SQLite enquanto pavimenta o caminho para migrar para o PostgreSQL no futuro.

Reference

All rights reserved,未經允許不得隨意轉載
Criado com Hugo
Tema Stack desenvolvido por Jimmy