Introduction : pourquoi conteneuriser les applications TypeScript ?
Le développement logiciel moderne est impensable sans la conteneurisation. Docker est devenu le standard de facto pour empaqueter, livrer et exécuter des applications dans des environnements isolés. Combiné à TypeScript — un sur-ensemble de JavaScript strictement typé — Docker assure la prévisibilité, la portabilité et simplifie les processus CI/CD.
Dans cet article, nous allons voir comment conteneuriser correctement une application TypeScript : du Dockerfile de base à une build prête pour la production en utilisant docker-compose et la construction multi-étapes. Vous apprendrez à éviter les erreurs typiques, à réduire la taille de l'image et à accélérer le déploiement.
Les bases : TypeScript et Node.js dans Docker
TypeScript est un langage qui se compile en JavaScript. Cela signifie que pour exécuter une application TypeScript dans un conteneur, nous devons d'abord compiler le code source, puis exécuter le JavaScript obtenu avec Node.js. Docker est idéal pour automatiser ce processus.
Création d'une application TypeScript simple
Pour commencer, créons une application minimale en TypeScript. Structure du projet :
my-ts-app/├── src/│ └── index.ts├── package.json├── tsconfig.json└── Dockerfilesrc/index.ts :
import express, { Request, Response } from 'express';
const app = express();const PORT = process.env.PORT || 3000;
app.get('/', (req: Request, res: Response) => { res.json({ message: 'Hello from TypeScript + Docker!' });});
app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`);});package.json :
{ "name": "my-ts-app", "version": "1.0.0", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "ts-node src/index.ts" }, "dependencies": { "express": "^4.18.2" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.11.0", "typescript": "^5.3.3", "ts-node": "^10.9.2" }}tsconfig.json :
{ "compilerOptions": { "target": "ES2020", "module": "commonjs", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"]}Dockerfile de base
Première version, la plus simple du Dockerfile :
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]Ce Dockerfile copie tout le projet, installe les dépendances, compile TypeScript et lance l'application. Cependant, cette approche présente des inconvénients : la taille de l'image est grande et la mise en cache des couches est inefficace.
Optimisation : construction multi-étapes et réduction de taille
Pour un environnement de production, il est recommandé d'utiliser une construction multi-étapes (multi-stage build). Cela permet de séparer les étapes de build et d'exécution, en ne conservant dans l'image finale que les fichiers nécessaires.
Dockerfile de production avec construction multi-étapes
# Stage 1: BuildFROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./RUN npm ci --only=production
COPY . .RUN npm run build
# Stage 2: ProductionFROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/package.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]Avantages de cette approche :
- Taille d'image plus petite — la couche finale ne contient que le code compilé et les dépendances de production.
- Sécurité — le code source TypeScript ne reste pas dans le conteneur, dev