Featured image of post ¡No se deje engañar por la informalidad de SQLite! ¿Cuáles son las trampas de los tipos dinámicos? ¿Por qué ALTER TABLE está a medias? ¿Cómo construir una arquitectura de programación defensiva en Node.js para actualizaciones de esquema sin dolor?

¡No se deje engañar por la informalidad de SQLite! ¿Cuáles son las trampas de los tipos dinámicos? ¿Por qué ALTER TABLE está a medias? ¿Cómo construir una arquitectura de programación defensiva en Node.js para actualizaciones de esquema sin dolor?

SQLite adopta un sistema de tipos dinámico y débil; meter una cadena en una columna INTEGER sorprendentemente no arrojará un error. Comprenda las trampas de Type Affinity en SQLite, el impacto de la falta de tipos Boolean y Date nativos, las limitaciones de ALTER TABLE junto con la estrategia de actualización segura de 'cuatro pasos para recrear y mover', y cómo construir arquitecturas de programación defensiva con herramientas como TypeScript, Zod y Prisma.

¿Se imagina un contenedor de reciclaje en el que ha colocado claramente una etiqueta que dice "Solo botellas de plástico", pero cuando alguien tira un trozo de papel dentro, lo acepta silenciosamente sin una sola palabra de protesta?

Esta es la experiencia escalofriante que viven los desarrolladores cuando conocen el sistema de tipos de SQLite por primera vez.

Si está acostumbrado al estricto estilo de agente de aduanas de PostgreSQL (donde los tipos incorrectos se rechazan directamente), la informalidad de SQLite podría hacerle cuestionar su vida.

Aún más aterrador, cuando quiere modificar la estructura de una tabla, le dirá:

"La tabla no se puede modificar directamente. Por favor, construya una casa nueva, mude los muebles y luego vuele la casa vieja."

Bajo el capó, SQLite solo tiene 5 clases de almacenamiento

No importa qué nombres de tipo elegantes declare en CREATE TABLE (VARCHAR(255), BIGINT, DECIMAL), SQLite bajo el capó solo reconoce estas 5 clases de almacenamiento:

Clase de almacenamiento Descripción
NULL Valor nulo
INTEGER Entero (ocupa automáticamente de 1 a 8 bytes según la magnitud)
REAL Número de coma flotante (fijo de 8 bytes)
TEXT Cadena de texto (codificación UTF-8 predeterminada)
BLOB Objeto binario grande (almacenado exactamente como se ingresó)

Los tipos que establece en las columnas son simplemente "sugerencias" para SQLite, no "reglas obligatorias".

Esto se llama "Type Affinity" (Afinidad de tipos). SQLite intentará convertir sus datos al tipo sugerido, pero si no puede, simplemente meterá los datos originales de todos modos sin lanzar ningún error.

Puede declarar una columna age INTEGER en SQLite y luego insertar la cadena 'siempre dieciocho'; lo aceptará con gusto.

Tres trampas de tipos más fáciles en las que caer

Trampa 1: Sin Boolean nativo

SQLite not tiene tipo booleano. True y False solo pueden representarse mediante los enteros 1 y 0.

Cuando recupera datos de SQLite usando Node.js, obtendrá el número 1 o 0, no true o false.

Si realiza directamente una verificación como if (user.is_admin === true), nunca será verdadera.

Trampa 2: Sin tipo Date/Time

SQLite no tiene tipo de fecha/hora. Solo puede almacenar el tiempo como:

Método de almacenamiento Ejemplo Pros y contras
TEXT (cadena ISO-8601) '2026-05-19T18:00:00Z' Más recomendado, alta legibilidad, conversión perfecta al pasar a PostgreSQL en el futuro
INTEGER (Unix Timestamp) 1747656000 Huella pequeña, pero no legible por humanos

Nunca almacene fechas en formatos personalizados arbitrarios como 19/5/2026 6:00 PM, de lo contrario, la migración de datos en el futuro será un desastre.

Trampa 3: Insertar una cadena en una columna Integer no arrojará un error

En PostgreSQL, insertar una cadena en una columna INTEGER arroja un error de inmediato. But SQLite solo intentará convertirla silenciosamente, y si falla, la aceptará tal como está.

Esto significa que datos sucios podrían colarse silenciosamente en su base de datos hasta que un día su programa falle debido a la recepción de un tipo inesperado, solo entonces descubrirá el problema.

