Skip to main content
The Fastify adapter provides seamless integration of go-go-scope with Fastify applications through a plugin-based architecture.

Installation

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

Quick Start

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

const app = fastify()

// Register the plugin
await app.register(fastifyGoGoScope, {
  name: 'my-api',
  timeout: 30000 // Optional: default timeout for all requests
})

app.get('/users/:id', async (request, reply) => {
  // Access request-scoped 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
})

await app.listen({ port: 3000 })

Configuration Options

name
string
default:"'fastify-app'"
Name for the root application scope
timeout
number
Default timeout in milliseconds for all request scopes

Plugin Architecture

1

Root Scope Creation

The plugin creates a single root scope when registered, accessible via fastify.scope
2

Request Scope Creation

On each request, a child scope is created with the root as parent, accessible via request.scope
3

Automatic Cleanup

Request scopes are automatically disposed after the response is sent
4

Graceful Shutdown

The root scope is disposed when Fastify closes

Usage Examples

Parallel Data Fetching

app.get('/dashboard', async (request, reply) => {
  const [err, results] = await request.scope.parallel(
    [
      () => fetchUserProfile(),
      () => fetchRecentOrders(),
      () => fetchRecommendations()
    ],
    { concurrency: 3 }
  )

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

  return {
    profile: results[0],
    orders: results[1],
    recommendations: results[2]
  }
})

Circuit Breaker Pattern

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

const breaker = new CircuitBreaker({
  failureThreshold: 5,
  resetTimeout: 30000
})

await app.register(fastifyGoGoScope, {
  name: 'resilient-api',
  circuitBreaker: breaker
})

app.get('/external-api', async (request, reply) => {
  // Automatically uses circuit breaker
  const [err, data] = await request.scope.task(
    () => fetchExternalAPI()
  )

  if (err) {
    return reply.code(503).send({ error: 'Service unavailable' })
  }
  return data
})

Channel-based Communication

app.get('/stream-events', async (request, reply) => {
  const channel = request.scope.channel({ buffer: 100 })

  // Producer task
  request.scope.task(async ({ signal }) => {
    for (let i = 0; i < 100; i++) {
      if (signal.aborted) break
      await channel.send({ event: 'update', data: i })
      await new Promise(r => setTimeout(r, 100))
    }
    channel.close()
  })

  // Stream to response
  reply.header('Content-Type', 'text/event-stream')
  
  for await (const event of channel) {
    reply.raw.write(`data: ${JSON.stringify(event)}\n\n`)
  }
  
  reply.raw.end()
})

Using Root Scope

await app.register(fastifyGoGoScope, { name: 'my-api' })

// Access root scope for application-level tasks
app.ready(() => {
  app.scope.task(async () => {
    // Start background job that lives for app lifetime
    await startMetricsCollector()
  })
})

Type Augmentation

The adapter automatically augments Fastify types:
declare module 'fastify' {
  interface FastifyInstance {
    scope: Scope
  }
  interface FastifyRequest {
    scope: Scope
  }
}

Lifecycle Hooks

The plugin integrates with Fastify hooks:
// Request scope is created before route handler
fastify.addHook('onRequest', async (request) => {
  // request.scope is now available
  request.scope = scope({
    parent: rootScope,
    name: `request-${request.id}`
  })
})

Best Practices

Always use request.scope.task() for operations tied to a single request. This ensures automatic cancellation when the request completes.
Use fastify.scope.task() for application-level tasks that should live beyond individual requests.
Configure both global timeouts (plugin options) and per-task timeouts to prevent hanging requests.
Always check the error value in result tuples and return appropriate HTTP status codes.

Express Adapter

Express middleware integration

NestJS Adapter

Dependency injection for NestJS

Hono Adapter

Lightweight Hono middleware

Core API

Core go-go-scope concepts

Build docs developers (and LLMs) love