https://a.storyblok.com/f/270183/1368x665/9c72c5f61f/25jun_dev-blog_sqlite.jpg

O retorno do SQLite

Publicado em June 12, 2025

Tempo de leitura: 13 minutos

Introdução

Tudo bem, vou admitir: não é um retorno. Vamos dizer que é mais uma releitura. O SQLite existe desde o ano 2000, e seu uso original era para rastreamento de mísseis a bordo de contratorpedeiros da Marinha dos Estados Unidos. Ele certamente provou seu valor como sistema de banco de dados relacional, embora nos últimos 10 anos tenhamos visto a ascensão do NoSQL e dos sistemas de banco de dados orientados a documentos, o que talvez tenha tirado um pouco dos holofotes dele. Neste artigo, explorarei o potencial subestimado do SQLite e demonstrarei o quão rápido é colocá-lo em funcionamento com uma arquitetura pronta para produção em mente. OK, vou admitir: não é um retorno. Vamos chamá-lo mais de uma reimaginação. O SQLite existe desde 2000. Embora a última década tenha testemunhado a ascensão do NoSQL e dos bancos de dados orientados a documentos, que roubaram grande parte dos holofotes, o SQLite continua sendo uma opção poderosa. Originalmente desenvolvido para rastreamento de mísseis em contratorpedeiros da Marinha dos Estados Unidos, ele discretamente provou ser um banco de dados relacional confiável. É provável que você esteja subestimando seu potencial em arquiteturas de nível de produção. Neste artigo, vou mostrar como é rápido e fácil colocar o SQLite em funcionamento.

>> Resumo: Você pode encontrar o código-fonte completo no GitHub.

É possível usar o SQLite em ambiente de produção?

Foi uma introdução e tanto, mas como os engenheiros de software — especialmente os desenvolvedores de aplicativos web — conhecem o SQLite? Bem, ele tem basicamente duas reputações:

  • Preciso executar meus testes e não quero iniciar um servidor , então por que não usar esse arquivinho útil para rodá-lo?

  • É para sistemas embarcados porque é pequeno, certo? Mas isso não adianta nada para o meu aplicativo web, que vai gerar montanhas de dados.

Não há como contestar o primeiro ponto; o SQLite é incrivelmente útil para testes. Você tem, digamos, sua arquitetura existente no código: migrações, seeders e similares. Assim, ao executar seu conjunto de testes, em vez de usar um servidor de banco de dados compatível com SQL, como MySQL, MariaDB, ou Postgres, você executa todo o mesmo código, mas com um arquivo SQLite vazio. Ótimo. Mas e quanto ao ponto 2? 

Vantagem do SQLite: Portabilidade

Dark brown vintage leather briefcase with metal buckles, resting on a round wooden stool against a white backgroundJust one file: portability at its finestSim, é pequeno. E o melhor: a conectividade é simples: é só um arquivo.

Há uma grande variedade de plug-ins e recursos de compatibilidade para IDEs — os IDEs da JetBrains possuem um painel de banco de dados capaz de criar um novo arquivo SQLite, e o VSCode tem vários plug-ins que também cuidam disso para você. Quero dizer, para os verdadeiros desenvolvedores , é possível criá-lo diretamente no Vim.

O tamanho do programa, assim que instalado, é de apenas 699 kb. É só isso. Não há nenhum serviço para ser executado, nem processos em segundo plano — apenas um arquivo.

Isso é muito diferente dos recursos necessários para colocar em funcionamento um servidor MySQL completo.

Agora, chegamos à pergunta que a maioria das pessoas quer fazer: se é tão simples assim, por que não usamos o SQLite em mais applications da web?

A resposta geralmente se resume a suposições. Os bancos de dados relacionais baseados em servidor predominam, mas, com um pouco de reflexão, você pode perceber que o SQLite é a opção perfeita para nossas necessidades reais.

Vantagem do SQLite: Desempenho

