O ccomando git init
cria um repositorio local no diretorio atual.
git init
Posso iniciar um repositorio no github e clona-lo localmente com o comando git clone
.
O yarn init --y
cria um package.json com as configurações padrões, sem precisar responder as perguntas.
O --y
é para aceitar as configurações padrões.
yarn init -y
O @types/node
é um pacote que contém arquivos de declaração de tipo para o Node.js. Isso permite que você escreva código TypeScript que importe módulos do Node.js.
yarn add @types/node -D
O TypeScript
é um superconjunto de JavaScript desenvolvido pela Microsoft que adiciona tipagem e alguns outros recursos a linguagem.
yarn add -D typescript
O @types/typescript
é um pacote que contém arquivos de declaração de tipo para o TypeScript. Isso permite que você escreva código TypeScript que importe módulos do TypeScript.
yarn add -D @types/typescript
Utilizo o yarn tsc --init
para iniciar o typescript, mas você pode utilizar o npx tsc --init
também.
Este comando irá criar um arquivo tsconfig.json
na raiz do projeto, que é o arquivo de configuração do typescript.
Você pode alterar as configurações do typescript neste arquivo.
yarn tsc --init
Dentro do arquivo tsconfig.json
você pode alterar as configurações do typescript para que o Typescript seja capaz de resolver módulos json.
"resolveJsonModule": true,
Abaixo temos uma forma reduzida de adicionar packages no package.json, utilizando o yarn add -D
e o yarn add
onde eu posso executar a instalação de vários packages ao mesmo tempo.
No caso abaixo eu estou instalando o typescript
, @types/node
e @types/typescript
no modo de desenvolvimento, e logo após executo o yarn tsc --init
para iniciar o typescript, tudo isso em uma única linha de comando.
# instalando conjunto de packages como dependencias de desenvolvimento
yarn add -D @types/node typescript @types/typescript tsc --init
O ts-node-dev é um utilitário que permite executar o código TypeScript diretamente, sem a necessidade de compilar o código para JavaScript.
yarn add -D ts-node-dev
No arquivo package.json
eu adiciono o script de inicialização da aplicação utilizando o ts-node-dev
e passando os parâmetros necessários.
"scripts": {
"dev": "ts-node-dev --inspect=0.0.0.0:3339 --transpile-only --ignore-watch node_modules --respawn src/server.ts"
},
No script acima eu estou passando os seguintes parâmetros:
- --inspect=0.0.0.0:3339, onde eu estou passando o endereço de ip e a porta para o debug do node.
- --transpile-only, onde eu estou passando para o ts-node-dev que ele deve transpilar o código apenas, sem verificar os erros de tipagem.
- --ignore-watch node_modules, onde eu estou passando para o ts-node-dev que ele deve ignorar a pasta node_modules.
- --respawn, onde eu estou passando para o ts-node-dev que ele deve reiniciar o servidor sempre que houver alterações no código.
O eslint
é uma ferramenta de linting para JavaScript e JSX. Ele analisa seu código JavaScript e aponta problemas. Ele é totalmente configurável e tem plugins para muitos ambientes populares, como React, Vue.js e Node.js.
yarn add -D eslint
Para iniciar o eslint, eu utilizo o comando yarn eslint --init
, mas você pode utilizar o npx eslint --init
também.
O @types/eslint
é um pacote que contém arquivos de declaração de tipo para o eslint. Isso permite que você escreva código TypeScript que importe módulos do eslint.
yarn add -D @types/eslint
Utilizo o yarn eslint --init
para iniciar o eslint, mas você pode utilizar o npx eslint --init
também.
Este comando irá criar um arquivo .eslintrc.js
na raiz do projeto, que é o arquivo de configuração do eslint.
Você pode alterar as configurações do eslint neste arquivo.
Ao executar o comando yarn eslint --init
você irá responder algumas perguntas, e o eslint irá criar o arquivo de configuração .eslintrc.js
com as configurações que você escolheu.
Você pode alterar as configurações manualmente no arquivo .eslintrc.js
ou executar o comando yarn eslint --init
novamente para alterar as configurações.
O eslint-plugin-import
é um plugin do eslint que fornece regras de lint para importações e exports.
yarn add -D eslint-plugin-import
O eslint-import-resolver-typescript
é um plugin do eslint que permite que você configure o eslint para resolver módulos do TypeScript.
yarn add -D eslint-import-resolver-typescript
O eslint-plugin-import-helpers
é um plugin do eslint que fornece regras de lint para organizar as importações.
yarn add -D eslint-plugin-import-helpers
O @typescript-eslint/eslint-plugin
é um plugin do eslint que fornece regras de lint para o TypeScript.
yarn add -D @typescript-eslint/eslint-plugin
O @typescript-eslint/parser
é um plugin do eslint que fornece um parser para o TypeScript.
yarn add -D @typescript-eslint/parser
O eslint-config-airbnb-base
é um plugin do eslint que fornece regras de lint para o Airbnb.
yarn add -D eslint-config-airbnb-base
O prettier
é uma ferramenta de formatação de código. Ele analisa seu código e aplica um conjunto de regras para formatar o código.
yarn add -D prettier
O @types/prettier
é um pacote que contém arquivos de declaração de tipo para o prettier. Isso permite que você escreva código TypeScript que importe módulos do prettier.
yarn add -D @types/prettier
Para configurar o eslint para utilizar o prettier, você deve instalar o seguinte pacote:
yarn add -D eslint-config-prettier
Precisamos o eslint-plugin-prettier para executar o prettier como uma regra do eslint.
yarn add -D eslint-plugin-prettier
node_modules
dist
/*.js
No arquivo .eslintignore
eu estou ignorando a pasta node_modules
e a pasta dist
, e também estou ignorando todos os arquivos .js
.
{
"env": {
"es2020": true,
"node": true,
"jest": true
},
"extends": [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"prettier",
"eslint-plugin-import-helpers"
],
"rules": {
"camelcase": "off",
"prettier/prettier": "error",
"import/no-unresolved": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "interface",
"format": [
"PascalCase"
],
"custom": {
"regex": "^I[A-Z]",
"match": true
}
}
],
"class-methods-use-this": "off",
"import/prefer-default-export": "off",
"no-shadow": "off",
"no-console": "off",
"no-useless-constructor": "off",
"no-empty-function": "off",
"lines-between-class-members": "off",
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never"
}
],
"import-helpers/order-imports": [
"warn",
{
"newlinesBetween": "always",
"groups": [
"module",
"/^@shared/",
[
"parent",
"sibling",
"index"
]
],
"alphabetize": {
"order": "asc",
"ignoreCase": true
}
}
],
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.spec.js"
]
}
]
},
"settings": {
"import/resolver": {
"typescript": {}
}
}
}
No arquivo .eslintrc.js
eu estou configurando o eslint para utilizar o TypeScript, o prettier e o Airbnb. Além disso, eu estou configurando algumas regras do eslint.
No ruless eu estou desabilitando algumas regras do eslint que não são necessárias para o projeto, e também estou configurando algumas regras do eslint.
Esou configurando o camelcase como off, pois o TypeScript já faz essa validação.
No "prettier/prettier" eu estou configurando o prettier para ser executado como uma regra do eslint.
No "import/no-unresolved" eu estou configurando o eslint para verificar se os imports estão corretos.
No "@typescript-eslint/naming-convention" eu estou configurando o eslint para verificar se as interfaces estão seguindo o padrão de nomeação do TypeScript.
No class-methods-use-this eu estou desabilitando a regra do eslint que obriga a utilizar o this em métodos de classe.
No import/prefer-default-export eu estou desabilitando a regra do eslint que obriga a utilizar o export default.
No no-shadow eu estou desabilitando a regra do eslint que obriga a utilizar o this em métodos de classe.
No no-console eu estou desabilitando a regra do eslint que obriga a utilizar o console.log.
No no-useless-constructor eu estou desabilitando a regra do eslint que obriga a utilizar o this em métodos de classe.
No no-empty-function eu estou desabilitando a regra do eslint que obriga a utilizar o this em métodos de classe.
No lines-between-class-members eu estou desabilitando a regra do eslint que obriga a utilizar o this em métodos de classe.
No "import/extensions" eu estou configurando o eslint para verificar se os imports estão com a extensão correta.
No "import-helpers/order-imports" eu estou configurando o eslint para ordenar os imports.
No "import/no-extraneous-dependencies" eu estou configurando o eslint para verificar se as dependências estão corretas.
No settings eu estou configurando o import/resolver
para utilizar o typescript
.
export const singleQuote = false;
// module.exports = {
// singleQuote: false,
// }
No arquivo prettier.config.js
eu estou configurando o prettier para utilizar aspas duplas.
O express
é um framework para Node.js que permite criar aplicações web.
yarn add express
O @types/express
é um pacote que contém arquivos de declaração de tipo para o express. Isso permite que você escreva código TypeScript que importe módulos do express.
yarn add -D @types/express
O swagger-ui-express
é um pacote que permite que você adicione o swagger ao seu projeto.
yarn add -D swagger-ui-express
O @types/swagger-ui-express
é um pacote que contém arquivos de declaração de tipo para o swagger-ui-express. Isso permite que você escreva código TypeScript que importe módulos do swagger-ui-express.
yarn add -D @types/swagger-ui-express
O arquivo swagger.json
é o arquivo que contém a configuração do swagger.
Neste arquivo eu configuro as informações do projeto, as rotas, os parâmetros, os schemas, etc.
Este arquivo deve ser criado na raiz do projeto.
{
"openapi": "3.0.0",
"info": {
"title": "API",
"description": "API",
"version": "1.0.0"
},
}
O swagger-jsdoc
é um pacote que permite que você adicione o swagger ao seu projeto.
yarn add -D swagger-jsdoc
O @types/swagger-jsdoc
é um pacote que contém arquivos de declaração de tipo para o swagger-jsdoc. Isso permite que você escreva código TypeScript que importe módulos do swagger-jsdoc.
yarn add -D @types/swagger-jsdoc
Para gerar a documentação automatica eu criei uma rota para o swagger:
import { Router } from "express";
import swaggerJsDoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import { swaggerOptions } from "../../swagger/swaggerOptions";
const swaggerRoutes = Router();
const swaggerDocs = swaggerJsDoc(swaggerOptions);
swaggerRoutes.use(
"/swagger/docs/default",
swaggerUi.serve,
swaggerUi.setup(swaggerDocs)
);
swaggerRoutes.use("/swagger", swaggerUi.serve);
swaggerRoutes.get(
"/swagger",
swaggerUi.setup(swaggerDocs, {
swaggerOptions: {
url: "/swagger/swagger.json",
},
})
);
swaggerRoutes.get("/swagger/swagger.json", (req, res) => res.json(swaggerDocs));
swaggerRoutes.get("/swagger/docs", (request, response) => {
const { protocol, baseUrl } = request;
const fullUrl = `${protocol}://${request.get("host")}${baseUrl}`;
const htmlFile = `
<body>
<div id="redoc-container"></div>
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.55/bundles/redoc.standalone.min.js"> </script>
<script src="https://cdn.jsdelivr.net/gh/wll8/redoc-try@1.4.1/dist/try.js"></script>
<script>
initTry({
openApi:'${fullUrl}/swagger/swagger.json'
});
</script>
</body>
`;
response.send(htmlFile);
});
export { swaggerRoutes };
Nesta rota estou passando um arquivo de configuração para o swagger-jsdoc.
import { swaggerApiErrorSchema } from "./schemas/swaggerApiErrorSchema";
import { swaggerApiResponseSchema } from "./schemas/swaggerApiResponseSchema";
import { swaggerCategorySchema } from "./schemas/swaggerCategorySchema";
import { swaggerSpecificationsSchema } from "./schemas/swaggerSpecificationsSchema";
const swaggerApiDescription = `
<p style="line-height: 0">API desenvolvida durante as aulas do curso Ignite Trilha NodeJS.\n
Esta api foi desenvolvida baseada na OpenAPI 3.0 specification. \n
Você pode conhecer um pouco mais sobre o swagger em [https://swagger.io](https://swagger.io). \n
Esta API foi desenvolvida durante as aulas do curso Ignite da Rocketseat. Durante as aulas fui
aprendendo sobre o desenvolvimento de APIs utilizando NodeJS e Typescript e durante seu desenvolvimento
fui apresentado aso conceitos do SOLID onde pude aplicar alguns de seus conceitos como o Single Responsability Principle,
Dependency Inversion Principle e o liskov substitution principle.\n
Trabalhamos também com a organização a estrutura de pastas e arquivos do projeto, e finalizamos com a documentação da API
utilizando o swagger.\n
Como conteúdo adicional busquei formas de melorar o processo de documentação e pude aplicar algumas técnicas utilizando o
wagger-jsdoc e o swagger-autogen para gerar a documentação automatica a API no momento de sua execução o que possibilita manter
a API sempre atualizada sem muito esforço, apliquei tamtém uma estilizaçao na API com o ReDoc.\n
</p>
`;
const swaggerOptions = {
swaggerDefinition: {
openapi: "3.0.3",
info: {
title: "RentalX - Api de Integração",
version: "1.0.0",
description: swaggerApiDescription,
contact: {
email: "claudneysartisessa@gmail.com",
},
},
servers: [
{
url: "{protocol}://{baseUrl}:{port}/v1",
variables: {
protocol: {
enum: ["http", "https"],
default: "http",
},
baseUrl: {
default: "localhost",
description:
"This value is assigned by the service provider, in this example `gigantic-server.com`",
},
port: {
enum: ["3333", "80", "8080"],
default: "3333",
},
},
},
],
components: {
schemas: {
Category: swaggerCategorySchema,
Specifications: swaggerSpecificationsSchema,
ApiError: swaggerApiErrorSchema,
ApiResponse: swaggerApiResponseSchema,
},
},
},
apis: ["**/*.ts"],
};
export { swaggerOptions };
Neste aqruivo eu criei um objeto com as configurações do swagger, como o titulo, a versão, a descrição, o contato, os servidores, os schemas e as apis que serão documentadas.
Criei também uma pasta para organizar os schemas e ps paths do swagger, mantendo assim a organização do projeto.
Estou utilizando o swagger-jsdoc para gerar a documentação da API, e o swagger-ui-express para exibir a documentação da API.
Desta forma pude incluir direto no projeto a possibilidade de documentar a API utilizando o swagger de forma automática, simples e organizada.
A documentação será gerada automaticamente a cada execução da API, e a documentação pode ser acessada através da rota /swagger/docs.
Como complemento na rota do swagger eu adicionei uma configuração para gerar uma API estilizada utilizando o ReDoc.
O swagger-autogen
é um pacote que permite que você adicione o swagger ao seu projeto.
yarn add -D swagger-autogen
O jest
é um framework de testes para JavaScript. Ele permite que você escreva testes unitários, de integração e de ponta a ponta.
yarn add -D jest
O @types/jest
é um pacote que contém arquivos de declaração de tipo para o jest. Isso permite que você escreva código TypeScript que importe módulos do jest.
yarn add -D @types/jest
O ts-jest
é um plugin do jest que permite que você execute testes com TypeScript.
yarn add -D ts-jest
O supertest
é um pacote que permite que você escreva testes de integração para o seu projeto.
yarn add -D supertest
O @types/supertest
é um pacote que contém arquivos de declaração de tipo para o supertest. Isso permite que você escreva código TypeScript que importe módulos do supertest.
yarn add -D @types/supertest
O uuid
é um pacote que permite que você gere um id único.
yarn add uuid
O uuid é um pacote que permite que você gere um id único.
Dentro do UUid temos 4 versões:
- v1: Gera um id baseado no tempo e no MAC address do computador.
- v2: Gera um id baseado no tempo e no MAC address do computador.
- v3: Gera um id baseado em um namespace e em um nome.
- v4: Gera um id aleatório.
- v5: Gera um id baseado em um namespace e em um nome.
Para esta aplicação, vamos utilizar a versão 4.
O @types/uuid
é um pacote que contém arquivos de declaração de tipo para o uuid. Isso permite que você escreva código TypeScript que importe módulos do uuid.
yarn add -D @types/uuid
O multer
é um pacote que permite que você faça upload de arquivos.
yarn add multer
O @types/multer
é um pacote que contém arquivos de declaração de tipo para o multer. Isso permite que você escreva código TypeScript que importe módulos do multer.
yarn add -D @types/multer
O csv-parser
é um pacote que permite que você faça upload de arquivos.
yarn add csv-parser
O @types/csv-parser
é um pacote que contém arquivos de declaração de tipo para o csv-parser. Isso permite que você escreva código TypeScript que importe módulos do csv-parser.
yarn add -D @types/csv-parser
O docker é uma ferramenta utilizada para a criação de containers, que são ambientes isolados, onde podemos rodar aplicações, serviços, bancos de dados, etc. Esses containers são isolados, mas podem se comunicar entre si, e com o host, que é o computador que está rodando o docker.
O docker é uma ferramenta muito utilizada em ambientes de desenvolvimento, pois ele permite que o ambiente de desenvolvimento seja o mesmo do ambiente de produção, e isso é muito importante para evitar problemas futuros.
# A instrução FROM indica a imagem base que será utilizada para a criação da imagem final.
FROM node
# A instrução WORKDIR define o diretório de trabalho atual para o diretório especificado.
WORKDIR /usr/app
# A instrução COPY copia novos arquivos ou diretórios do host e adiciona-os ao sistema de arquivos do container em execução.
COPY package.json ./
# A instrução RUN executa qualquer comando em uma nova camada e cria uma imagem.
RUN npm install
# A instrução COPY copia novos arquivos ou diretórios do host e adiciona-os ao sistema de arquivos do container em execução.
COPY . .
# A instrução EXPOSE informa ao Docker que o contêiner escuta na porta especificada em tempo de execução.
EXPOSE 3333
EXPOSE 3339
# A instrução CMD especifica o comando padrão que será executado quando o contêiner for iniciado.
CMD ["npm", "run", "debug"]
No arquivo a cima, temos o Dockerfile, que é o arquivo que contém as instruções para a criação do container.
O primeiro comando é o FROM, que indica qual imagem será utilizada para a criação do container. Nesse caso, estamos utilizando a imagem do node.
O segundo comando é o WORKDIR, que indica qual será o diretório de trabalho do container. Nesse caso, estamos utilizando o diretório /Users/claudneysessa/Developer/Docker/NodeJS/chapter02/rentx.
O terceiro comando é o COPY, que copia o arquivo package.json para o diretório de trabalho do container.
O quarto comando é o RUN, que executa o comando npm install, que instala as dependências do projeto.
O quinto comando é o COPY, que copia todos os arquivos do diretório atual para o diretório de trabalho do container.
O sexto comando é o EXPOSE, que expõe a porta 3333 do container.
O sétimo comando é o CMD, que executa o comando npm run dev, que inicia a aplicação.
Observe também que no projeto eu criei um .dockerignore
node_modules
.git
.vscode
Neste arquivo, eu indico quais arquivos e pastas não devem ser copiados para o container.
Aqui vamos gerar o build da imagem para o docker, para isso, vamos utilizar o comando:
docker build -t rentx .
Ao executar o comando acima, o docker irá criar a imagem rentx, utilizando o Dockerfile que está no diretório atual.
Aqui vamos executar o container e apontar a porta para utilização da API, para isso, vamos utilizar o comando:
docker run -p 3333:3333 rentx
Ao executar o comando acima, o docker irá executar o container rentx, e apontar a porta 3333 do container para a porta 3333 do host.
Aqui vamos executar comandos dentro do docker, para isso, vamos utilizar o comando:
docker exec -it recursing_bose /bin/bash
Ao executar o comando acima, o docker irá executar o container recursing_bose, e abrir o terminal do container, para que possamos executar comandos dentro do container.
O docker-compose é uma ferramenta para definir e executar aplicativos Docker com vários contêineres. Com o docker-compose, você usa um arquivo YAML para configurar os serviços do seu aplicativo. Em vez de executar um comando docker longo para iniciar um único contêiner, você pode executar docker-compose up e iniciar todos os serviços definidos em seu arquivo docker-compose.yml.
A diferença entre o Dockerfile e o docker-compose é que o Dockerfile é utilizado para criar uma imagem, e o docker-compose é utilizado para criar um container, com o docker-compose, podemos criar vários containers, e cada um deles pode ter uma imagem diferente.
# A instrução version indica qual versão do docker-compose será utilizada.
version: "3.7"
# A instrução services indica quais serviços serão utilizados.
services:
# A instrução db é o nome do serviço.
rentx:
# A instrução build indica qual será a imagem que será utilizada para a criação do container do serviço. Nesse caso, estamos utilizando a imagem rentx.
build: .
# A instrução container_name indica qual será o nome do container do serviço.
container_name: rentx
# A instrução ports indica quais portas serão expostas do container do serviço.
ports:
- "3333:3333"
- "3339:3339"
# A instrução volumes indica quais volumes serão utilizados do container do serviço.
volumes:
- .:/usr/app
No arquivo a cima, temos o docker-compose.yml, que é o arquivo que contém as instruções para a criação do container.
O primeiro comando é o version, que indica qual versão do docker-compose será utilizada.
O segundo comando é o services, que indica os serviços que serão utilizados.
O terceiro comando é o rentx, que indica o nome do serviço.
O quarto comando é o build, que indica qual será o diretório de trabalho do container. Nesse caso, estamos utilizando o diretório /Users/claudneysessa/Developer/Docker/NodeJS/chapter02/rentx.
O quinto comando é o container_name, que indica o nome do container.
O sexto comando é o ports, que expõe a porta 3333 do container.
O sétimo comando é o volumes, que indica o diretório que será utilizado para o volume.
O comando docker-compose up
cria e inicia todos os contêineres definidos em um arquivo docker-compose.yml. Ele também cria as redes, inicia os volumes e define as configurações padrão especificadas no arquivo docker-compose.yml.
O comando docker-compose up -d
cria e inicia todos os contêineres definidos em um arquivo docker-compose.yml em segundo plano, mantendo o container em execução.
# Iniciando o container runtime
docker-compose up
# Iniciando o container background
docker-compose up -d
Com o docker-compose up
você pode iniciar todos os containers de uma vez, e com o docker-compose up -d
você pode iniciar todos os containers em background, ou seja, você pode continuar utilizando o terminal, sem precisar ficar parado esperando o container iniciar.
O comando docker-compose ps
lista os contêineres em execução.
O comando docker-compose ps -a
lista todos os contêineres criados incluindo os que não estão em execução.
# Listar containers ativos
docker ps
# Listar todos os containers
docker ps -a
Ao utilizar o comando docker ps
, podemos ver que os containers que estão em execução.
Ao utilizar o comando docker ps -a
, podemos ver que os containers que estão em execução e os que não estão em execução.
Com este comando apresenta os logs do container quando este estiver sendo executado em background.
# Executando os logs do container rentx
docker logs rentx -f
Ao utilizar o comando docker logs rentx -f
é possível acompanhar os logs do container rentx em tempo real.
Com este comando eu abro um canal de comunicação com o container, assim eu consigo executar comandos dentro do container.
# Executando comandos dentro do container rentx
docker exec -it rentx /bin/bash
Ao utilizar o comando docker exec -it rentx /bin/bash
é possível executar comandos dentro do container rentx, como por exemplo, executar o comando yarn dev
.
Com este comando eu paro o container.
docker-compose stop
O comando docker-compose stop
para todos os contêineres definidos em um arquivo docker-compose.yml. Ele também para contêineres associados a contêineres definidos no arquivo docker-compose.yml.
Com este comando eu removo o container.
docker-compose down
O comando docker-compose down
remove os contêineres, as redes, os volumes e as imagens criadas pelo comando docker-compose up.
O debugger é uma ferramenta que permite que você execute seu código passo a passo, analisando o valor de cada variável em cada momento. Ele é muito útil para encontrar erros e bugs no seu código.
Para configurar o debugger, precisamos criar um arquivo chamado launch.json, que fica dentro da pasta .vscode.
Na configuração abaixo estou habilitando o debugger no F5 de 2 formas.
A primeira eu faço o debugger local com o ts-node-dev possibilitando o debugger local com a reinicialização automática do servidor.
{
"name": "Run and Debugger",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ts-node-dev",
"program": "${workspaceFolder}/src/server.ts",
"console": "internalConsole",
"internalConsoleOptions": "neverOpen",
"autoAttachChildProcesses": true,
"restart": true,
"sourceMaps": true,
"smartStep": true,
"trace": true,
"showAsyncStacks": true,
"timeout": 30000,
"skipFiles": [
"<node_internals>/**"
],
"env": {
"NODE_ENV": "development"
}
}
A segunda configuração eu habilito o debugger no docker, possibilitando o debugger no container.
{
"name": "Docker: Attach to Node",
"type": "node",
"request": "attach",
"restart": true,
"port": 3339,
"remoteRoot": "/usr/app"
}
Para utilizar o debugger no container nos executamos o docker-compose up -d, e depois executamos o comando F5 para attachar no container.
Observe que estou utilizando a porta 3339, pois a porta 3333 está sendo utilizada pelo container.
No meu script do docker-compose.yml eu estou utilizando a porta 3333 para a API, e estou utilizando a porta 3339 para o --inspect=0.0.0.0:3339 configurado la no meu package.json quando solicito a execução pelo ts-node-dev.
O TypeORM é um ORM que pode ser utilizado com Node.js, TypeScript, JavaScript, Ionic, Cordova, PhoneGap, NativeScript, Expo, React Native, Vue.js, Angular, Nest, Ionic, Meteor e outras plataformas que suportam JavaScript.
Para instalar o TypeORM, precisamos executar o comando abaixo.
yarn add typeorm
yarn add reflect-metadata
Para configurar o tsconfig.json, precisamos adicionar o código abaixo.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
}
}
Vamos instalar o driver do banco de dados que vamos utilizar, no meu caso eu vou utilizar o Postgres.
yarn add pg
Para iniciar a configuração crio uma pasta database dentro da pasta src.
Dentro da pasta database crio um arquivo chamado index.ts.
import { createConnection } from 'typeorm';
createConnection();
Crio também um arquivo chamado ormconfig.json na raiz do projeto.
{
"type": "postgres",
"host": "localhost",
"username": "postgres",
"password": "docker",
"database": "rentx",
"port": 5432,
"migrations": [
"./src/database/migrations/*.ts"
],
}
Para verificar o IP do container, precisamos executar o comando abaixo.
docker inspect rentx | grep IPAddress
ou
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ignite_rentx
Para ajustar a network do container precisamos ajustar o docker-compose.yml adicionando as opções de network dos containers
Para forçar a substituição do container, precisamos executar o comando abaixo.
docker-compose up --force-recreate
Após muita luta consegui resolver o problema de conexão alterando o docker-compose.yml da seguinte forma:
version: "3.7"
services:
ignite_rentx_database:
image: postgres
container_name: ignite_rentx_database
restart: always
ports:
- 5432:5432
environment:
POSTGRES_USER: docker
POSTGRES_PASSWORD: ignite
POSTGRES_DB: rentx
volumes:
- pgdata:/data/postgres
networks:
- rentx_lan
ignite_rentx:
build: .
container_name: ignite_rentx
ports:
- "3333:3333"
- "3339:3339"
volumes:
- .:/usr/app
environment:
- "DATABSE_URL=postgres://docker:ignite@ignite_rentx_database:5432/rentx"
depends_on:
- ignite_rentx_database
networks:
- rentx_lan
volumes:
pgdata:
driver: local
networks:
rentx_lan: {}
Também tive que remover o ormconfig.json e adicionar a variável de ambiente DATABASE_URL no docker-compose.yml.
Após esta configuração alterei o arquivo database/index.ts para o código abaixo:
- No local do localhost eu coloquei o nome do container do banco de dados.
import { DataSource } from "typeorm";
const appDataSource = new DataSource({
type: "postgres",
host: "ignite_rentx_database",
port: 5432,
username: "docker",
password: "ignite",
database: "rentx"
});
appDataSource.initialize()
.then(() => {
console.log("Connected to database!");
})
.catch((error) => {
console.error("Connected fail!", error);
});
E por final tive que remover todo container e fazer um novo docker-compose up -d
para funcionar.
Depois de ter o código funcionando fui testando e verificando que estava adicionado de forma excessiva no docker-compose.yml e cheguei a seguinte configuração:
version: "3.7"
services:
ignite_rentx_database:
image: postgres
container_name: ignite_rentx_database
restart: always
ports:
- 5432:5432
environment:
POSTGRES_USER: docker
POSTGRES_PASSWORD: ignite
POSTGRES_DB: rentx
volumes:
- pgdata:/data/postgres
ignite_rentx:
build: .
container_name: ignite_rentx
ports:
- "3333:3333"
- "3339:3339"
volumes:
- .:/usr/app
volumes:
pgdata:
driver: local
Seguem alguns POSTS que me ajudaram a resolver o problema:
- Não conecta o Banco de Dados
- erro de conexão com o banco de dados
- Error: getaddrinfo EAI_AGAIN image_name
- Problemas ao configurar banco de dados
Adicionar os scripts typeorm e migrations no package.json
"scripts": {
"typeorm": "ts-node-dev ./node_modules/typeorm/cli.js",
"migrations": "ts-node-dev ./node_modules/typeorm/cli.js migration:create"
},
import { DataSource } from "typeorm";
const appDataSource = new DataSource({
type: "postgres",
// host: "ignite_rentx_database",
host: "localhost",
port: 5432,
username: "docker",
password: "ignite",
database: "rentx",
synchronize: true,
logging: true,
entities: [
"./src/database/entities/*.ts"
],
migrations: [
"./src/database/migrations/*.ts"
],
});
appDataSource.initialize()
.then(() => {
console.log("Connected to database!");
})
.catch((error) => {
console.error("Connected fail!", error);
});
Para criar a migration de categoria, precisamos executar o comando abaixo.
yarn typeorm migration:create ./src/database/migrations/CreateCategories