Skip to main content

Building Web APIs with Framework Adapters

go-go-scope provides framework adapters for seamless integration with popular Node.js web frameworks. Each request gets its own scope with automatic cleanup and cancellation support.

Fastify Integration

Fastify plugin provides request-scoped structured concurrency with automatic cleanup.
1

Install the adapter

npm install @go-go-scope/adapter-fastify
2

Register the plugin

import fastify from 'fastify'
import { fastifyGoGoScope } from '@go-go-scope/adapter-fastify'

const app = fastify()

// Register plugin with optional timeout
await app.register(fastifyGoGoScope, {
  name: 'my-api',
  timeout: 30000  // 30 second default timeout for all requests
})
3

Use request scope in routes

app.get('/users/:id', async (request, reply) => {
  // Each request has its own scope
  const [err, user] = await request.scope.task(
    () => fetchUser(request.params.id),
    { retry: 'exponential', timeout: 5000 }
  )

  if (err) {
    return reply.code(500).send({ error: err.message })
  }

  return user
})
4

Parallel data fetching

app.get('/dashboard', async (request, reply) => {
  const userId = request.user.id

  // Run multiple queries in parallel
  const [err, results] = await request.scope.parallel([
    () => fetchUserProfile(userId),
    () => fetchUserOrders(userId),
    () => fetchUserNotifications(userId),
  ])

  if (err) {
    return reply.code(500).send({ error: err.message })
  }

  const [profile, orders, notifications] = results.map(r => r[1])

  return {
    profile,
    orders,
    notifications
  }
})
The request scope is automatically disposed when the response completes, ensuring all running tasks are cancelled and resources are cleaned up.

Express Integration

Express middleware provides similar functionality with a familiar middleware pattern.
import express from 'express'
import { goGoScope } from '@go-go-scope/adapter-express'

const app = express()

// Apply middleware
app.use(goGoScope(app, { 
  name: 'express-api',
  timeout: 30000 
}))

app.get('/products/:id', async (req, res) => {
  const [err, product] = await req.scope.task(
    () => fetchProduct(req.params.id),
    { retry: 'exponential' }
  )

  if (err) {
    return res.status(500).json({ error: err.message })
  }

  res.json(product)
})

NestJS Integration

NestJS module provides dependency injection integration with request-scoped services.
1

Import the module

import { Module } from '@nestjs/common'
import { GoGoScopeModule } from '@go-go-scope/adapter-nestjs'

@Module({
  imports: [
    GoGoScopeModule.forRoot({
      name: 'nestjs-api',
      timeout: 30000,
      isGlobal: true  // Make available in all modules
    })
  ]
})
export class AppModule {}
2

Inject scope in controllers

import { Controller, Get, Param, Req } from '@nestjs/common'
import { Request } from 'express'

@Controller('users')
export class UsersController {
  @Get(':id')
  async getUser(@Param('id') id: string, @Req() req: Request) {
    const [err, user] = await req.scope.task(
      () => this.usersService.findOne(id),
      { retry: 'exponential', timeout: 5000 }
    )

    if (err) {
      throw new HttpException('User not found', HttpStatus.NOT_FOUND)
    }

    return user
  }
}
3

Use in services

import { Injectable } from '@nestjs/common'

@Injectable()
export class UsersService {
  async findOne(id: string, scope: Scope) {
    // Use scope for concurrent operations
    const [err, results] = await scope.parallel([
      () => this.db.query('SELECT * FROM users WHERE id = ?', [id]),
      () => this.cache.get(`user:${id}`),
    ])

    if (err) throw err

    const [dbResult, cacheResult] = results.map(r => r[1])
    
    return cacheResult || dbResult
  }
}

Advanced Patterns

Circuit Breaker for External APIs

import { CircuitBreaker } from 'go-go-scope'

const externalApiBreaker = new CircuitBreaker({
  failureThreshold: 5,
  resetTimeout: 60000,  // 1 minute
  halfOpenRequests: 3
})

app.get('/external-data', async (request, reply) => {
  const [err, data] = await request.scope.task(
    () => fetch('https://api.example.com/data').then(r => r.json()),
    { 
      circuitBreaker: externalApiBreaker,
      timeout: 5000,
      retry: 'exponential'
    }
  )

  if (err) {
    return reply.code(503).send({ 
      error: 'External service unavailable',
      retryAfter: 60
    })
  }

  return data
})

Rate Limiting with Semaphore

import { Semaphore } from 'go-go-scope'

// Limit concurrent database connections to 10
const dbSemaphore = new Semaphore(10)

app.get('/heavy-query', async (request, reply) => {
  const [err, result] = await request.scope.task(
    async ({ signal }) => {
      // Acquire semaphore (blocks if limit reached)
      await using permit = await dbSemaphore.acquire(signal)
      
      // Run heavy database query
      return await db.query('SELECT * FROM large_table')
    },
    { timeout: 30000 }
  )

  if (err) {
    return reply.code(500).send({ error: err.message })
  }

  return result
})

Request Cancellation

app.get('/slow-operation', async (request, reply) => {
  // If client disconnects, the scope is cancelled
  const [err, result] = await request.scope.task(
    async ({ signal }) => {
      // Check cancellation periodically
      for (let i = 0; i < 100; i++) {
        if (signal.aborted) {
          throw new Error('Request cancelled by client')
        }
        await processChunk(i)
      }
      return 'Complete'
    },
    { timeout: 60000 }
  )

  if (err) {
    if (err.message.includes('cancelled')) {
      return reply.code(499).send({ error: 'Client closed request' })
    }
    return reply.code(500).send({ error: err.message })
  }

  return { result }
})

Other Framework Adapters

go-go-scope supports many popular frameworks:
  • Hono: @go-go-scope/adapter-hono
  • Elysia: @go-go-scope/adapter-elysia
  • Koa: @go-go-scope/adapter-koa
  • Hapi: @go-go-scope/adapter-hapi
  • Next.js: @go-go-scope/adapter-nextjs
  • Remix: @go-go-scope/adapter-remix
  • SvelteKit: @go-go-scope/adapter-sveltekit
Each adapter follows similar patterns with framework-specific integration details.

Build docs developers (and LLMs) love