A velocidade de leitura e gravação é uma das maiores diferenças. O SQLite, para um banco de dados de tamanho médio com alguns milhões de linhas, é incrivelmente rápido. Os testes de desempenho variam, mas consultas isoladas levam até 5 ms, em comparação com uma média máxima de cerca de 10 ms para o MySQL.

No entanto, se o SQLite tivesse se destacado em todos os testes de desempenho, obviamente estaríamos usando-o para tudo, o que não é o caso, já que ele não é tão comum no desenvolvimento de aplicativos web. Em primeiro lugar, a velocidade de gravação é muito mais lenta. O SQLite consegue gravar aproximadamente até 300 inserções em um único thread, em comparação com até 50 mil no MySQL (dependendo do ambiente). Essa é uma grande diferença. 

A segunda coisa a se observar é que o SQLite possui um mecanismo de bloqueio de thread único; os registros que estão sendo lidos ou gravados/atualizados apresentam cinco estados possíveis de gravação. Ao gravar, o estado de bloqueio de um registro é alterado para EXCLUSIVE, o que restringe quaisquer outras ações simultâneas. O MySQL, no entanto, utiliza o MVCC (Controle de Concorrência Multiversão), que permite várias leituras e gravações simultâneas.

Casos de uso

Uma das perguntas que procuro sempre lembrar às pessoas é Qual é o seu caso de uso? No mundo das tecnologias nativas da nuvem e do Kubernetes, muitos argumentam que “o PHP é lento” porque “o Node tem um loop de eventos”, mas o que eu peço que as pessoas reflitam é: “o que sua aplicação vai fazer, de fato fazer? Se você vai armazenar alguns dados de usuários ou fazer upload de alguns arquivos CSV e imagens, você está realmente vai perceber a diferença entre a escolha da linguagem e do framework? Em muitos casos, provavelmente não. O mesmo vale para a comparação entre SQLite e MySQL: o SQLite ainda é uma escolha fantástica se você tiver algum dados, mas, mais importante ainda, você vai ter 20.000 usuários gravando no banco de dados do seu aplicativo? Se você estiver desenvolvendo a próxima versão de um aplicativo global de táxi, então com certeza. O que estou dizendo aqui é que o SQLite é muito mais adequado para muitos casos de desenvolvimento do que muitos imaginam.

Configurando seu projeto

Pré-requisitos

Chega de conversa, é hora de mostrar quantas poucas linhas de código podemos escrever para demonstrar uma gravação no SQLite em ambiente de produção com Typescript. Vamos apresentar um aplicativo capaz de ler webhooks gerados pela Messages API da Vonage. Primeiro, crie um novo diretório para o seu projeto, acesse-o e crie um novo projeto Node.

npm init --yes

Isso utilizará os valores padrão para o seu package.json que for criado. Em seguida, precisamos instalar o TypeScript:

npm install typescript
npx tsc --init

O Typescript é compilado em JavaScript puro e utiliza um arquivo de configuração tsconfig.json, que é criado durante a instalação. Abra esse arquivo de configuração e substitua o código padrão pelo seguinte:

{
 "compilerOptions": {
   "target": "es2016",
   "module": "CommonJS",
   "rootDir": "./",
   "outDir": "./dist",
   "esModuleInterop": true,
   "forceConsistentCasingInFileNames": true,
   "strict": true,
   "skipLibCheck": true,
   "moduleResolution": "node",
   "baseUrl": ".",
   "paths": {
     "@prisma/client":["node_modules/@prisma/client"]
   }
 },
 "include": ["src/**/*.ts"]
}

Os pontos importantes a serem observados nesta configuração são:

  • O TypeScript será compilado em um diretório que será criado caso ainda não exista, chamado dist. Esse será o ponto de entrada do seu aplicativo.

  • Você vai perceber que o Prisma está na paths chave. Isso ocorre porque, quando instalamos o Prisma, ele também precisa ser compilado (de certa forma). Quando definimos modelos no Prisma, ele usa um comando de geração para criar um cliente Prisma personalizado.

