The Elysia adapter provides native Bun integration with go-go-scope through Elysia’s plugin system.
Installation
bun add @go-go-scope/adapter-elysia go-go-scope elysia
Quick Start
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope({
name: 'elysia-api',
timeout: 30000 // Optional: default timeout for all requests
}))
.get('/users/:id', async ({ scope, params }) => {
const [err, user] = await scope.task(
() => fetchUser(params.id),
{ retry: 'exponential', timeout: 5000 }
)
if (err) {
return { error: err.message }
}
return user
})
.listen(3000)
console.log(`Server running at http://localhost:${app.server?.port}`)
Configuration Options
name
string
default:"'elysia-app'"
Name for the root application scope
Default timeout in milliseconds for all request scopes
Plugin Architecture
State Setup
The plugin stores the root scope in Elysia’s global state
Request Hook
On each request, a child scope is created and stored using a symbol key
Derived Context
The scope and rootScope are injected into context via .derive()
Cleanup Hook
Request scopes are disposed in the onAfterResponse hook
Usage Examples
REST API
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/posts/:id', async ({ scope, params, set }) => {
const [err, post] = await scope.task(
() => db.posts.findById(params.id)
)
if (err) {
set.status = 404
return { error: 'Post not found' }
}
return post
})
.listen(3000)
Parallel Operations
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/dashboard/:userId', async ({ scope, params }) => {
const [err, results] = await scope.parallel([
() => fetchProfile(params.userId),
() => fetchPosts(params.userId),
() => fetchFollowers(params.userId)
])
if (err) {
return { error: 'Failed to load dashboard' }
}
return {
profile: results[0],
posts: results[1],
followers: results[2]
}
})
.listen(3000)
Server-Sent Events
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/events', async ({ scope, set }) => {
set.headers['Content-Type'] = 'text/event-stream'
set.headers['Cache-Control'] = 'no-cache'
set.headers['Connection'] = 'keep-alive'
const channel = scope.channel({ buffer: 100 })
// Producer
scope.task(async ({ signal }) => {
for (let i = 0; i < 50; i++) {
if (signal.aborted) break
await channel.send({ event: 'update', data: i })
await new Promise(r => setTimeout(r, 1000))
}
channel.close()
})
// Stream events
return new Response(
new ReadableStream({
async start(controller) {
for await (const message of channel) {
controller.enqueue(
`data: ${JSON.stringify(message)}\n\n`
)
}
controller.close()
}
})
)
})
.listen(3000)
Circuit Breaker
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
import { CircuitBreaker } from 'go-go-scope'
const breaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 30000
})
const app = new Elysia()
.use(goGoScope())
.get('/external', async ({ scope, set }) => {
const [err, response] = await scope.task(async ({ signal }) => {
return breaker.execute(() =>
fetch('https://external-api.com/data', { signal })
)
})
if (err) {
if (breaker.state === 'open') {
set.status = 503
return { error: 'Service unavailable' }
}
set.status = 500
return { error: err.message }
}
return await response.json()
})
.listen(3000)
Plugin Composition
import { Elysia } from 'elysia'
import { cors } from '@elysiajs/cors'
import { swagger } from '@elysiajs/swagger'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(cors())
.use(swagger())
.use(goGoScope({ name: 'my-api' }))
.get('/data', async ({ scope }) => {
const [err, data] = await scope.task(() => fetchData())
if (err) return { error: err.message }
return data
})
.listen(3000)
WebSocket with Scope
import { Elysia } from 'elysia'
import { goGoScope, getScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.ws('/ws', {
message(ws, message) {
// Note: WebSocket handlers don't have request scope
// Use rootScope or create a custom scope
},
open(ws) {
console.log('WebSocket opened')
},
close(ws) {
console.log('WebSocket closed')
}
})
.listen(3000)
Background Jobs with Root Scope
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope({ name: 'worker-api' }))
.get('/', ({ rootScope }) => {
// Access root scope for background tasks
rootScope.task(async ({ signal }) => {
while (!signal.aborted) {
await processQueue()
await new Promise(r => setTimeout(r, 5000))
}
})
return { status: 'ok' }
})
.listen(3000)
Helper Functions
getScope(context)
Retrieves the request scope from the Elysia context:
import { getScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/data', async (context) => {
const scope = getScope(context)
const [err, data] = await scope.task(() => fetchData())
return data
})
getRootScope(context)
Retrieves the root scope from the Elysia context:
import { getRootScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/health', async (context) => {
const rootScope = getRootScope(context)
return { status: 'ok' }
})
Bun-Specific Features
The adapter leverages Bun’s native capabilities:
import { Elysia } from 'elysia'
import { goGoScope } from '@go-go-scope/adapter-elysia'
const app = new Elysia()
.use(goGoScope())
.get('/bun-specific', async ({ scope }) => {
const [err, data] = await scope.task(async () => {
// Use Bun's native fetch
const response = await fetch('https://api.example.com')
return response.json()
})
return data
})
.listen(3000)
console.log(
`Running on Bun ${Bun.version} at http://localhost:${app.server?.port}`
)
Type Safety
The plugin injects scope and rootScope into the Elysia context:
// Automatically available in handlers
app.get('/typed', async ({ scope, rootScope, params, query }) => {
// TypeScript knows the types
const [err, result] = await scope.task(() => fetchData())
return result
})
Elysia with Bun offers exceptional performance:
- Native Bun runtime speed
- Zero-overhead plugin system
- Minimal request scope allocation
- Fast Symbol-based storage
Best Practices
Leverage Bun's performance
Take advantage of Bun’s fast startup and native APIs for optimal performance.
Access scope and rootScope directly from context - they’re automatically injected.
Handle errors consistently
Always destructure and check errors from task results for robust error handling.
Combine with other Elysia plugins like CORS, Swagger, and JWT for full-featured APIs.
Hono Adapter
Lightweight Hono middleware
Fastify Adapter
Fastify plugin integration
Worker Threads
Using go-go-scope with worker threads
Core API
Core go-go-scope concepts