The NestJS adapter provides native dependency injection integration with go-go-scope through services, modules, and decorators.
Installation
npm install @go-go-scope/adapter-nestjs go-go-scope @nestjs/common @nestjs/core
Quick Start
import { Module, Injectable, Controller, Get } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import {
GoGoScopeModule,
GoGoRequestScopeService,
Task
} from '@go-go-scope/adapter-nestjs'
@Injectable()
class UserService {
constructor(private requestScope: GoGoRequestScopeService) {}
async getUser(id: string) {
const scope = this.requestScope.getScope()
const [err, user] = await scope.task(
() => fetchUser(id),
{ retry: 'exponential', timeout: 5000 }
)
if (err) throw err
return user
}
}
@Controller('users')
class UserController {
constructor(private userService: UserService) {}
@Get(':id')
async getUser(@Param('id') id: string) {
return this.userService.getUser(id)
}
}
@Module({
imports: [GoGoScopeModule.forRoot({ name: 'my-api', timeout: 30000 })],
controllers: [UserController],
providers: [UserService]
})
class AppModule {}
const app = await NestFactory.create(AppModule)
await app.listen(3000)
Module Registration
name
string
default:"'nestjs-app'"
Name for the root application scope
Default timeout in milliseconds for all request scopes
@Module({
imports: [
GoGoScopeModule.forRoot({
name: 'production-api',
timeout: 15000
})
]
})
export class AppModule {}
Services
GoGoScopeService
Provides access to the root application scope.
import { Injectable } from '@nestjs/common'
import { GoGoScopeService } from '@go-go-scope/adapter-nestjs'
@Injectable()
class BackgroundJobService {
constructor(private scopeService: GoGoScopeService) {}
async startJob() {
// Use root scope for application-level tasks
const [err] = await this.scopeService.rootScope.task(
async ({ signal }) => {
while (!signal.aborted) {
await this.processQueue()
await new Promise(r => setTimeout(r, 5000))
}
}
)
if (err) console.error('Job failed:', err)
}
createChildScope(name: string) {
return this.scopeService.createScope(name, {
timeout: 10000
})
}
}
GoGoRequestScopeService
Provides a unique scope per HTTP request (REQUEST-scoped).
import { Injectable, Scope } from '@nestjs/common'
import { GoGoRequestScopeService } from '@go-go-scope/adapter-nestjs'
@Injectable({ scope: Scope.REQUEST })
class RequestScopedService {
constructor(private requestScope: GoGoRequestScopeService) {}
async fetchData() {
const scope = this.requestScope.getScope()
const [err, data] = await scope.parallel([
() => this.fetchFromAPI1(),
() => this.fetchFromAPI2(),
() => this.fetchFromAPI3()
])
if (err) throw err
return data
}
}
Decorators
@Task Decorator
Automatically execute methods within a task scope:
import { Injectable } from '@nestjs/common'
import { Task, GoGoRequestScopeService } from '@go-go-scope/adapter-nestjs'
@Injectable()
class PaymentService {
@Task({ retry: 'exponential', timeout: 10000 })
async processPayment(amount: number, cardToken: string) {
// Automatically wrapped in a task with retry and timeout
const result = await paymentGateway.charge(amount, cardToken)
return result
}
@Task({ retry: 'linear' })
async refundPayment(transactionId: string) {
return await paymentGateway.refund(transactionId)
}
}
Dependency Injection Patterns
Import GoGoScopeModule
Register the module with forRoot() in your root module
Inject Services
Inject GoGoScopeService or GoGoRequestScopeService into your providers
Use Scopes
Access rootScope for app-level tasks or requestScope.getScope() for request-scoped tasks
Automatic Cleanup
Scopes are automatically disposed via OnModuleDestroy lifecycle hook
Usage Examples
Controller with Request Scope
import { Controller, Get, Inject } from '@nestjs/common'
import { GoGoRequestScopeService } from '@go-go-scope/adapter-nestjs'
@Controller('dashboard')
export class DashboardController {
constructor(private requestScope: GoGoRequestScopeService) {}
@Get()
async getDashboard() {
const scope = this.requestScope.getScope()
const [err, results] = await scope.parallel([
() => this.getUserStats(),
() => this.getRecentActivity(),
() => this.getNotifications()
])
if (err) {
throw new HttpException(
'Failed to load dashboard',
HttpStatus.INTERNAL_SERVER_ERROR
)
}
return {
stats: results[0],
activity: results[1],
notifications: results[2]
}
}
}
Background Service with Root Scope
import { Injectable, OnModuleInit } from '@nestjs/common'
import { GoGoScopeService } from '@go-go-scope/adapter-nestjs'
@Injectable()
export class MetricsCollector implements OnModuleInit {
constructor(private scopeService: GoGoScopeService) {}
onModuleInit() {
// Start background collection using root scope
this.scopeService.rootScope.task(async ({ signal }) => {
while (!signal.aborted) {
await this.collectMetrics()
await new Promise(r => setTimeout(r, 60000)) // Every minute
}
})
}
private async collectMetrics() {
// Collect and store metrics
}
}
Service with Circuit Breaker
import { Injectable } from '@nestjs/common'
import { GoGoRequestScopeService } from '@go-go-scope/adapter-nestjs'
import { CircuitBreaker } from 'go-go-scope'
@Injectable()
export class ExternalAPIService {
private breaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 30000
})
constructor(private requestScope: GoGoRequestScopeService) {}
async callExternalAPI(endpoint: string) {
const scope = this.requestScope.getScope()
const [err, response] = await scope.task(
async ({ signal }) => {
return this.breaker.execute(
() => fetch(endpoint, { signal })
)
}
)
if (err) {
if (this.breaker.state === 'open') {
throw new ServiceUnavailableException('Circuit breaker open')
}
throw err
}
return response
}
}
Custom Scope Provider
import { Module, Scope } from '@nestjs/common'
import { GoGoScopeService } from '@go-go-scope/adapter-nestjs'
import type { Scope as GoGoScope } from 'go-go-scope'
const CUSTOM_SCOPE = Symbol('CUSTOM_SCOPE')
@Module({
providers: [
{
provide: CUSTOM_SCOPE,
scope: Scope.REQUEST,
useFactory: (scopeService: GoGoScopeService): GoGoScope => {
return scopeService.createScope('custom', {
timeout: 5000
})
},
inject: [GoGoScopeService]
}
],
exports: [CUSTOM_SCOPE]
})
export class CustomScopeModule {}
Testing
Unit Testing with Mock Scopes
import { Test } from '@nestjs/testing'
import { GoGoRequestScopeService } from '@go-go-scope/adapter-nestjs'
import { scope } from 'go-go-scope'
describe('UserService', () => {
let service: UserService
let mockRequestScope: GoGoRequestScopeService
beforeEach(async () => {
// Create mock scope
const testScope = scope({ name: 'test' })
mockRequestScope = {
requestScope: testScope,
getScope: () => testScope,
onModuleDestroy: async () => {}
} as any
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: GoGoRequestScopeService,
useValue: mockRequestScope
}
]
}).compile()
service = module.get(UserService)
})
it('should fetch user', async () => {
const user = await service.getUser('123')
expect(user).toBeDefined()
})
})
Lifecycle Integration
The adapter integrates with NestJS lifecycle hooks:
@Injectable()
export class GoGoScopeService implements OnModuleDestroy {
async onModuleDestroy() {
// Root scope disposed automatically
await this.rootScope[Symbol.asyncDispose]()
}
}
Best Practices
Use REQUEST scope for request-specific tasks
Inject GoGoRequestScopeService with { scope: Scope.REQUEST } for services that handle request-specific logic.
Use root scope for background jobs
Inject GoGoScopeService for application-level background tasks that outlive requests.
Use the @Task decorator for automatic retry and timeout handling on service methods.
Combine with Guards and Interceptors
Scopes work seamlessly with NestJS guards, interceptors, and pipes.
Fastify Adapter
Fastify plugin integration
Express Adapter
Express middleware integration
Dependency Injection
DI patterns in go-go-scope
Core API
Core go-go-scope concepts