Temos seis dependências:

  • Prisma: nosso mapeador objeto-relacional

  • ts-node: permite executar o TypeScript sem compilar no diretório de saída

  • nodemon: no nosso servidor de desenvolvimento, o código é recarregado sempre que há alterações

  • dotenv: para lidar com variáveis de ambiente)

  • Express: nosso roteador

  • SQLite: para nossa conexão com o banco de dados

Você pode instalar tudo isso de uma só vez na linha de comando:

npm install prisma ts-node nodemon dotenv express @types/express sqlite3

O Prisma é um ORM capaz de gerenciar seus modelos de dados, migrações e todas as outras consultas. Para configurar seu diretório do Prisma, precisamos executar o comando `init`:

npx prisma init --datasource-provider sqlite --output ../generated/prisma

Este comando configurará o Prisma para gerar uma classe Client compilada na pasta ../generated/prisma e informa que o SQLite é nossa fonte de banco de dados. Ao executar esse comando, o Prisma solicitará quaisquer dependências adicionais necessárias e, em seguida, configurará seu arquivo .env . Configuraremos esse arquivo mais tarde.

Configurando um banco de dados no WebStorm

Como o SQLite é um arquivo gerenciado por uma biblioteca, você pode criá-lo com a maioria dos IDEs modernos que oferecem suporte ao driver. Agora abra seu projeto no WebStorm. Em seguida, clique no ícone “Banco de dados” no painel à direita. Depois, clique no botão “+” para criar um novo banco de dados. Selecione “Fonte de dados” e role a tela para baixo até encontrar o SQLite. Isso abrirá a janela modal para a criação de um novo banco de dados:

Screenshot of generating a new database connection in WebstormJetbrains Supports A Massive List of DBsEscolha SQLite como tipo e aparecerá a janela pop-up de criação:

Screenshot of SQlite database creation in WebstormCreate Your DatabaseNa primeira vez que você fizer isso, caso ainda não tenha usado o WebStorm, o IDE detectará que você não tem os drivers do SQLite instalados e exibirá um aviso no lugar da opção Test Connection opção. Caso não tenha os drivers, clique no link para instalá-los.

No Name campo, altere identifier.sqlite para webhookse o nome do arquivo para webhooks.sqlite. Como o arquivo deve estar localizado na raiz do seu projeto, clique nos pontos à direita do campo de arquivo para garantir que o caminho esteja correto. Em seguida, clique em OK.

Seu cliente Prisma está configurado em prisma/schema.prisma, mas ele importa a DATABASE_URL variável de ambiente do seu .env arquivo. Abra seu enve substitua da seguinte forma:

DATABASE_URL="file:../webhooks.sqlite"

O Prisma vem configurado por padrão para gerar o cliente ORM personalizado no node_modules diretório. Não queremos que ele seja gerado no @prisma diretório; portanto, no schema.prisma arquivo, substitua as configurações do cliente da seguinte forma, abaixo da fonte de dados:

generator client {
 provider = "prisma-client-js"
 output   = "../node_modules/.prisma/client"
}

Isso faz com que o Prisma coloque o cliente gerado em um novo diretório, .prisma. Mais tarde, ao criar um registro no Express, vamos indicar ao Prisma de onde importar o cliente.

Criação do nosso modelo de banco de dados

Precisamos criar um modelo que será executado pela migração. O que fiz aqui foi um truque bem legal — o Prisma exige que você digite todos os seus modelos de banco de dados manualmente. Bem, estamos utilizando uma webhook da Messages API da Vonage; e se eu acessasse a referência do webhook na especificação OpenAPI, copiasse o JSON de exemplo e, em seguida, pedisse à IA para escrever o modelo para mim? Foi exatamente isso que fiz.

Screenshot of ChatGPT responding to my request to generate a model from the OpenAPI SpecAuto-generated AI code from OpenAPI specsVou poupar o trabalho para você: basta copiar os modelos a seguir para o seu  schema.prisma below fonte de dados e no cliente:

model WebhookEvent {
 id            String   @id @default(uuid())
 channel       String
 messageUuid   String   @unique
 to            String
 from          String
 timestamp     DateTime
 contextStatus String
 messageType   String
 location      Location?
 createdAt     DateTime @default(now())
}

