Skip to content
Graffle is a work in progress. Learn more.

Extension Authoring ​

NOTE

This documentation is coming soon. In the meantime, please refer to the existing extensions in the codebase for examples of how to create your own extensions.

Overview ​

Graffle's extension system allows you to extend the client with custom functionality. Extensions can hook into various parts of the request/response lifecycle and add new methods to the client.

Examples ​

For now, you can look at these built-in extensions as examples:

Basic Pattern ​

Extensions typically follow this pattern:

ts
import { type Extension } from 'graffle'

export const MyExtension = () => {
  return Extension.create({
    name: 'MyExtension',
    // Extension implementation
  })
}

Parameterized vs Non-Parameterized Extensions ​

Extensions can be parameterized (accept configuration) or non-parameterized (no configuration needed). This design choice affects how users consume your extension.

Non-Parameterized Extensions ​

Extensions without configuration can be exported directly as extension data objects. Users reference them without calling:

ts
import { Extension } from 'graffle'

export const MyExtension = Extension.create({
  name: 'MyExtension',
  // No configuration needed
})

// Usage - no function call needed
client.use(MyExtension)

Examples:

  • TransportHttp - No base configuration required
  • TransportMemory - No configuration needed
  • Throws - Simple behavior toggle

Parameterized Extensions ​

Extensions that accept configuration should be wrapped in a factory function. Users must call the extension to use it:

ts
import { Extension } from 'graffle'

export const MyExtension = (config?: { option?: string }) => {
  return Extension.create({
    name: 'MyExtension',
    // Use config in your implementation
  })
}

// Usage - must call to provide config (even if config is optional)
client.use(MyExtension({ option: 'value' }))
client.use(MyExtension()) // Still must call even with no args

Examples:

  • DocumentBuilder() - Accepts domains configuration
  • SchemaErrors() - Accepts error handling options
  • Introspection() - May accept introspection options
  • OpenTelemetry() - Accepts telemetry configuration

Choosing the Right Pattern ​

Use Non-Parameterized when:

  • Extension has zero configuration options
  • Behavior is purely additive without customization
  • You want the simplest possible API

Use Parameterized when:

  • Extension accepts any configuration (even if optional)
  • You may add configuration options in the future
  • Behavior can be customized

Important: Once you ship a non-parameterized extension, you cannot easily add configuration later without breaking changes. When in doubt, use a parameterized pattern with optional config.

More detailed documentation will be available soon.

Released under the MIT License.