Introdução ao TypeORM, aqui irei mostrar como foi a minha impressão usando o ORM, então para esse projeto criei uma simples API de criação e listagem de usuários.
Esse projeto irá abordar sobre Migrations, e arquitetura MVC.
Caso queira navegar por tópicos, aqui está os tópicos abordados:
- Setup do Projeto
- Configurando Typescript
- Inicio do projeto com Express
- Trabalhando com TypeORM
- Views
- Resultado
- Conclusão
Nesse projeto estou usando:
- Node.js
- Typescript
- Express
- TypeORM
- SQLITE
também estou usando outras tecnologias:
- Ts-node-dev ( dar reload automático )
- Insomnia ( fazer requisições )
O Projeto foi criado no seguinte formato:
Instalando Typescript:
npm install typescript -D
Instalando Express e Tipagem:
npm install express
npm install @types/express -D
Instalando SQLITE:
npm install sqlite3
Instalando TypeORM:
npm install typeorm
{
"name": "orm",
"version": "1.0.0",
"main": "server.ts",
"license": "MIT",
"scripts": {
"dev": "tsnd --transpile-only --ignore-watch node_modules ./src/server.ts",
"typeorm": "ts-node-dev ./node_modules/typeorm/cli.js"
},
"dependencies": {
"express": "^4.17.1",
"sqlite3": "^5.0.0",
"typeorm": "^0.2.28"
},
"devDependencies": {
"@types/express": "^4.17.8",
"ts-node-dev": "^1.0.0",
"typescript": "^4.0.3"
}
}Esse arquivo possui alguns scripts, para uso da cli.js do typeorm e para iniciar o projeto com ts-node-dev.
Aqui irei mostrar quais foram as configurações usadas com o Typescript.
{
"compilerOptions": {
// Basic Options
"target": "es2017",
"module": "commonjs",
"allowJs": true,
// Strict Type-Checking Options
"strict": true,
"strictPropertyInitialization": false,
// Module Resolution Options
"esModuleInterop": true,
// Experimental Options
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// Advanced Options
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}O arquivo não teve muitas mudanças como você pode ver e encontrar aqui.
Só foi alterado algumas opções:
target foi alterado para es2017, que é a versão que você irá dar suporte. ( O padrão é es5 )
strictPropertyInitialization foi adicionado e alterado para false, para permitir a inicialização de propriedades sem atribuir algum valor a elas. ( O padrão é true )
experimentalDecorators e emitDecoratorMetadata foram adicionados e atribuídos como true para permitir o uso de Decorators, que será usado pelo TypeORM. ( O padrão é false )
Aqui temos 3 arquivos que trabalharam diretamente com o express, app.ts, server.ts e routes.ts.
Aqui é onde iremos importar o express e criar nosso app:
const app = express();nosso app.ts irá ficar assim:
import express from 'express';
import routes from './routes';
import './database/connection';
const app = express();
app.use(express.json());
app.use(routes);
export default app;No app.ts temos também a importação das rotas e da conexão com o TypeORM.
e temos o use do express que permitirá ele reconhecer e trabalhar com JSON, e usar as routes.
Nosso routes.ts está da seguinte forma:
import { Router } from 'express';
import UserController from './controllers/UserController';
const routes = Router();
// Rota de listagem de usuários
routes.get('/users', UserController.index);
// Rota de listagem de um único usuário
routes.get('/users/:id', UserController.show);
// Rota de criação de usuário
routes.post('/user', UserController.create);
export default routes;Aqui foi importado de dentro do express o Router para a criação das rotas, e temos a importação do nosso Controller, UserController.ts, que você pode encontrar aqui.
Aqui basicamente só importamos e iniciamos nosso app:
import app from './app';
app.listen(3333);Aqui mostrarei como foi a configuração, e uso do TypeORM seguindo a documentação.
{
"type": "sqlite",
"database": "./src/database/database.sqlite",
"migrations": [
"./src/database/migrations/*.ts"
],
"entities": [
"./src/models/*.ts"
],
"cli": {
"migrationsDir": "./src/database/migrations"
}
}Aqui temos algumas configurações para o TypeORM, seguindo a documentação.
Aqui type é o banco de dados que está sendo usando.
Em database iremos passar o path do bando de dados.
Já os outros como o próprio nome diz migrations é onde você irá passar o path das migrations e entities é onde você irá passar o path dos models.
para mais detalhes você pode acessar a documentação aqui.
Agora iremos conectar com o TypeORM para começarmos a útilizar.
Nesse projeto eu criei um arquivo connection.ts na pasta database com o seguinte conteúdo:
import { createConnection } from 'typeorm';
createConnection();e chamei esse arquivo no app.ts
import './database/connection';para mais detalhes acesse a documentação aqui.
Entity é uma classe que mapeia para a DB ( ou coleção quando usamos MongoDB ).
Para criar uma Entity, você pode atribuir @Entity a uma Class.
Para mapear as colunas de uma Class para o banco de dados, usamos @Column.
e assim fica o nosso Model em User.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')
export default class User {
@PrimaryGeneratedColumn('increment')
id: number;
@Column()
name: string;
@Column()
age: number;
}Podemos ver que a mais alguns tipos de Entity Column atribuídos, e para saber mais sobre Entity e Column você pode acessar a documentação aqui.
Em produção, trabalhando com um time você precisa ter um projeto sincronizado, para não gerar conflitos, e para isso que existem as migrations.
Usando a CLI do TypeORM iremos criar nossas migrations, esse projeto usa somente uma migration para a tabela de criação de usuário.
Para criar uma nova migration usando a CLI do TypeORM basta usar o seguinte comando:
usando npm:
npx typeorm migration:create -n Userou usando yarn:
yarn typeorm migration:create -n Useronde User é o nome da migration, quando for gerada a migration, você verá algo como {TIMESTAMP}-User.ts, onde {TIMESTAMP} é um registro de data e horário que a migration foi gerada.
Quando você entrar no arquivo gerando pela CLI, verá algo parecido com isso:
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class User1603254408159 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}Aqui temos up e down, em up ficará todas as alterações que você quer fazer, e em down ficará o reverso do que você fará em up.
Em up criaremos nossa tabela, já em down iremos deletar.
ficando assim:
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class User1603254408159 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
queryRunner.createTable(new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'integer',
unsigned: true,
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'name',
type: 'varchar',
},
{
name: 'age',
type: 'decimal',
},
]
}))
}
public async down(queryRunner: QueryRunner): Promise<void> {
queryRunner.dropTable('users');
}
}Aqui criei uma tabela chamada users, com as colunas, id, name, age, em formato de objeto, onde:
id é auto increment do tipo integer.
name é do tipo varchar e será preenchido na requisição.
age é do tipo decimal e também será preenchido na requisição de criação de usuário.
Usando a CLI do TypeORM, usamos o seguinte comando:
usando npm:
npx typeorm migration:runou usando yarn:
yarn typeorm migration:rune conforme foi configurado o arquivo ormconfig.json na propriedade database, será criado um arquivo chamando database.sqlite na pasta database, logo abaixo das migrations e do connection.ts.
Para mais detalhes de como trabalhar com migration no TypeORM acesse a documentação aqui.
Repository é apenas um tipo de gerenciador de Entidades mas com operações limitadas uma uma Entity concreta.
Você pode acessar um repository usando getRepository() e passando a Entity que foi criado, no caso o model User.
const userRepository = getRepository(User);um exemplo de uso é na criação de um usuário:
async create(request: Request, response: Response) {
const { name, age } = request.body;
const userRepository = getRepository(User);
const data = {
name,
age,
};
const user = userRepository.create(data);
await userRepository.save(user);
return response.status(201).json(user);
}Você pode encontrar esse código no UserController.ts, aqui.
Para mais detalhes de como usar os Repository do TypeORM, acesse a documentação aqui.
Para fazer uma chamada a um banco de dados usando TypeORM basicamente você irá usar o find().
como na listagem de todos os usuários:
async index(response: Response) {
const userRepository = getRepository(User);
const users = await userRepository.find();
const userRender = UserView.renderMany(users);
return response.status(200).json(userRender);
}onde usamos somente:
const users = await userRepository.find();sem passar nada a find(), e isso nos retornará todas as informações do banco de dados.
já na listagem de um único usuário:
async show(request: Request, response: Response) {
const { id } = request.params;
const userRepository = getRepository(User);
const user = await userRepository.findOne(id);
return response.status(200).json(user);
}temos:
const user = await userRepository.findOne(id);temos o findOne() que nos retornará somente uma associação da tabela, aqui é passado o id, que terá o retorno de um único usuário que possui o id igual ao que foi passado.
você pode encontrar o código em UserController.ts, aqui.
e para mais detalhes sobre find, acesse a documentação aqui.
Agora vou falar sobre as Views que apareceram na listagem de usuários.
Usar views pode ser útil para retornar dados depois de uma requisição, pois em alguns casos você não irá querer retornar todos os dados que a chamada ao banco de dados trás, como no nosso projeto, por exemplo, aqui quando é feita uma requisição para listar todos os usuários, é feita uma chamada ao banco de dados, que acaba retornando todas as informações dos usuários, no caso, id, name e age, mas eu quero retornar somente os nomes, então é usado as views para filtrar as informações e retornar ao usuário somente o que você realmente deseja.
o arquivo users_view.ts está assim:
import User from "../models/User";
export default {
render(user: User) {
return {
name: user.name
}
},
renderMany(users: User[]) {
return users.map(user => this.render(user))
}
}Aqui temos duas funções, render e renderMany que irão tratar os dados, render irá receber um usuário com todos os dados e retornar somente o name, já o renderMany irá tratar um array de usuários, percorrendo cada um e chamando a função render para tratar cada usuário que está sendo percorrido.
E agora chagamos ao resultado.
Começamos rodando nossa aplicação:
usando npm:
npm run devou yarn:
yarn devUsando o Insomnia para fazer as requisições, começamos criando um usuário:
Temos o seguinte resultado após a criação:
Agora com as Views entrando em ação iremos listar todos os usuários:
Temos esse resultado:
E listando somente um usuário, com o id 6, por exemplo, temos:
Com esse resultado:
Se você chegou até aqui, muito obrigado =)
Como considerações, posso dizer que o TypeORM tem muitas coisas que podem facilitar o desenvolvimento, assim como outras ferramentas, e ele não se limita a esses exemplos, o TypeORM tem uma documentação bem atrativa e com diversos exemplos e casos de uso, caso tenha interesse em conhecer mais, você pode acessar a documentação completa do TypeORM aqui.