model Location {
 id        String  @id @default(uuid())
 webhookId String  @unique
 lat       Float
 long      Float
 webhook   WebhookEvent @relation(fields: [webhookId], references: [id], onDelete: Cascade)
}

Agora você pode executar a migração, e ela irá gerar tabelas no banco de dados para esses modelos.

npx prisma migrate dev --name webhooks

O Prisma já criou a migração para você, que serve como um registro de todas as alterações feitas no banco de dados. Quer renomear um campo? Pois é, nova migração, enviada para o código-fonte. Agora você tem um registro completo de como deixar o banco de dados do seu aplicativo no estado correto, em código.

Criando nosso aplicativo Express para lidar com webhooks

Sua tarefa final é criar o aplicativo Express que irá utilizar o webhook. Crie um novo diretório na raiz do seu projeto chamado src e crie um novo arquivo TypeScript chamado index.ts. Ele fica assim:

import express, { Express, Request, Response } from "express";
import dotenv from 'dotenv';
import { PrismaClient } from ".prisma/client"

dotenv.config();

const app: Express = express();
const port = process.env.PORT;
const prisma = new PrismaClient();

app.use(express.json())

app.get('/', (req: Request, res: Response) => {
 res.send('Express + TypeScript Server');
});

app.post('/webhook', async (req, res) => {
 try {
   const { channel, message_uuid, to, from, timestamp, context_status, message_type, location } = req.body
   // Save the webhook data in the database
   const webhook = await prisma.webhookEvent.create({
     data: {
       channel,
       messageUuid: message_uuid,
       to,
       from,
       timestamp: new Date(timestamp),
       contextStatus: context_status,
       messageType: message_type,
       location: location ? {
         create: {
           lat: location.lat,
           long: location.long
         }
       } : undefined
     },
     include: { location: true } // Optional: Include related location in the response
   })

   console.log('Webhook saved:', webhook)
   res.status(201).json({ message: 'Webhook saved successfully', webhook })
 } catch (error) {
   console.error('Error saving webhook:', error)
   res.status(500).json({ error: 'Internal server error' })
 }
})

app.listen(port, () => {
 console.log(`[server]: Server is running at http://localhost:${port}`);
});

Há bastante coisa para analisar aqui, então vamos examinar linha por linha:

  • import { PrismaClient } from ".prisma/client" certifique-se de importar a partir do diretório .prisma.

  • app.use(express.json()) é garantir que o Express saiba que precisa processar JSON.

  • app.post('/webook') define um novo POST ponto de extremidade para o qual a Vonage enviará dados.

  • O const definido no closure do webhook extrai todos os campos necessários para variáveis imutáveis, que serão gravadas no banco de dados

  • Por fim, prisma.webhookEvent.create() é chamado, o que, por sua vez, grava os dados recebidos no seu banco de dados SQLite.

Você precisará ser capaz de recompilar seu cliente Prisma pela linha de comando, além das opções adicionadas para que o TypeScript compilado funcione. Na scripts seção do seu package.json arquivo, altere-o para o seguinte:

"scripts": {
 "test": "echo \"Error: no test specified\" && exit 1",
 "build": "npm run prisma:generate && npx tsc",
 "start": "node dist/src/index.js",
 "dev": "nodemon --exec ts-node src/index.ts",
 "prisma:generate": "prisma generate"
}

Para executar seu aplicativo aqui, primeiro compile-o no dist e, em seguida, inicie o aplicativo Express, depois de ter regenerado o Prisma e chamado o compilador do TypeScript. 

npm run buildnpm run start

Você deve ter notado que também adicionei o comando npm run dev que executará o compilador do TypeScript e nodemon, que fará uma compilação de desenvolvimento sempre que um arquivo for alterado.

A última etapa é o envio dos dados propriamente ditos. Para isso, primeiro precisaremos expor nosso aplicativo local à internet usando o ngrok. Para isso, abra uma nova aba no seu terminal e execute:

ngrok http 3000

Saiba mais sobre o ngrok.

