Docker e TypeScript: conteinerização de aplicações do A ao Z
A conteinerização tornou-se o padrão do desenvolvimento moderno. O Docker permite empacotar uma aplicação TypeScript junto com todas as suas dependências e ambiente, garantindo seu funcionamento estável em qualquer plataforma — desde o computador local do desenvolvedor até o servidor de produção. Neste artigo, abordaremos o ciclo completo: desde o Dockerfile básico até a build de produção otimizada.
1. Fundamentos do Docker para desenvolvedores TypeScript
Docker é uma plataforma para criar, distribuir e executar aplicações em ambientes isolados (contêineres). Para aplicações TypeScript, que são compiladas para JavaScript e executadas no Node.js, o Docker resolve três tarefas principais:
- Reprodutibilidade do ambiente: esqueça a frase "funciona na minha máquina". As versões do Node.js, npm e pacotes do sistema são fixadas na imagem.
- Isolamento: cada aplicação é executada em seu próprio contêiner, sem afetar outros serviços.
- Escalabilidade: contêineres são facilmente replicados e orquestrados.
Antes de começar, certifique-se de ter o Docker e o Docker Compose instalados. Você pode verificar com os comandos:
docker --versiondocker-compose --version2. Criando um Dockerfile para uma aplicação TypeScript
Vamos começar com uma aplicação TypeScript simples. Suponha que temos o arquivo src/index.ts:
import express from 'express';
const app = express();const port = process.env.PORT || 3000;
app.get('/', (req, res) => { res.send('Hello from Docker + TypeScript!');});
app.listen(port, () => { console.log(`Server started on port ${port}`);});Agora, crie o Dockerfile na raiz do projeto:
# ---- Imagem base para desenvolvimento ----FROM node:18-alpine AS baseWORKDIR /appCOPY package*.json ./RUN npm ci --only=production
# ---- Build do TypeScript ----FROM base AS buildCOPY tsconfig.json ./COPY src/ ./src/RUN npm ci && npm run build
# ---- Imagem de Produção ----FROM node:18-alpine AS productionWORKDIR /appCOPY --from=build /app/dist ./distCOPY --from=base /app/node_modules ./node_modulesEXPOSE 3000CMD ["node", "dist/index.js"]Este é um build multi-estágio (multi-stage build). Ele permite:
- Instalar todas as dependências (incluindo devDependencies) apenas na etapa de build
- Apenas o JavaScript compilado e as dependências de produção vão para a imagem final
- O tamanho da imagem é reduzido em 2 a 3 vezes
Construa a imagem e execute o contêiner:
docker build -t my-typescript-app .docker run -p 3000:3000 my-typescript-app3. Configurando docker-compose para desenvolvimento e produção
O Docker Compose simplifica o trabalho com aplicações multi-contêiner. Vamos criar o docker-compose.yml:
version: '3.8'
services: app: build: context: . target: development ports: - "3000:3000" volumes: - .:/app - /app/node_modules environment: - NODE_ENV=development command: npm run dev
# Versão de produção app-prod: build: context: . target: production ports: - "80:3000" environment: - NODE_ENV=productionPara conveniência, adicione um Dockerfile separado para desenvolvimento (você pode estender a imagem base):
# Dockerfile.devFROM node:18-alpineWORKDIR /appCOPY package*.json ./RUN npm installCOPY . .EXPOSE 3000CMD ["npm", "run", "dev"]Executando em modo de desenvolvimento:
docker-compose up appGraças aos volumes, as alterações no código são capturadas imediatamente (hot-reload), e /app/node_modules é excluído para não sobrescrever as dependências do contêiner.