🚀 ORM com Node.js

🧠 1. Conceitos Fundamentais de ORM

  • O que é um ORM?
    • Object-Relational Mapping (Mapeamento Objeto-Relacional).
    • Uma camada de abstração que atua como tradutor entre o código orientado a objetos (JS/TS) e o banco de dados relacional (SQL).
  • O Problema que Resolve
    • Antes do ORM: Escrever SQL puro no código.
      • const query = "INSERT INTO books (title, author) VALUES (?, ?)"
      • Risco de SQL Injection.
      • Verboso e repetitivo.
      • Acoplamento com o dialeto SQL específico (MySQL, PostgreSQL, etc.).
  • Com o ORM: Usar métodos e objetos da própria linguagem.
    • await Book.create({ title: '...', author: '...' })
  • Vantagens Principais
    • Aumento de Produtividade: Menos código para escrever. Foco na regra de negócio.
    • Independência do Banco de Dados: Trocar de SQLite para PostgreSQL exige mudança mínima (apenas na configuração da conexão).
    • Segurança: Prevenção automática contra SQL Injection na maioria das operações.
    • Código Mais Limpo e Legível: A manipulação de dados se assemelha à manipulação de objetos comuns.
    • Recursos Avançados: Gestão de migrações (versionamento do schema do DB), transações e relacionamentos complexos (1:1, 1:N, N:M).

📚 2. Foco Principal: Sequelize + SQLite

  • ⚙️ 2.1. Configuração do Ambiente

    • 1. Iniciar o projeto Node.js
      • mkdir aula-biblioteca
      • cd aula-biblioteca
      • npm init -y (Cria o package.json)
    • 2. Instalar dependências
      • Sequelize: O ORM em si.
      • SQLite3: O "driver" que permite ao Sequelize "falar" com o SQLite.
      • npm install sequelize sqlite3
  • 🔌 2.2. Conexão com o Banco

    • Criar o arquivo database.js.
    • O SQLite é um banco de dados "serverless", ele funciona com um único arquivo no seu projeto.
    • // database.js
      const { Sequelize } = require('sequelize');
      
      const sequelize = new Sequelize({
        dialect: 'sqlite',
        storage: 'biblioteca.sqlite', // Nome do arquivo do banco
      });
      
      module.exports = sequelize;
      
  • 📖 2.3. Definição do Modelo Book

    • Um Modelo (Model) representa uma tabela no banco de dados.
    • Criar o arquivo book.js.
  • // book.js
    const { DataTypes } = require('sequelize');
    const sequelize = require('./database');
    
    const Book = sequelize.define('Book', {
      // Atributos (colunas da tabela)
      title: {
        type: DataTypes.STRING,
        allowNull: false, // NOT NULL
      },
      author: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      publicationYear: {
        type: DataTypes.INTEGER,
      },
      isbn: {
        type: DataTypes.STRING,
        unique: true, // Garante que não haja ISBNs duplicados
      },
      // O Sequelize cria 'id', 'createdAt' e 'updatedAt' por padrão.
    });
    
    module.exports = Book;
    
  • ✨ 2.4. Sincronização

    • É o processo de criar a estrutura da tabela no banco de dados com base no modelo definido.
    • sequelize.sync(): Compara os modelos definidos no código com o banco de dados e cria as tabelas que não existem.
      • { force: true }: Atenção! Apaga a tabela se ela já existir e a recria do zero. Útil apenas em desenvolvimento para testar alterações no modelo. Em produção, usa-se migrations.
  • // index.js
    const sequelize = require('./database');
    const Book = require('./book');
    
    async function setup() {
      await sequelize.sync({ force: true });
      console.log('Tabela de livros (re)criada!');
    }
    setup();
    
  • 🔄 2.5. Operações CRUD (Create, Read, Update, Delete)

    • CREATE (Criar): Adicionar um novo registro.
      • Usa o método Model.create().
      • await Book.create({
          title: '1984',
          author: 'George Orwell',
          publicationYear: 1949,
        });
        
  • READ (Ler): Buscar registros.
    • Model.findAll(): Retorna um array com todos os registros.
    • Model.findByPk(id): Busca um registro pela sua chave primária (Primary Key).
    • Model.findOne({ where: { ... } }): Busca o primeiro registro que corresponda à condição.
    • // Buscar todos
      const allBooks = await Book.findAll();
      // Buscar por uma condição
      const orwellBook = await Book.findOne({
        where: { author: 'George Orwell' },
      });
      
  • UPDATE (Atualizar): Modificar um registro existente.
    • Usa o método Model.update(dados, { where: { ... } }).
    • O primeiro argumento é um objeto com os novos dados.
    • O segundo argumento define quais registros serão atualizados.
    • await Book.update(
        { publicationYear: 1950 }, // Novos dados
        { where: { title: '1984' } } // Condição
      );
      
  • DELETE (Deletar): Remover um registro.
    • Usa o método Model.destroy({ where: { ... } }).
    • A condição where é crucial para não apagar dados indesejados.
    • await Book.destroy({
        where: { id: 1 },
      });
      

⚡️ 3. ORMs Alternativos: Prisma & TypeORM

  • 🅿️ Prisma

    • Abordagem "Schema First": A "fonte da verdade" é um arquivo schema.prisma.
      • O schema define o banco de dados E o cliente que será gerado.
      • schema.prisma para Livros:
        model Book {
          id              Int      @id @default(autoincrement())
          isbn            String   @unique
          title           String
          author          String
          publicationYear Int?
        }
        
  • Cliente Totalmente Type-Safe: Gera um cliente de banco de dados para JavaScript/TypeScript com tipagem automática.
    • Oferece um autocompletar excelente e previne erros em tempo de desenvolvimento.
    • prisma.book.findMany({ where: { ... } })
  • Migrations Explícitas e Seguras: Comandos como prisma migrate dev analisam o schema, geram o arquivo de migração SQL e o aplicam. É mais seguro e controlável que sync({ force: true }).
  • 🅾️ TypeORM

    • Foco Principal em TypeScript: Utiliza Decorators para definir entidades, uma sintaxe muito comum em outras linguagens como Java (Hibernate) e C# (Entity Framework).
    • Padrão Repository ou Active Record: Oferece flexibilidade de arquitetura.
      • Repository: Separa o modelo (Entity) da sua lógica de persistência (Repository).
  • Definição da Entidade Book:

    import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
    
    @Entity()
    export class Book {
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column({ unique: true })
      isbn: string;
    
      @Column()
      title: string;
    
      @Column()
      author: string;
    }
    

⚖️ 4. Conclusão e Quadro Comparativo

Característica Sequelize TypeORM Prisma
Paradigma Active Record (predominante) Active Record & Data Mapper (flexível) Schema First, com um cliente gerado
Linguagem JavaScript (bom suporte a TS) TypeScript First Agnóstico (gera cliente para JS/TS)
Tipagem (TS) Boa, mas muitas vezes manual Excelente e Nativa Excelente e Automática (melhor da categoria)
Curva de Aprendizagem Moderada Moderada (exige conhecimento de TS e Decorators) Baixa para Moderada (fácil de começar)
Característica Sequelize TypeORM Prisma
Ideal Para... Projetos legados, JS puro, ecossistema maduro. Projetos complexos em TS, equipes com background em Java/C#. Novas aplicações (especialmente serverless), projetos que priorizam a segurança de tipos e a experiência do desenvolvedor.

https://docs.google.com/spreadsheets/d/1DHSGcN1QHK-QXmbaXzbSu4wWnejEFH-zt9ItnrvwU0E/edit?usp=sharing