5 Técnicas do NestJS para aplicativos eficientes e de fácil manutenção
NestJS é uma estrutura poderosa e altamente evolutiva, embora complexa. Com muitos recursos prontos para uso, é uma excelente escolha para desenvolvedores que desejam criar aplicativos escaláveis e robustos. No entanto, alguns de seus recursos menos conhecidos e subutilizados são verdadeiras joias escondidas. Neste artigo, apresentarei cinco dessas joias, que podem ser extremamente úteis em contextos específicos.
Controle de versão
O controle de versão é um aspecto essencial do desenvolvimento de APIs da Web, permitindo que diferentes versões da API coexistam e sejam acessíveis de forma independente. Antes do lançamento do NestJS 8, não havia suporte pronto para uso para o controle de versão de APIs, o que significava que os desenvolvedores precisavam implementá-lo manualmente.
Felizmente, o NestJS 8 introduziu suporte para três tipos diferentes de controle de versão de API: controle de versão de URI, versionamento de cabeçalho e controle de versão do tipo de mídia. O controle de versão de URI é o padrão e envolve a inclusão da versão na URI da solicitação.
Para habilitar o controle de versão de URI, basta seguir os seguintes passos:
app.enableVersioning({
type: VersioningType.URI,
});
Para aplicar o controle de versão de URL ao controlador:
@Controller({
version: '1',
})
export class BMWControllerV1 { ... }
Ou você pode aplicá-lo a rotas individuais
@Version('2')
@Get('bmw')
findAllV2(): string {
return 'This action returns all bmw for version 2';
}
Outro recurso útil é o chamado "Versionamento Neutro", que permite definir um controlador como neutro em termos de versão. Com essa abordagem, um único controlador pode lidar com várias versões da API, simplificando assim a manutenção e evolução da aplicação.
@Controller({
version: VERSION_NEUTRAL,
})
export class BMWController { ... }
Uma das vantagens do novo recurso de versionamento no NestJS é a sua simplicidade e flexibilidade. Por exemplo, se uma solicitação de entrada não contiver uma versão, ela será automaticamente mapeada para o controlador com a versão neutra VERSION_NEUTRAL
. Isso torna a implementação e a manutenção de APIs mais intuitivas e eficientes.
Em resumo, essa é apenas mais uma das muitas boas razões para escolher o NestJS como estrutura para desenvolvimento de aplicativos.
Usar cache na memória com um decorador personalizado
Uau! Você sabia que o gerenciamento de cache pode ser um dos maiores desafios em Ciência da Computação? Mas não se preocupe, o NestJS torna tudo mais fácil com um gerenciador de cache pronto para uso que vem com um armazenamento de dados na memória padrão.
E o melhor de tudo? O uso dessa ferramenta é simples e descomplicado. Tudo o que você precisa fazer é importar o módulo de cache CacheModule
e configurar as opções de cache para a sua aplicação. Com essa funcionalidade integrada, você pode melhorar significativamente o desempenho da sua API e aliviar a carga do servidor.
Não é incrível? O NestJS realmente pensou em tudo para tornar a sua experiência de desenvolvimento mais fácil e eficiente.
imports: [CacheModule.register()]
Em seguida, podemos injetá-lo em uma class usando o token e começar a usá-lo.CACHE_MANAGER
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
// Para recuperar itens do cache
await this.cacheManager.get('key');
// Para adicionar um item ao cache
await this.cacheManager.set('key', 'value');
Para implementar o cache na API NestJS, o código geralmente é como o seguinte.
const value = await this.cacheManager.get('test-key-bmw');
if (!value) {
const response = await this.getHello();
this.cacheManager.set(
'test-key-bmw',
response,
{ ttl: 0 },
);
return response;
}
return value;
Quando o seu aplicativo começa a crescer, é comum que o código comece a se repetir em diversos lugares, o que pode tornar o desenvolvimento e a manutenção cada vez mais difíceis.
Mas como podemos evitar essa duplicação de código? A resposta é um decorador personalizado!
O decorador é uma função que pode estender o comportamento de outra função sem modificá-la diretamente. No caso do gerenciador de cache, para criar um decorador personalizado, precisamos injetar o gerenciador de cache na função.
Felizmente, graças ao poderoso NestJS DI, a criação de um decorador personalizado é relativamente simples. Abaixo está um exemplo de implementação de um decorador personalizado que utiliza o gerenciador de cache na memória.
Com essa funcionalidade, você pode criar um gerenciamento de cache mais eficiente e organizado, evitando a repetição de código e tornando a manutenção da sua aplicação muito mais fácil.
import { CACHE_MANAGER, Inject } from '@nestjs/common';
export const cacheManagerDecorator = (ttl = 10) => {
const injectCache = Inject(CACHE_MANAGER);
return function (
target: any,
_propertyName: string,
descriptor: PropertyDescriptor,
) {
injectCache(target, 'cache');
const decoratedMethod = descriptor.value;
const cacheKey = `${target.constructor.name}-${decoratedMethod?.name}`;
descriptor.value = async function () {
const cachedData = await this.cache.get(cacheKey);
if (cachedData) {
console.log('cachedData:', cachedData);
return cachedData;
}
const response = await decoratedMethod.apply(this, arguments);
this.cache.set(cacheKey, response, { ttl: ttl });
console.log('response:', response);
return response;
};
};
};
Para entender melhor a implementação do decorador personalizado para o gerenciador de cache no NestJS, é importante compreender a essência da solução.
O target
é a classe que contém o decorador, enquanto o descriptor
é a referência ao método da classe que está sendo decorado. Na linha 12, por exemplo, extraímos o nome do construtor de destino e o nome do método para formar um 'cacheKey' exclusivo.
O ttl
representa a duração para invalidar o cache e, por padrão, é definido como 10 segundos.
Vale lembrar que, nas linhas 4 e 10 do código, o uso do decorador @Inject
no construtor é equivalente.
Essa solução permite que você tenha um gerenciamento de cache mais eficiente e organizado, evitando repetição de código e simplificando a manutenção da sua aplicação. Com o poder do NestJS, é possível criar soluções robustas e escaláveis em pouco tempo e com um mínimo de esforço.
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
O uso do decorador é tão simples quanto:
@cacheManagerDecorator()
async getHello() {
return `Hello - ${new Date().toLocaleString()}`;
}
É muito mais limpo agora, e podemos aplicá-lo a qualquer serviço que precise ser armazenado em cache.
Trabalho NestJS Cron
Embora o NestJS seja projetado principalmente para a API REST, ele também vem com um pacote de agendamento. O pacote expõe uma API que nos permite criar trabalhos cron.
Para começar com o trabalho cron do NestJS, precisamos instalar o pacote primeiro
npm install --save @nestjs/schedule
npm install --save-dev @types/cron
Você pode criar um trabalho cron de duas maneiras diferentes:
criar o trabalho cron de forma declarativa.
O decorador suporta o padrão cron.@Cron
@Cron('45 * * * * *')
handleCron() {
this.logger.debug('Called when the current second is 45');
}
O método será chamado a cada minuto, no 45º segundo. Você também pode usar a expressão cron predefinida para configurar o trabalho.handleCron
@Cron(CronExpression.EVERY_MINUTE)
crie dinamicamente um novo trabalho cron.
Usando o objeto do pacote, você pode criar o trabalho cron. Em seguida, você pode usar o método para adicionar o trabalho ao registro de CronJobcronSchedulerRegistry.addCronJob()
addCronJob(name: string, seconds: string) {
const job = new CronJob(`${seconds} * * * * *`, () => {
this.logger.warn(`time (${seconds}) for job ${name} to run!`);
});
this.schedulerRegistry.addCronJob(name, job);
job.start();
Para descobrir outras opções e recursos disponíveis no pacote de agendamento de tarefas do NestJS, confira a documentação oficial.
Validar entrada com pipes
A validação de dados é essencial em qualquer API da web. No NestJS, podemos usar pipes para executar a validação em tempo de execução. Quando um pipe é usado para validação, os dados serão retornados inalterados se a validação for bem-sucedida, caso contrário, um erro será lançado se os dados estiverem incorretos.
O NestJS possui pipes prontos para uso integrados, incluindo o ValidationPipe. O ValidationPipe interno é baseado no popular pacote de validação de classe. Veja um exemplo de uso abaixo:
import { IsString, IsInt } from 'class-validator';
export class CreateBMWDto {
@IsString()
name: string;
@Length(10)
description: string;
}
@IsString()
: verifica se um valor de entrada é uma string.
@Length(10)
: verifica se o valor tem um comprimento mínimo de 10 caracteres. Ele também
retornará um erro se o valor não for uma string.
Quando uma solicitação é recebida com uma propriedade inválida no corpo da solicitação, o aplicativo responderá automaticamente com um status de resposta 400 Bad Request.
No exemplo acima, a validação é aplicada com decoradores. O mesmo decorador pode ser usado em diferentes classes DTO em todo o aplicativo NestJS, facilitando a manutenção da lógica de validação.
Há muitos outros decoradores disponíveis no tubo de validação embutido. Você também pode criar seu próprio tubo de validação personalizado usando os pacotes class-transformer e class-validator.
Os tubos no NestJS são flexíveis e poderosos. Eles podem ser síncronos e assíncronos e podem ser aplicados em diferentes níveis de escopo: parâmetro, método, controlador ou global.
Para obter mais detalhes sobre como usar os pipes NestJS para validação, você pode conferir a documentação oficial.
Melhore o desempenho alternando a plataforma subjacente
Por padrão, o NestJS é executado em cima do Express. Comparado a outras estruturas de API, seu desempenho pode não ser o melhor. Para a maioria dos aplicativos, a diferença de desempenho não será notável.
No entanto, se você estiver trabalhando em um aplicativo onde o desempenho rápido é uma prioridade, pode ser vantajoso migrar seu aplicativo NestJS para o Fastify. O Fastify é aproximadamente duas vezes mais rápido que o Express.
Felizmente, a migração do Express para o Fastify é relativamente simples, graças à independência da estrutura fornecida pelo NestJS por meio de padrões de adaptador. Em outras palavras, o design do NestJS torna o Express e o Fastify intercambiáveis.
Para usar o Fastify, basta instalar o pacote
yarn add @nestjs/platform-fastify
e configurar o aplicativo para usá-lo. A documentação oficial do NestJS fornece detalhes sobre como migrar para o Fastify e aproveitar ao máximo sua velocidade aprimorada.
Em seguida, podemos configurar a plataforma Fastify em main.ts.
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.listen(3000);
}
Agora que o Fastify foi instalado, podemos usá-lo como o provedor HTTP no NestJS. É importante notar que, como o Fastify é uma estrutura diferente do Express, os pacotes que dependem do Express precisarão ser substituídos por pacotes equivalentes do Fastify. É possível encontrar muitos pacotes de middleware do Fastify disponíveis na web. Alguns pacotes comuns usados no NestJS são o fastify-cors
e o fastify-helmet
. No entanto, é importante ler a documentação de cada pacote cuidadosamente antes de usá-lo, para garantir que seja compatível com o NestJS e o Fastify.