Type ORM
Conceitos
- O TypeORM é um ORM (Object Relational Mapping) que possui o intuito de abstrair a sintaxe já faciltiada de query builders;
- Permite a escrita de queries igualitária independente do banco de dados utilizado;
- Trabalha com o Knex por baixo dos panos.
Glossário de Comandos
Migrations
⚠ IMPORTANTE: Os comandos abaixos estão disponíveis apenas após configurar o script typeorm na seção subsequente
Criar migration
$ yarn typeorm migration: create -n NomeDaMigration
Rodar migrations
$ yarn typeorm migration:run
Reverter migrations
$ yarn typeorm migration:revert
Listar migrations já executadas
$ yarn typeorm migration:show
Configuração
Instalação do typeorm + o driver específico do banco (verificar em https://typeorm.io/#/connection-options).
$ yarn add typeorm reflect-metadata
Exemplo com o PostgreSQL
$ yarn add pg
Arquivo src/server.ts
Importação do reflect-metadata antes de tudo
import 'reflect-metadata';
Arquivo package.json
Implementação do script do TypeORM
{"scripts": {"typeorm": "ts-node-dev ./node_modules/typeorm/cli.js"}}
Arquivo ormconfig.json
Criação de arquivo de configurações
{"type": "postgres", // Tipo do banco de dados"host": "localhost", // IP da máquina ou link do banco"port": 5432, // Porta do banco"username": "postgres", // Usuário do banco"password": "docker", // Senha do banco"database": "banco_de_dados", // Database de acesso"entities": ["./src/models/*.ts" // Pasta onde armazenará as entidades],"migrations": ["./src/database/migrations/*.ts" // Arquivos de migrations],"cli": {"migrationsDir": "./src/database/migrations" // Diretório das migrations para interface de linha de comando}}
Arquivo src/database/index.ts
import { createConnection } from "typeorm";createConnection();
Arquivo src/server.ts
Importação do arquivo do TypeORM
import "./database";
Versão final do arquivo tsconfig.json
{"compilerOptions": {"target": "es5","module": "commonjs","outDir": "./dist","rootDir": "./src","strict": true,"strictPropertyInitialization": false,"esModuleInterop": true,"experimentalDecorators": true,"emitDecoratorMetadata": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true}}
Migrations
Após configurar os comandos acima, para criar uma migration basta rodar o comando $ yarn typeorm migration:create -n CreateAppointments
Exemplo de migration
import { MigrationInterface, QueryRunner, Table } from 'typeorm';export default class CreateAppointments1598219914693implements MigrationInterface {public async up(queryRunner: QueryRunner): Promise<void> {await queryRunner.createTable(new Table({name: 'appointments', // Nome da Tabela (separado por underline e no plural)columns: [{name: 'id',type: 'varchar',isPrimary: true,generationStrategy: 'uuid',default: 'uuid_generate_v4()', // Apenas para PostgreSQL},{name: 'provider',type: 'varchar',isNullable: false,},{name: 'date',type: 'timestamp with time zone', // Caso não seja PostgreSQL, deve-se usar apenas timestampisNullable: false,},],}),);}public async down(queryRunner: QueryRunner): Promise<void> {await queryRunner.dropTable('appointments');}}
Rodando as migrations
$ yarn typeorm migration:run
Models
Exemplo de Model
Arquivo src/models/Appointment.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';@Entity('appointments') // Definição de entidade para a tabela "appointments"class Appointment { // A entidade deve, obrigatóriamente, estar "colada" com a classe para funcionar@PrimaryGeneratedColumn('uuid') // Coluna primária com uuidid: string;@Column() // Coluna padrãoprovider: string;@Column('timestamp with time zone') // Coluna com formato específico para PostgreSQL. No caso de outros bancos como MySQL, deve-se usar apenas "timestamp" (esse formato não é habilitado para os demais banco de dados relacionais)date: Date;}export default Appointment;
Repositórios
Exemplo de Repositório
import { EntityRepository, Repository } from 'typeorm';import Appointment from '../models/Appointment';@EntityRepository(Appointment) // Declaração do repositório (passando a model como parâmetro)class AppointmentsRepository extends Repository<Appointment> { // Criação da classe do repositório, extendendo do "Repository" importado do typeorm e passando a model tambem como parâmetropublic async findByDate(date: Date): Promise<Appointment | null> { // Criação da função de buscar por data (exemplo), onde o retorno dessa função é uma promise que contem A) o appointment ou B) nullconst findAppointment = await this.findOne({ // Sintaxe de busca da tabela (findOne)where: {date,},});return findAppointment || null;}}export default AppointmentsRepository;
Exemplo de serviço para criar dados
Arquivo src/services/CreateAppointmentService.ts
import { startOfHour } from 'date-fns';import { getCustomRepository } from 'typeorm'; // Importação para obter um repositório personalizado (exemplificado acima)import Appointment from '../models/Appointment';import AppointmentsRepository from '../repositories/AppointmentsRepository';interface RequestDTO {provider: string;date: Date;}class CreateAppointmentService {public async execute({ provider, date }: RequestDTO): Promise<Appointment> { // Função assíncrona onde retorna uma promise que, como parâmetro, recebe a model de Appointmentconst appointmentsRepository = getCustomRepository(AppointmentsRepository); // Criação da variável que obtem os dados do repositórioconst appointmentDate = startOfHour(date);const findAppointmentInSameDate = appointmentsRepository.findByDate( // Função recuperada do repositórioappointmentDate,);if (findAppointmentInSameDate) {throw Error('This appointment is already booked.');}const appointment = appointmentsRepository.create({ // Criação da instância (antes de salvar no banco de dados)provider,date: appointmentDate,});await appointmentsRepository.save(appointment); // Método para salvar no banco de dadosreturn appointment;}}export default CreateAppointmentService;