Dando continuidade ao post anterior, hoje vamos configurar o Typeorm e escrever o primeiro crud.
OBS: Sugiro que para o bom entendimento do tutorial, o caro leitor já esteja familiarizado com uso do Docker
Typeorm
Como o próprio nome ja diz, o Typeorm é o cara que vai nos ajudar a conectar no banco e manipular seus dados.
Sem muita enrolação, bora pro código.
Instalações
Começamos instalando algumas dependências:
yarn add typeorm reflect-metadata mongodb && yarn add @types/mongodb -D
Após o fim da instalação, precisamos importar o reflect-metadata em um arquivo global da nossa aplicação.
Configurações
src/app.ts
.
.
.
import 'reflect-metadata';
class App {
.
.
.
}
Vamos atualizar o nosso arquivo global de environments com alguns novos dados:
src/config/index.ts
import { config } from 'dotenv';
const envfile = `.env.${process.env.NODE_ENV}`;
const envdir = process.cwd();
config({ path: `${envdir}/${envfile}` });
export const server = {
port: process.env.PORT,
env: process.env.NODE_ENV,
};
// dados de conexão com o banco
export const dbConnections = {
mongo: {
name: 'mongo',
conn: String(process.env.DATABASE_MONGO_CONN),
},
};
Agora criamos as nossas configurações de conexão com o banco:
src/config/db/index.ts
import { createConnections } from 'typeorm';
import { dbConnections, server } from '../index';
const connection = createConnections([
{
name: dbConnections.mongo.name,
type: 'mongodb',
url: dbConnections.mongo.conn,
entities: [],
useNewUrlParser: true,
useUnifiedTopology: true,
synchronize: server.env === 'dev', // Se o ambiente for dev, o typeorm se incarrega de gerar e alterar as tabelas
},
]);
export default connection;
Após todas as configurações feitas, precisamos alterar o start da nossa aplicação e também adicionar a url de conexão nas variaveis de ambiente.
.env.dev
PORT=3000
DATABASE_MONGO_CONN=mongodb://localhost:27017/example
Primeiro conectamos na base, e no sucesso da conexão startamos a API.
src/server.ts
import connection from '@config/db';
import { server } from '@config/index';
import logger from '@middlewares/logger';
connection.then(() => {
logger.info(`Database connected`);
// precisamos importar o express somente após a conexão com a base, ou então o typeorm vai reclamar que alguns repositories não existem
require('./app').default.app.listen(server.port, () => {
logger.info('Server running', { port: server.port, mode: server.env });
});
});
E agora para testar nossa conexão, vamos utilizar do Docker/Docker Compose para subir uma imagem do MongoDB
docker-compose.yml
version: '3'
volumes:
mongo_volume:
driver: local
services:
mongo:
image: mongo
container_name: mongo_example
ports:
- '27017:27017'
Vamos subir o banco e iniciar a api e ver o que aparece no console.
docker-compose up -d
yarn start:dev
Primeira Entidade
Já estamos conectando na base de dados, mas ainda não temos nenhuma entididade definida.
Vamos escrever nossa primeira entidade.
Com o typeorm, nós definimos uma classe com alguns @decorators e a mágica acontece:
Adicionamos uma nova pastinha na nossa estrutura: src/apps/Users
A única regra na nossa collection de usuários é que não pode existir dois com o mesmo documento
src/apps/Users/Users.entity.ts
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
Index,
ObjectIdColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class Users extends BaseEntity {
@ObjectIdColumn({
type: 'uuid',
})
_id!: string;
@Column()
name!: string;
@Column()
@Index({ unique: true })
document!: string;
@Column()
password!: string;
@CreateDateColumn({
type: 'timestamp',
})
createdAt!: Date;
@UpdateDateColumn({
type: 'timestamp',
nullable: true,
})
updatedAt?: Date;
}
E por final vamos passar nossas entidades nas configurações do typeorm:
src/config/db/index.ts
import { createConnections } from 'typeorm';
import { Users } from '@apps/Users/Users.entity';
import { dbConnections, server } from '../index';
const connection = createConnections([
{
name: dbConnections.mongo.name,
type: 'mongodb',
url: dbConnections.mongo.conn,
entities: [Users],
useNewUrlParser: true,
useUnifiedTopology: true,
synchronize: server.env === 'dev', // Se o ambiente for dev, o typeorm se incarrega de gerar e alterar as tabelas
},
]);
export default connection;
Após o restart da aplicação, já conseguimos ver a collection de usuários criada, para isso faça o download da extension MongoDB for VS Code
Clique no ícone do mongodb e configure a url de conexão
Com a base conectada e a collection criada, vamos escrever nossa classe de serviço e as rotas.
CRUD
Vamos escrever o crud de usuários
src/apps/Users/UserService.ts
import { CustomError } from 'express-handler-errors';
import { ObjectID } from 'mongodb';
import { getConnection, MongoRepository } from 'typeorm';
import { dbConnections } from '@config/index';
import { Users } from './Users.entity';
class UserService {
private readonly repository: MongoRepository<Users>;
constructor() {
this.repository = getConnection(
dbConnections.mongo.name
).getMongoRepository(Users);
}
async create(user: Users): Promise<Users> {
try {
const response = await this.repository.save(user);
return response;
} catch (e) {
if (e.code === 11000)
throw new CustomError({
code: 'USER_ALREADY_EXISTS',
message: 'Usuário já existente',
status: 409,
});
throw e;
}
}
async findOne(_id: string): Promise<Users> {
const user = await this.repository.findOne(_id);
if (!user)
throw new CustomError({
code: 'USER_NOT_FOUND',
message: 'Usuário não encontrado',
status: 404,
});
return user;
}
async update(_id: string, name: string): Promise<Users> {
await this.repository.updateOne(
{
_id: new ObjectID(_id),
},
{
$set: {
name,
},
}
);
return this.findOne(_id);
}
async delete(_id: string): Promise<Users> {
const user = await this.findOne(_id);
await this.repository.deleteOne({
_id: new ObjectID(_id),
});
return user;
}
}
export default new UserService();
src/apps/Users/UsersController.ts
import { Request, Response } from 'express';
import UserService from './UserService';
export const create = async (
req: Request,
res: Response
): Promise<Response> => {
const response = await UserService.create(req.body);
return res.json(response);
};
export const findOne = async (
req: Request,
res: Response
): Promise<Response> => {
const response = await UserService.findOne(req.params.id);
return res.json(response);
};
export const update = async (
req: Request,
res: Response
): Promise<Response> => {
const response = await UserService.update(req.params.id, req.body.name);
return res.json(response);
};
export const deleteOne = async (
req: Request,
res: Response
): Promise<Response> => {
const response = await UserService.delete(req.params.id);
return res.json(response);
};
src/apps/routes.ts
import { Router } from 'express';
import * as controller from './UserController';
const route = Router();
route.post('/', controller.create);
route.get('/:id', controller.findOne);
route.put('/:id', controller.update);
route.delete('/:id', controller.deleteOne);
export default route;
E por final configuramos a rota de usuários, no arquivo global das rotas;
src/routes.ts
import { Router } from 'express';
import UserRoutes from '@apps/Users/routes';
const route = Router();
route.use('/users', UserRoutes);
export default route;
Testando o CRUD
Instale a extension REST Client.
Na raiz do projeto crie um arquivo requests.http
- Criando usuário
requests.http
POST http://localhost:3000/api/users HTTP/1.1
Content-Type: application/json
{
"name": "Vitor",
"document": "42780908890",
"password": "1234"
}
Repare que vai ter uma label escrito Send Request
, clique nela e o request será realizado.
- Buscando o usuário pelo Id
requests.http
.
.
.
GET http://localhost:3000/api/users/6001abf43d4675bc1aa693bd HTTP/1.1
Se atualizarmos a aba do mongodb, também podemos buscar o usuário lá.
- Atualizando o nome
requests.http
.
.
.
PUT http://localhost:3000/api/users/6001abf43d4675bc1aa693bd HTTP/1.1
Content-Type: application/json
{
"name": "Vitor Delfino"
}
- Excluindo o usuário
requests.http
.
.
.
DELETE http://localhost:3000/api/users/6001abf43d4675bc1aa693bd HTTP/1.1
Error Handler
E como ficaram os responses caso o usuário não exista ou o documento ja tenha sido cadastrado
Considerações finais
Hoje configuramos o primeiro serviço da nossa aplicação.
Na estrutura sugerida, os serviços ficam dentro de uma pasta apps e caso a aplicação aumente muito, e cada serviço precise se tornar uma aplicação a parte, está fácil fazer a quebra do nosso mini monolito.
Só precisamos fazer a configuração básica do ultimo post para cada serviço desaclopado.
O que está por vir
No próximo post, vamos fazer algumas validações com Yup antes de cadastrar usuários, e também escrever um swagger.
E pra facilitar o teste das nossas rotas, vamos configurar um Insomnia.
Top comments (0)