Programación defensiva: Tratar una base de datos informal con una actitud estricta

Frente a la informalidad de SQLite, debe construir mecanismos de defensa estrictos en el desarrollo con Node.js:

Nivel de defensa Herramienta Rol
Guardia en tiempo de compilación TypeScript Capturar tipos incorrectos en la etapa de escritura del código
Validación de entrada de API Zod Validar los datos entrantes estrictamente (asegurar que age sea siempre un número)
Conversión de tipos implícita Prisma / Drizzle ORM Manejar automáticamente las diferencias de tipos entre SQLite y PostgreSQL

Mover al "guardián de la validación de datos" desde la capa de la base de datos a la capa de la aplicación Node.js es una estrategia clave para aprovechar la velocidad de desarrollo de SQLite y al mismo tiempo garantizar la escalabilidad futura.

Al usar un ORM, siempre que declare type: 'boolean' en su código, el ORM lo convierte automáticamente a 1/0 al guardar en SQLite, y lo vuelve a convertir a true/false al leer, ocultando perfectamente las diferencias de tipos subyacentes.

ALTER TABLE está a medias: Qué se puede y qué no se puede modificar

El soporte de SQLite para modificar estructuras de tablas es muy limitado:

Operasi Didukung
Agregar columna (ADD COLUMN)
Renombrar columna (RENAME COLUMN)
Eliminar columna (DROP COLUMN) (en versiones más nuevas)
Renombrar tabla (RENAME TO)
Cambiar tipo de columna No
Agregar/eliminar restricciones UNIQUE, NOT NULL No
Modificar clave primaria No
Modificar clave foránea No

Una vez que necesite realizar cualquiera de las modificaciones "no soportadas", SQLite requiere que ejecute la estrategia de "recrear y mover".

Los cuatro pasos para recrear y mover: La forma de actualizar tablas de SQLite

Dado que no se puede modificar directamente, la práctica estándar recomendada por los documentos oficiales es construir una casa nueva, mudar los muebles, volar la casa vieja y colocar la placa nueva:

Paso Acción Descripción
1 Crear tabla nueva CREATE TABLE users_new (...) usando la estructura correcta
2 Copiar datos INSERT INTO users_new SELECT ... FROM users
3 Eliminar tabla vieja DROP TABLE users
4 Renombrar tabla ALTER TABLE users_new RENAME TO users

Estos cuatro pasos deben ejecutarse de un solo tirón; cualquier corte de energía o caída de la aplicación a mitad del camino resultará en la pérdida de datos.

Garantizar que las actualizaciones no pierdan datos: Dos líneas de defensa de seguridad

Línea de defensa 1: Defensa física, copiar el archivo directamente

SQLite es esencialmente solo un archivo. Antes de ejecutar cualquier cambio de esquema, simplemente haga una copia del archivo .db como respaldo.

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

Si las cosas salen mal, reemplazar el archivo restaura todo en un instante. Esta es una ventaja que otras bases de datos grandes no pueden ofrecer.

Línea de defensa 2: Contenedor de Transaction, la máquina del tiempo de la base de datos

Envuelva todos los pasos de migración dentro de una sola Transaction; si algún paso falla, todo el proceso se revertirá automáticamente (Rollback) como si nunca hubiera pasado nada.

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('Tabla actualizada con éxito');
} catch (error) {
  console.error('La actualización falló, datos restaurados de forma segura:', error.message);
}

"Archivo de respaldo + Vinculación de Transaction" es el airbag de la migración de su base de datos.

Controle la informalidad de SQLite con una actitud estricta

Domine la informalidad de SQLite con una arquitectura estricta en la capa de aplicación para disfrutar de su velocidad de desarrollo ultrarrápida y evitar deudas técnicas futuras.

El sistema de tipos de SQLite es muy informal, y ALTER TABLE tiene muchas limitaciones.

Sin embargo, siempre que realice comprobación de tipos con TypeScript + validación con Zod + abstracción con ORM en el lado de Node.js, combinado con la estrategia de seguridad de respaldo físico + Transaction, puede disfrutar con seguridad de la eficiencia de desarrollo que ofrece SQLite mientras pavimenta el camino para migrar a PostgreSQL en el futuro.

Reference

All rights reserved,未經允許不得隨意轉載
Creado con Hugo
Tema Stack diseñado por Jimmy