Pourquoi Deployer NestJS sur GCP ?
Deployer un application NestJS sur Google Cloud Platform (GCP) offre de nombreux avantages, notamment :
- Scalabilité : GCP permet une mise à l'échelle facile et efficace de vos applications grâce à ses services d'hébergement à la demande.
- Haute disponibilité : Utiliser GCP vous offre des options pour assurer un haut niveau de disponibilité, en particulier avec le Cloud Run qui fournit une architecture serverless.
- Fonctionnalités intégrées : GCP propose une série d'outils et de services qui peuvent être facilement intégrés à votre application NestJS, comme Firebase pour les notifications push ou Google Cloud Pub/Sub pour la communication entre microservices.
Un cas d'utilisation concret pourrait être le développement d'une API backend pour une application mobile. En déployant cette API sur GCP, vous pouvez tirer parti des capacités de mise à l'échelle et de haute disponibilité pour assurer un bon fonctionnement même sous charge élevée.
Prerequis
- Connaissance approfondie de NestJS
- Compréhension du concept d'architecture microservices
- Familiarité avec les services GCP (Cloud Functions, Cloud Run, Cloud Storage)
- Node.js et npm installés sur votre machine
Concepts fondamentaux
1. Cloud Functions
Cloud Functions est un service serverless qui permet de créer des fonctions qui s'exécutent en réponse à des événements.
Schéma mental :
+---------------------+
| |
| Appel d'API |
| |
+---------+-----------+
|
v
+---------v-----------+
| |
| Cloud Functions |
| |
+---------+-----------+
|
v
+---------v-----------+
| |
| Logique de la |
| fonction |
| |
+---------------------+
Code fonctionnel :
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get('hello')
getHello(): string {
return 'Hello World!';
}
}
2. Cloud Run
Cloud Run est un service serverless qui permet de déployer et d'exécuter des applications en conteneur.
Schéma mental :
+---------------------+
| |
| Application |
| |
+---------+-----------+
|
v
+---------v-----------+
| |
| Dockerfile |
| |
+---------+-----------+
|
v
+---------v-----------+
| |
| Cloud Run |
| |
+---------+-----------+
|
v
+---------v-----------+
| |
| Exécution |
| |
+---------------------+
Code fonctionnel :
## Dockerfile
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD ["node", "dist/main"]
Mise en pratique : projet fil rouge
Mini-projet : API de blog
Nous allons créer une simple API de blog avec NestJS et déployer sur GCP.
- Initialisation du projet :
## Création d'un nouveau projet NestJS
nest new blog-api
## Accès au dossier du projet
cd blog-api
- Création des entités et services :
## src/blog/blog.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Blog {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
content: string;
}
typescript
## src/blog/blog.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Blog } from './blog.entity';
@Injectable()
export class BlogService {
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
) {}
findAll(): Promise<Blog[]> {
return this.blogRepository.find();
}
findOne(id: number): Promise<Blog> {
return this.blogRepository.findOne(id);
}
async create(blog: Blog): Promise<Blog> {
const newBlog = this.blogRepository.create(blog);
return this.blogRepository.save(newBlog);
}
async update(id: number, blog: Blog): Promise<void> {
await this.blogRepository.update(id, blog);
}
async remove(id: number): Promise<void> {
await this.blogRepository.delete(id);
}
}
- Création des contrôleurs :
## src/blog/blog.controller.ts
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { BlogService } from './blog.service';
import { Blog } from './blog.entity';
@Controller('blogs')
export class BlogController {
constructor(private readonly blogService: BlogService) {}
@Get()
findAll(): Promise<Blog[]> {
return this.blogService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string): Promise<Blog> {
return this.blogService.findOne(+id);
}
@Post()
create(@Body() blog: Blog): Promise<Blog> {
return this.blogService.create(blog);
}
@Put(':id')
update(@Param('id') id: string, @Body() blog: Blog): Promise<void> {
return this.blogService.update(+id, blog);
}
@Delete(':id')
remove(@Param('id') id: string): Promise<void> {
return this.blogService.remove(+id);
}
}
- Configuration de la base de données :
## src/typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeORMConfig: TypeOrmModuleOptions = {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'your_username',
password: 'your_password',
database: 'blog_db',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
- Mise à jour du fichier
app.module.ts:
## src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BlogModule } from './blog/blog.module';
@Module({
imports: [
TypeOrmModule.forRoot(typeORMConfig),
BlogModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- Création du module
Blog:
## src/blog/blog.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BlogService } from './blog.service';
import { BlogController } from './blog.controller';
import { Blog } from './blog.entity';
@Module({
imports: [TypeOrmModule.forFeature([Blog])],
controllers: [BlogController],
providers: [BlogService],
})
export class BlogModule {}
- Démarrage de l'application :
## Lancer le serveur local
npm run start
## Vérifier que l'API est fonctionnelle en accédant à http://localhost:3000/blogs
Déploiement sur Cloud Run :
Construire l'image Docker :
gcloud builds submit --tag gcr.io/your-project-id/blog-api
- Déployer sur Cloud Run :
gcloud run deploy blog-api --image gcr.io/your-project-id/blog-api --platform managed
- Accéder à l'URL fournie pour tester votre API.
Erreurs frequentes et debugging
1. Error: listen EACCES: permission denied, port 8080
Code incorrect :
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(8080);
}
bootstrap();
Code correct :
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 8080);
}
bootstrap();
2. Error: cannot find module 'typeorm'
Code incorrect :
// src/blog/blog.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Blog } from './blog.entity';
@Injectable()
export class BlogService {
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
) {}
// Méthodes de service
}
Code correct :
// src/blog/blog.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Blog } from './blog.entity';
@Injectable()
export class BlogService {
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
) {}
// Méthodes de service
}
3. Error: connection refused
Code incorrect :
// src/typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeORMConfig: TypeOrmModuleOptions = {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'your_username',
password: 'your_password',
database: 'blog_db',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
Code correct :
// src/typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeORMConfig: TypeOrmModuleOptions = {
type: 'mysql',
host: 'your-mysql-host',
port: 3306,
username: 'your_username',
password: 'your_password',
database: 'blog_db',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
Pour aller plus loin
1. Authentification et autorisation avec Firebase Authentication
Intégrer Firebase Authentication
2. Utilisation de Cloud Pub/Sub pour la communication entre services
3. Optimisation des performances avec Cloud CDN
Défi pratique
Créez un nouveau service dans votre application NestJS qui utilise Firebase Firestore pour stocker et récupérer des données utilisateur. Déployez ensuite ce service sur Cloud Run.
Ressources :