Features Avançadas
1. Cron Jobs (Tarefas Agendadas)
Execute tarefas em intervalos regulares com sincronização via Redis/RedLock.
Criar um Cron Job
// src/application/person/create-person-job/create-person-job.ts
import { Injectable } from '@nestjs/common'
import {
CronJobHandlerBase,
CronJobResponse,
CronJobSettings,
} from '@koalarx/nest/core/backgroud-services/cron-service/cron-job.handler.base'
import { EventQueue } from '@koalarx/nest/core/backgroud-services/event-service/event-queue'
import { ok } from '@koalarx/nest/core/request-overflow/request-result'
import { ILoggingService } from '@koalarx/nest/services/logging/ilogging.service'
import { IRedLockService } from '@koalarx/nest/services/redlock/ired-lock.service'
import { Injectable } from '@nestjs/common'
import { CreatePersonHandler } from '../create/create-person.handler'
import { InactivePersonEvent } from '../events/inactive-person/inactive-person-event'
import { IPersonRepository } from '@/domain/repositories/iperson.repository'
@Injectable()
export class CreatePersonJob extends CronJobHandlerBase {
constructor(
redlockService: IRedLockService,
loggingService: ILoggingService,
private readonly createPerson: CreatePersonHandler,
private readonly repository: IPersonRepository,
) {
super(redlockService, loggingService)
}
// Configurações do job (intervalo de execução)
protected async settings(): Promise<CronJobSettings> {
return {
isActive: true,
timeInMinutes: 1, // Executa a cada 1 minuto (use 1440 para 24 horas)
}
}
// Lógica principal do job
protected async run(): Promise<CronJobResponse> {
const result = await this.createPerson.handle({
name: 'John Doe',
phones: [{ phone: '22999999999' }],
address: { address: 'Street 1' },
})
if (result.isOk()) {
const person = await this.repository.read(result.value.id)
if (person) {
// Emitir eventos para handlers processar
const jobs = new PersonEventJob()
jobs.addEvent(new InactivePersonEvent())
EventQueue.dispatchEventsForAggregate(jobs._id)
}
console.log('Person created with id:', result.value.id)
} else {
console.error('Error creating person:', result.value)
}
// Sempre retorna ok(null) - erros são logados, não falham o job
return ok(null)
}
}Registrar Cron Job
1. No AppModule, passe os jobs via cronJobs:
// src/host/app.module.ts
import { CreatePersonJob } from '@/application/person/create-person-job/create-person-job'
import { DeleteInactiveJob } from '@/application/person/delete-inative-job/delete-inactive-job'
import { env } from '@/core/env'
import { KoalaNestModule } from '@koalarx/nest/core/koala-nest.module'
import { Module } from '@nestjs/common'
import { PersonModule } from './controllers/person/person.module'
@Module({
imports: [
KoalaNestModule.register({
env,
controllers: [PersonModule],
cronJobs: [DeleteInactiveJob, CreatePersonJob], // Registrar jobs
}),
],
})
export class AppModule {}2. No arquivo main.ts, adicione o job usando .addCronJob():
// src/main.ts
import { CreatePersonJob } from '@/application/person/create-person-job/create-person-job'
import { DeleteInactiveJob } from '@/application/person/delete-inative-job/delete-inactive-job'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await new KoalaApp(app)
.useDoc({
ui: 'scalar',
endpoint: '/doc',
title: 'API de Demonstração',
version: '1.0',
})
.addCronJob(CreatePersonJob) // Registrar primeiro job
.addCronJob(DeleteInactiveJob) // Registrar segundo job
.enableCors()
.buildAndServe()
}
bootstrap()Nota sobre RedLock: Os Cron Jobs utilizam IRedLockService (que depende de Redis) para garantir que apenas uma instância execute o job simultaneamente em ambientes distribuídos. Certifique-se de configurar a REDIS_URL no arquivo .env para ambientes com múltiplas instâncias.
2. Event Jobs (Handlers de Eventos)
Processe eventos de forma assincronizada usando o padrão EventJob com handlers especializados.
Criar uma Classe de Evento
Eventos devem estender EventClass:
// src/application/person/events/inactive-person/inactive-person-event.ts
import { EventClass } from '@koalarx/nest/core/backgroud-services/event-service/event-class'
export class InactivePersonEvent extends EventClass {}Criar um Event Handler
Handler estende EventHandlerBase e processa eventos:
// src/application/person/events/inactive-person/inactive-person-handler.ts
import { ReadManyPersonDto } from '@/domain/dtos/read-many-person.dto'
import { IPersonRepository } from '@/domain/repositories/iperson.repository'
import { EventHandlerBase } from '@koalarx/nest/core/backgroud-services/event-service/event-handler.base'
import { Injectable } from '@nestjs/common'
import { InactivePersonEvent } from './inactive-person-event'
@Injectable()
export class InactivePersonHandler extends EventHandlerBase {
constructor(private readonly repository: IPersonRepository) {
super(InactivePersonEvent) // Especifica qual evento processa
}
async handleEvent(): Promise<void> {
// handleEvent() é chamado quando eventos estão na fila
const result = await this.repository.readMany(
new ReadManyPersonDto({ active: true }),
)
for (const person of result.items) {
person.active = false
await this.repository.save(person)
}
console.log(
'InactivePersonHandler: Registros ativos inativados com sucesso!',
)
}
}Criar EventJob para Agrupar Handlers
A classe EventJob agrupa handlers relacionados a uma entidade:
// src/application/person/events/person-event-job.ts
import { Person } from '@/domain/entities/person/person'
import { EventHandlerBase } from '@koalarx/nest/core/backgroud-services/event-service/event-handler.base'
import { EventJob } from '@koalarx/nest/core/backgroud-services/event-service/event-job'
import { Type } from '@nestjs/common'
import { InactivePersonHandler } from './inactive-person/inactive-person-handler'
export class PersonEventJob extends EventJob<Person> {
defineHandlers(): Type<EventHandlerBase>[] {
return [InactivePersonHandler]
}
}Registrar Event Handler
1. No AppModule, passe os handlers via eventJobs:
// src/host/app.module.ts
import { PersonEventJob } from '@/application/person/events/person-event-job'
import { env } from '@/core/env'
import { KoalaNestModule } from '@koalarx/nest/core/koala-nest.module'
import { Module } from '@nestjs/common'
import { PersonModule } from './controllers/person/person.module'
@Module({
imports: [
KoalaNestModule.register({
env,
controllers: [PersonModule],
eventJobs: [PersonEventJob], // Registrar a EventJob
}),
],
})
export class AppModule {}2. No arquivo main.ts, adicione o handler usando .addEventJob():
// src/main.ts
import { PersonEventJob } from '@/application/person/events/person-event-job'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await new KoalaApp(app)
.useDoc({
ui: 'scalar',
endpoint: '/doc',
title: 'API de Demonstração',
version: '1.0',
})
.addEventJob(PersonEventJob) // Registrar a EventJob
.setAppName('example')
.setInternalUserName('integration.bot')
.setDbTransactionContext(DbTransactionContext)
.enableCors()
.buildAndServe()
}
bootstrap()Resumo de Registro:
- Event Handlers são agrupados em uma EventJob e registrados em duas etapas:
- AppModule: Via
eventJobs: [PersonEventJob]emKoalaNestModule.register() - main.ts: Via
.addEventJob(PersonEventJob)emKoalaApp
- AppModule: Via
- A EventJob agrupa handlers por entidade (PersonEventJob agrupa InactivePersonHandler)
- Múltiplos handlers podem estar na mesma EventJob
Fluxo de Eventos
Cron Job executa
└─ Processa lógica
└─ Emite evento via EventQueue.dispatchEventsForAggregate()
└─ EventJob localiza handlers
└─ Event Handler processa evento
└─ handleEvent() é chamado
└─ Lógica de negócio executadaQuando Usar Events
- Background Sync: Use Event Handlers para processar eventos de forma assincronizada
- Async Escalável: EventQueue é sincronizado via Redis (RedLock) em ambientes distribuídos
- Agregados: Agrupe múltiplos handlers em uma EventJob para organizar processamento por entidade
3. Autenticação e Autorização
A biblioteca fornece o decorador @IsPublic() para marcar rotas públicas. Para proteger seus endpoints com Guards do NestJS usando Passport Strategies, você implementa de acordo com suas necessidades (JWT, API Key, OAuth, etc.).
Importante: Guards e Strategies são implementações específicas do seu projeto. A biblioteca não fornece Guards/Strategies prontas, pois estas são muito particulares de cada aplicação.
Onde Registrar Guards: Use o método
.addGlobalGuard()no builder doKoalaAppemmain.tspara registrar seus guards globalmente. Veja a seção "Registrar Guards Globalmente" mais abaixo.
Criar Estratégia JWT
Aqui está um exemplo de como implementar uma estratégia customizada para validar tokens JWT:
// src/host/security/strategies/jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common'
import { PassportStrategy } from '@nestjs/passport'
import { Strategy } from 'passport-custom'
import { jwtDecode } from 'jwt-decode'
import { IUserRepository } from '@/domain/repositories/iuser.repository'
export type DoneFn = (err: Error | null, user?: any) => void
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(private readonly userRepository: IUserRepository) {
super()
}
async validate(request: any, done: DoneFn) {
const token = request.headers?.authorization?.replace('Bearer ', '')
if (token) {
try {
const decodedToken = jwtDecode(token) as any
// Validar expiração
if (decodedToken.exp * 1000 < Date.now()) {
done(new UnauthorizedException('Token expirado'))
return
}
// Buscar usuário no banco
const user = await this.userRepository.findByEmail(decodedToken.email)
if (user) {
done(null, user)
} else {
done(new UnauthorizedException('Usuário não encontrado'))
}
} catch {
done(new UnauthorizedException('Token inválido'))
}
} else {
done(new UnauthorizedException('Token não fornecido'))
}
}
}Estratégia de Autenticação com API Key
Para suportar autenticação via API Key, estenda a classe base da biblioteca:
// src/host/security/strategies/api-key.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { ApiKeyStrategyBase } from '@koalarx/nest/core/security/strategies/api-key.strategy'
import { Request } from 'express'
export type DoneFn = (err: Error | null, user?: any) => void
@Injectable()
export class ApiKeyStrategy extends ApiKeyStrategyBase {
constructor(private readonly jwtService: JwtService) {
super({ header: 'x-api-key' })
}
async validate(apikey: string, done: DoneFn, request: Request) {
try {
// Validar API Key usando JWT com chave pública
const publicKey = process.env.JWT_PUBLIC_KEY
? Buffer.from(process.env.JWT_PUBLIC_KEY, 'base64')
: undefined
const user = await this.jwtService.verifyAsync(apikey, {
algorithms: ['RS256'],
publicKey,
})
done(null, user)
} catch {
done(new UnauthorizedException('API Key inválida ou expirada'))
}
}
}Criar Guard de Autenticação
Crie um guard que suporta múltiplas estratégias (JWT e API Key):
// src/host/security/guards/auth.guard.ts
import { Injectable } from '@nestjs/common'
import { ExecutionContext } from '@nestjs/common'
import { AuthGuard as NestAuthGuard } from '@nestjs/passport'
import { Reflector } from '@nestjs/core'
import { IS_PUBLIC_KEY } from '@koalarx/nest/decorators/is-public.decorator'
@Injectable()
export class AuthGuard extends NestAuthGuard(['jwt', 'api-key']) {
constructor(public readonly reflector: Reflector) {
super()
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(
IS_PUBLIC_KEY,
[context.getHandler(), context.getClass()],
)
const request = context.switchToHttp().getRequest()
// Se rota é pública e não há header de autenticação, permitir
if (
isPublic &&
!request.headers.authorization &&
!request.headers['x-api-key']
) {
return true
}
// Aplicar estratégia JWT ou API Key
const canActivate = super.canActivate(context)
if (typeof canActivate === 'boolean') {
return canActivate
}
return canActivate as Promise<boolean>
}
}Guard de Autorização (Perfis)
Para autorização por perfil de usuário:
// src/host/security/guards/profiles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { UserProfileEnum } from '@/domain/entities/user/enums/user-profile.enum'
@Injectable()
export class ProfilesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Obter perfis requeridos do metadata
const requiredProfiles = this.reflector.get<UserProfileEnum[]>(
'profiles',
context.getHandler(),
)
if (!requiredProfiles || requiredProfiles.length === 0) {
return true
}
const request = context.switchToHttp().getRequest()
const user = request.user
// Verificar se usuário possui um dos perfis requeridos
return requiredProfiles.includes(user?.profile)
}
}Registrar Estratégias e Guards
Crie um módulo de segurança com suporte a JWT e API Key:
// src/host/security/security.module.ts
import { Module } from '@nestjs/common'
import { PassportModule } from '@nestjs/passport'
import { JwtModule } from '@nestjs/jwt'
import { JwtStrategy } from './strategies/jwt.strategy'
import { ApiKeyStrategy } from './strategies/api-key.strategy'
import { InfraModule } from '@/infra/infra.module'
@Module({
imports: [
InfraModule,
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' },
}),
],
providers: [JwtStrategy, ApiKeyStrategy],
})
export class SecurityModule {}Registrar Guards Globalmente
A biblioteca fornece o método .addGlobalGuard() no builder do KoalaApp para registrar seus guards globalmente:
// src/host/main.ts
import { AuthGuard } from './security/guards/auth.guard'
import { ProfilesGuard } from './security/guards/profiles.guard'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await new KoalaApp(app)
.useDoc({...})
.addGlobalGuard(AuthGuard) // Guard de autenticação (JWT + API Key)
.addGlobalGuard(ProfilesGuard) // Guard de autorização
.buildAndServe()
}Onde Adicionar Guards: Use
.addGlobalGuard()noKoalaAppbuilder emmain.tspara registrar seus guards globalmente. A ordem importa - guards são executados na ordem que são adicionados.
Usar em Controllers
// src/host/controllers/person/person.controller.ts
import { IsPublic } from '@koalarx/nest/decorators/is-public.decorator'
@Controller('persons')
export class PersonController {
@Post('login')
@IsPublic()
async login(@Body() credentials: LoginDto) {
// Acesso público - sem autenticação
return { token: 'jwt-token' }
}
@Get()
async list() {
// Requer autenticação (JWT)
return { items: [] }
}
@Delete(':id')
async delete(@Param('id') id: number) {
// Requer autenticação
return { success: true }
}
}4. Documentação Swagger/Scalar
Configure a documentação automática da API usando Swagger ou Scalar.
Configuração Básica
No seu arquivo main.ts, use .useDoc():
// src/host/main.ts
await new KoalaApp(app)
.useDoc({
ui: 'scalar', // 'swagger' ou 'scalar'
endpoint: '/doc',
title: 'API de Demonstração',
version: '1.0',
})
.buildAndServe()Com Servidores Adicionais
.useDoc({
ui: 'scalar',
endpoint: '/doc',
title: 'My API',
version: '1.0.0',
servers: [
{
url: 'http://localhost:3000',
description: 'Local development',
},
{
url: 'https://api.example.com',
description: 'Production',
},
],
})Com Autenticação Bearer
.useDoc({
ui: 'scalar',
endpoint: '/doc',
title: 'My API',
version: '1.0.0',
authorizations: [
{
name: 'bearer',
config: {
type: 'http',
scheme: 'bearer',
},
},
],
})5. CORS (Cross-Origin Requests)
Habilite requisições cross-origin com um único método:
// src/host/main.ts
await new KoalaApp(app)
.enableCors()
.buildAndServe()Configuração padrão aplicada:
credentials: true- Permite cookiesorigin: true- Aceita qualquer origemoptionsSuccessStatus: 200- Status de sucesso para preflight
6. Validação com Zod
Valide dados automaticamente usando Zod integrado ao RequestValidatorBase.
Definir Schema Zod
// src/application/person/create/create-person.request.ts
import { z } from 'zod'
export const CreatePersonSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
phone: z.string().optional(),
active: z.boolean().default(true),
})
export type CreatePersonRequest = z.infer<typeof CreatePersonSchema>Usar em Validator
// src/application/person/create/create-person.validator.ts
import { Injectable } from '@nestjs/common'
import { RequestValidatorBase } from '@koalarx/nest/core/request-handler/request-validator.base'
import { CreatePersonSchema, CreatePersonRequest } from './create-person.request'
@Injectable()
export class CreatePersonValidator extends RequestValidatorBase<CreatePersonRequest> {
protected get schema() {
return CreatePersonSchema
}
}Erros de Validação
Erros Zod são capturados automaticamente pelo ZodErrorsFilter:
// src/host/main.ts - O filter já está registrado automaticamente
// Erros Zod retornam Status 400 com detalhes dos campos inválidos7. Redis (Sincronização de Background Services)
Redis é essencial para sincronizar CronJobs e EventHandlers em ambientes distribuídos usando RedLock.
Como Funciona
- RedLock: Distributed locking que garante apenas uma instância execute cada job
- Cron Jobs: Sincronizam via RedLock para execução única por intervalo
- Event Handlers: Usam Redis para coordenação entre instâncias
Configurar Redis
Defina em .env:
REDIS_URL=redis://localhost:6379Usar Redis para Cache (Opcional)
Além do controle de jobs, use Redis para cache:
// src/services/cache.service.ts
import { Injectable } from '@nestjs/common'
import { IRedisService } from '@koalarx/nest/services/redis/iredis.service'
@Injectable()
export class CacheService {
constructor(private readonly redisService: IRedisService) {}
async set(key: string, value: any, expiresIn: number = 3600): Promise<void> {
await this.redisService.set(key, JSON.stringify(value), 'EX', expiresIn)
}
async get<T>(key: string): Promise<T | null> {
const value = await this.redisService.get(key)
return value ? JSON.parse(value) : null
}
async delete(key: string): Promise<void> {
await this.redisService.del(key)
}
}Nota importante: Redis é essencial quando rodando múltiplas instâncias da aplicação. Sem Redis, CronJobs executarão em paralelo em todos os servidores!
8. Transações com Prisma
Execute múltiplas operações de banco em uma transação atômica através do repositório.
Implementar DbTransactionContext
// src/infra/database/db-transaction-context.ts
import { PrismaTransactionalClient } from '@koalarx/nest/core/database/prisma-transactional-client'
import { Prisma } from 'prisma/generated/client'
import { DefaultArgs } from '@prisma/client/runtime/library'
export class DbTransactionContext
extends PrismaTransactionalClient
implements PrismaClientWithCustomTransaction
{
get person(): Prisma.PersonDelegate<DefaultArgs> {
return this.transactionalClient.person
}
get personPhone(): Prisma.PersonPhoneDelegate<DefaultArgs> {
return this.transactionalClient.personPhone
}
get personAddress(): Prisma.PersonAddressDelegate<DefaultArgs> {
return this.transactionalClient.personAddress
}
}Injetar DbTransactionContext no Repositório
// src/infra/database/repositories/person.repository.ts
import { PRISMA_TOKEN } from '@koalarx/nest/core/koala-nest-database.module'
import { RepositoryBase } from '@koalarx/nest/core/database/repository.base'
import { Inject, Injectable } from '@nestjs/common'
import { DbTransactionContext } from '../db-transaction-context'
@Injectable()
export class PersonRepository
extends RepositoryBase<Person>
implements IPersonRepository
{
constructor(
@Inject(PRISMA_TOKEN)
prisma: DbTransactionContext,
) {
super({
modelName: Person,
context: prisma,
include: {
phones: true,
address: true,
},
})
}
// Os métodos herdados de RepositoryBase já usam transações automaticamente
async save(person: Person): Promise<CreatedRegistreWithIdResponse | null> {
return this.saveChanges(person) // Transação automática
}
read(id: number): Promise<Person | null> {
return this.findById(id)
}
}Registrar no KoalaApp
// src/host/main.ts
await new KoalaApp(app)
.setDbTransactionContext(DbTransactionContext)
.buildAndServe()As transações são executadas automaticamente pelo RepositoryBase quando você usa métodos como saveChanges(), remove() e outras operações de escrita. Múltiplas operações dentro do repositório são garantidas como atômicas.
9. Logging Customizado
A biblioteca possui um sistema de logging abstrato que por padrão escreve erros no console, mas permite customização para enviar logs para diferentes destinos (Azure Storage, CloudWatch, Sentry, etc).
Entender o Contrato de Logging
O contrato é definido em apps/koala-nest/src/services/logging/ilogging.service.ts:
export interface LoggingReportProps {
loggedUsername: string // Usuário que gerou o erro
packageName: string // Pacote/módulo onde ocorreu
error: Error // Objeto do erro
httpRequest?: {
method: string // GET, POST, PUT, DELETE, etc
endpoint: string // Path do endpoint
queryParams?: string // Query params da requisição
payload?: object // Body da requisição
statusCode: number // Status HTTP da resposta
response?: object // Response enviado
}
}
export abstract class ILoggingService {
abstract report(data: LoggingReportProps): Promise<void>
}Implementação Padrão (Console)
Por padrão, a biblioteca usa LoggingService que escreve no console:
// apps/koala-nest/src/services/logging/logging.service.ts
import { Injectable } from '@nestjs/common'
import { ILoggingService, LoggingReportProps } from './ilogging.service'
import consola from 'consola'
@Injectable()
export class LoggingService implements ILoggingService {
async report(data: LoggingReportProps): Promise<void> {
consola.error(data.error)
}
}Criar Implementação Customizada
Para enviar logs para um local diferente (ex: Azure Storage), crie uma nova classe implementando ILoggingService:
// src/infra/logging/azure-logging.service.ts
import { Injectable } from '@nestjs/common'
import { ILoggingService, LoggingReportProps } from '@koalarx/nest/services/logging'
import { BlobServiceClient } from '@azure/storage-blob'
@Injectable()
export class AzureLoggingService implements ILoggingService {
private blobClient: BlobServiceClient
constructor(connectionString: string) {
this.blobClient = BlobServiceClient.fromConnectionString(connectionString)
}
async report(data: LoggingReportProps): Promise<void> {
try {
const containerClient = this.blobClient.getContainerClient('logs')
const timestamp = new Date().toISOString()
const blobName = `logs/${data.packageName}/${timestamp}.json`
const logEntry = {
timestamp,
username: data.loggedUsername,
packageName: data.packageName,
error: {
name: data.error.name,
message: data.error.message,
stack: data.error.stack,
},
httpRequest: data.httpRequest,
}
const blobClient = containerClient.getBlockBlobClient(blobName)
await blobClient.upload(
JSON.stringify(logEntry),
Buffer.byteLength(JSON.stringify(logEntry))
)
} catch (error) {
console.error('Erro ao enviar log para Azure:', error)
}
}
}Registrar no AppModule
Passe a classe de logging customizada diretamente em logging:
// src/host/app.module.ts
import { AzureLoggingService } from '@/infra/logging/azure-logging.service'
import { KoalaNestModule } from '@koalarx/nest/core/koala-nest.module'
import { Module } from '@nestjs/common'
@Module({
imports: [
KoalaNestModule.register({
env,
controllers: [PersonModule],
logging: AzureLoggingService, // Passar a classe diretamente
// ... outras configurações
}),
],
})
export class AppModule {}Nota: Se não configurar logging, a biblioteca usará LoggingService padrão (console).
Usar Logging em Handlers
O ILoggingService é automaticamente injetado em serviços que precisem fazer logging manual:
import { ILoggingService } from '@koalarx/nest/services/logging'
import { Injectable } from '@nestjs/common'
@Injectable()
export class MyHandler {
constructor(private readonly loggingService: ILoggingService) {}
async handle() {
try {
// sua lógica aqui
} catch (error) {
await this.loggingService.report({
loggedUsername: 'system',
packageName: 'MyHandler',
error: error as Error,
})
}
}
}10. Variáveis Globais
Acesse informações globais configuradas na inicialização da aplicação.
Configurar no main.ts
// src/host/main.ts
await new KoalaApp(app)
.setAppName('example')
.setInternalUserName('integration.bot')
.buildAndServe()Acessar em Qualquer Lugar
import { KoalaGlobalVars } from '@koalarx/nest/core/koala-global-vars'
// Em qualquer componente
console.log(KoalaGlobalVars.appName) // 'example'
console.log(KoalaGlobalVars.internalUserName) // 'integration.bot'11. Ngrok (Exposição Pública em Desenvolvimento)
Exponha sua aplicação local na internet para testes ou webhooks.
Configurar Token Ngrok
# .env
NGROK_AUTH_TOKEN=seu_token_aquiUsar no main.ts
// src/host/main.ts
await new KoalaApp(app)
.useNgrok(process.env.NGROK_AUTH_TOKEN!)
.buildAndServe()A URL pública do ngrok será exibida no console durante a inicialização da aplicação.