Sitemap

Integrando o Padrão Singleton no NestJS com OCI Vault: Um Guia Detalhado

4 min readApr 27, 2023
Press enter or click to view image in full size

Introdução

O NestJS é um framework Node.js avançado que facilita a criação de aplicações modulares. Um padrão muito usado é o Singleton, ideal para gerenciar dependências únicas como serviços de configuração. Neste artigo, mostraremos como aplicar o padrão Singleton no NestJS e integrá-lo ao OCI Vault, o serviço da Oracle Cloud Infrastructure para gerenciamento seguro de segredos e chaves criptográficas.

O que é o Singleton?

O Singleton é um padrão de projeto que garante que uma classe tenha apenas uma instância única e fornece um ponto de acesso global a essa instância. Isso é útil quando precisamos de um recurso compartilhado em todo o aplicativo, como um gerenciador de configurações ou uma conexão com o banco de dados.

Os principais elementos de um Singleton incluem:

  • Um construtor privado: Isso garante que a classe não possa ser instanciada de fora.
  • Uma variável estática para armazenar a instância única: Ela armazena a instância da classe e garante que haja apenas uma instância.
  • Um método estático para obter a instância única: Este método é usado para acessar a instância única, criando-a se ainda não existir.

Passo 1 - Implementando o Singleton no NestJS

Nesta seção, mostraremos como implementar um Singleton no NestJS. Para isso, criaremos um serviço de gerenciamento de configurações responsável por carregar as configurações do aplicativo e fornecer acesso a elas. O processo envolve as seguintes etapas:

nest generate service configuration

Passo 2 - Implemente a classe ConfigurationService como um Singleton:

// configuration.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class ConfigurationService {
private static instance: ConfigurationService;
private readonly config: Record<string, any>;

private constructor() {
this.config = {
// Carregue suas configurações aqui
};
}

public static getInstance(): ConfigurationService {
if (!ConfigurationService.instance) {
ConfigurationService.instance = new ConfigurationService();
}

return ConfigurationService.instance;
}

public getConfig(key: string): any {
return this.config[key];
}
}

Passo 3 - Use o Singleton no seu aplicativo:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigurationService } from './configuration/configuration.service';

@Module({
providers: [
{
provide: ConfigurationService,
useValue: ConfigurationService.getInstance(),
},
],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigurationService } from './configuration/configuration.service';

@Controller()
export class AppController {
constructor(private readonly configService: ConfigurationService) {}

@Get('config')
getConfig(): Record<string, any> {
return this.configService.getConfig('exampleConfig');
}
}

Passo 4 - Integrando o OCI Vault

O OCI Vault permite o armazenamento seguro de segredos como tokens e senhas. Para acessá-lo via Node.js, usaremos o SDK da OCI.

Pré-requisitos:

  • Um Vault configurado no OCI.
  • Uma Master Encryption Key no vault.
  • Uma Secret criada e ativa.
  • Permissões definidas via IAM policies.

Passo 5 — Instalando o SDK da OCI

npm install oci-sdk

Passo 6 - Criando o SecretClient da OCI:

  • Atualize o arquivo para a criação do SecretClient e recebê-lo como dependência:
// configuration/oci-secret-client.provider.ts
import { Provider } from '@nestjs/common';
import * as oci from 'oci-sdk';

export const OciSecretClientProvider: Provider = {
provide: 'OCI_SECRET_CLIENT',
useFactory: () => {
const provider = new oci.Common.ConfigFileAuthenticationDetailsProvider();
const secretsClient = new oci.vault.VaultsClient({
authenticationDetailsProvider: provider,
});

return secretsClient;
},
};

Aqui, definimos um provider para o SecretClient, que será usado para criar a instância do SecretClient com as configurações corretas.

Passo 7 - Atualize o ConfigurationService para usar o OCI Vault:

// configuration.service.ts
import { Injectable } from '@nestjs/common';
import { VaultsClient } from 'oci-sdk/lib/vault';

@Injectable()
export class ConfigurationService {
private static instance: ConfigurationService;

private constructor(private readonly secretsClient: VaultsClient) {}

public static getInstance(secretsClient: VaultsClient): ConfigurationService {
if (!ConfigurationService.instance) {
ConfigurationService.instance = new ConfigurationService(secretsClient);
}
return ConfigurationService.instance;
}

public async getConfig(secretId: string): Promise<string | null> {
try {
const response = await this.secretsClient.getSecretBundle({
secretId,
});

const secretContent = response.secretBundle.secretBundleContent as any;
const base64Decoded = Buffer.from(secretContent.content, 'base64').toString('utf-8');

return base64Decoded;
} catch (error) {
console.error(`Erro ao buscar segredo ${secretId}: ${error.message}`);
return null;
}
}
}

Passo 8 - Atualize o AppModule:

Atualize o arquivo app.module.ts para incluir o SecretClientProvider e passar o SecretClient para a função getInstance() da ConfigurationService:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigurationService } from './configuration/configuration.service';
import { OciSecretClientProvider } from './configuration/oci-secret-client.provider';
import { VaultsClient } from 'oci-sdk/lib/vault';

@Module({
providers: [
OciSecretClientProvider,
{
provide: ConfigurationService,
useFactory: (vaultsClient: VaultsClient) =>
ConfigurationService.getInstance(vaultsClient),
inject: ['OCI_SECRET_CLIENT'],
},
],
})
export class AppModule {}

Boas práticas e considerações

Ao trabalhar com o padrão Singleton e o NestJS, é importante ter em mente as seguintes boas práticas e considerações:

  • Use Singleton somente quando necessário.
  • Prefira injeção de dependência via Providers para manter testabilidade.
  • Utilize variáveis de ambiente para armazenar OCIDs e dados sensíveis.
  • Garanta políticas corretas no OCI IAM (para leitura de Vaults e Secrets).
  • Evite deixar segredos hardcoded no código.

Conclusão

Neste artigo, discutimos em detalhes como aplicar o padrão Singleton no NestJS e integrá-lo ao OCI Vault. Ao ajustar nossa implementação para usar providers e seguir boas práticas, criamos um aplicativo modular e seguro.

--

--

Cláudio Rapôso
Cláudio Rapôso

Written by Cláudio Rapôso

Microsoft MVP | Software Architect | Teacher | Book Author | MCT | 12x Microsoft Certified Connect with me in https://www.linkedin.com/in/cfraposo

No responses yet