Depois que o ngrok tiver aberto suas portas, você já deverá ter uma URL pública. Agora, ela será configurada no Vonage.

  • Para criar um aplicativo, acesse a página “Criar um aplicativo” no Painel da Vonage e defina um Nome para a sua Application.

  • Se você pretende usar uma API que utilize Webhooks, precisará de uma chave privada. Clique em “Gerar chave pública e privada”; o download deve iniciar automaticamente. Guarde-a em local seguro; essa chave não poderá ser baixada novamente em caso de perda. Ela seguirá a convenção de nomenclatura private_<seu ID de aplicativo>.key. Agora, essa chave pode ser usada para autenticar chamadas de API. Observação: sua chave não funcionará até que seu aplicativo seja salvo.

  • Escolha os recursos de que você precisa (por exemplo, Voice, Mensagens, RTC etc.) e forneça os webhooks necessários (por exemplo, URLs de eventos, URLs de resposta ou URLs de mensagens recebidas). Esses itens serão descritos no tutorial.

  • Para salvar e implantar, clique em “Gerar novo aplicativo” para finalizar a configuração. Seu aplicativo já está pronto para ser usado com as APIs da Vonage.

As chaves pública e privada e qualquer outra opção aqui não são realmente importantes, já que não estamos fazendo uma integração completa, apenas gerando alguns dados. O que é importante é que você cole a URL gerada pelo Ngrok nessas duas opções de entrada, seguida de /webhook , pois essa é a rota que foi criada no Express. Vá em frente e crie o aplicativo; a seção de mensagens deve ficar assim:

Screenshot of the Vonage Application dashboard configuration panel showing the webhook endpointsConfiguring your Vonage Application WebhooksQuaisquer respostas enviadas para o número associado a esta aplicação serão agora encaminhadas por meio de um webhook para a URL que você inseriu aqui como “URL de entrada”.

Para comprar um número de telefone virtual, acesse seu painel da API e siga as etapas mostradas abaixo.

Steps on how to purchase a phone number from the dashboard, from selecting the number and confirming the selection.Purchase a phone number

  1. Acesse seu painel da API

  2. Acesse CRIAR E GERENCIAR > Numbers > Comprar números.

  3. Selecione os atributos necessários e, em seguida, clique em “Pesquisar”

  4. Clique no botão “Comprar” ao lado do número desejado e confirme sua compra

  5. Para confirmar que você adquiriu o número virtual, acesse o menu de navegação à esquerda, na seção “CRIAR E GERENCIAR”, clique em “Numbers” e, em seguida, em “Seus números”

Nossa configuração já está concluída: envie uma mensagem para o número que você adquiriu, e toda a integração será feita para registrar o evento e a mensagem recebida diretamente em seu novo banco de dados SQLite. Aqui, utilizei uma das minhas ferramentas favoritas, HTTPie, para testar o envio do webhook:

Screenshot of HTTPie window showing a successful HTTP 201 request to create the recordSuccessful Persistence WinsNo Painel de Banco de Dados do PHPStorm, abra a tabela “webhooks” e veremos que o registro foi salvo corretamente:

Database Query View in WebStorm

Conclusão

Esta demonstração mostra o poder do SQLite: nós não fizeram muito para fazer com que esse código armazenasse os dados recebidos. Sem Docker, sem instâncias em nuvem de sistemas relacionais. Esta introdução serve como uma indicação de onde o SQLite pode ser bastante útil: volumes menores de arquivos de log em rotação, lotes menores de dados gerados por pessoas em vez de máquinas e dados de sessão. Pense nisso como uma configuração rápida semelhante ao Redis quando a concorrência e o tráfego não são uma grande preocupação. Mas, como em tudo na vida, lembre-se do lema dos engenheiros: It Depends®.

Tem alguma dúvida ou quer compartilhar o que está criando?

Fique conectado e acompanhe as últimas notícias, dicas e eventos para desenvolvedores.

Compartilhar:

https://a.storyblok.com/f/270183/400x385/12b3020c69/james-seconde.png
James SecondePromotor Sênior de Desenvolvimento em PHP

Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.