Toda aplicação Node.js que precisa de um banco local — cache, fila de jobs, dados de teste, protótipo rápido — acabava instalando better-sqlite3 ou sqlite3 como dependência nativa. Desde o Node.js 22.5.0, o runtime traz seu próprio módulo node:sqlite: um binding síncrono ao SQLite embutido no binário do Node, sem npm install e sem compilação nativa.
Criando e consultando um banco
A classe principal é DatabaseSync. O construtor aceita um caminho de arquivo ou a string especial :memory: para um banco em memória, que desaparece quando o processo termina:
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(':memory:');
db.exec(`
CREATE TABLE produtos(
id INTEGER PRIMARY KEY,
nome TEXT,
preco REAL
) STRICT
`);
db.exec("INSERT INTO produtos (nome, preco) VALUES ('Teclado', 150.0)");
db.exec("INSERT INTO produtos (nome, preco) VALUES ('Mouse', 50.0)");
const linhas = db.prepare('SELECT * FROM produtos ORDER BY preco').all();
console.log(linhas);
// [ { id: 2, nome: 'Mouse', preco: 50 }, { id: 1, nome: 'Teclado', preco: 150 } ]
exec() roda SQL sem parâmetros e sem retorno — ideal para criar tabelas. Para consultas reais, o caminho é prepare(), que compila o SQL numa StatementSync reutilizável.
Statements preparados e parâmetros
Uma StatementSync aceita parâmetros posicionais (?) ou nomeados (:nome, $nome, @nome) e expõe três métodos principais: run() para escritas, get() para a primeira linha e all() para todas as linhas:
const inserir = db.prepare('INSERT INTO produtos (nome, preco) VALUES (?, ?)');
const resultado = inserir.run('Monitor', 899.9);
console.log(resultado);
// { changes: 1, lastInsertRowid: 3 }
const buscarPorNome = db.prepare('SELECT * FROM produtos WHERE nome = :nome');
console.log(buscarPorNome.get({ nome: 'Monitor' }));
// { id: 3, nome: 'Monitor', preco: 899.9 }
Como todas as chamadas são síncronas, não existe await nem callback — o que simplifica scripts e testes, mas significa que consultas pesadas bloqueiam o event loop, exatamente como fs.readFileSync().
Iterando resultados grandes
Para não carregar uma tabela inteira na memória de uma vez, iterate() devolve um iterador que produz uma linha por vez e funciona direto num for...of:
const todos = db.prepare('SELECT * FROM produtos ORDER BY id');
for (const produto of todos.iterate()) {
console.log(`${produto.nome}: R$ ${produto.preco}`);
}
Banco em arquivo e opções de abertura
Trocar :memory: por um caminho de arquivo persiste os dados em disco. O construtor aceita um segundo argumento com opções, como abrir em modo somente leitura:
const somenteLeitura = new DatabaseSync('./dados/produtos.db', {
readOnly: true,
});
const relatorio = somenteLeitura.prepare('SELECT COUNT(*) AS total FROM produtos').get();
console.log(relatorio.total);
somenteLeitura.close();
close() fecha a conexão e libera o arquivo; statements abertos também têm seu próprio close() para liberar recursos antes de fechar o banco.
Casos de uso
- Cache local, fila de jobs ou configuração persistida em CLIs e scripts, sem dependência nativa para compilar.
- Banco de dados de teste isolado por suíte, criado em memória e descartado ao final.
- Protótipos e ferramentas internas que precisam de SQL real sem subir um servidor de banco.
- Ler ou inspecionar arquivos
.sqlite/.dbjá existentes, gerados por outra aplicação.
Cuidados
Por ser síncrono, node:sqlite não é a escolha certa para servidores web com alto volume de escrita concorrente — bibliotecas como better-sqlite3 existem há mais tempo, têm mais recursos (backup incremental, extensões) e também são síncronas, mas por escolha de design, não por imaturidade da API nativa. A classe DatabaseSync ainda é marcada como experimental mesmo depois de não exigir mais flag, então sua API pode mudar entre versões menores do Node — vale checar o changelog antes de atualizar em produção.
Compatibilidade
O módulo node:sqlite chegou no Node.js 22.5.0, atrás da flag --experimental-sqlite. A flag deixou de ser necessária no Node.js 23.4.0 e no 22.13.0 (LTS), embora o módulo continue classificado como experimental nessas versões. Use node --version para confirmar: em qualquer Node.js 22.13.0 ou mais recente, require('node:sqlite') já funciona sem flags.
Conclusão
Com node:sqlite, um banco relacional de verdade passou a fazer parte do que o Node.js oferece de fábrica, do mesmo jeito que aconteceu com o test runner e o carregamento de .env. Para scripts, CLIs, testes e protótipos, isso elimina mais uma dependência nativa da lista — e para cargas mais pesadas, continua valendo a pena olhar para bibliotecas estabelecidas como better-sqlite3.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.