Plugins

Lifecycle Methods

Understanding plugin lifecycle hooks in Universal Data Layer

Plugin Lifecycle

The Universal Data Layer plugin system provides lifecycle hooks that allow plugins to execute code at specific points during the application's initialization and runtime.

Available Lifecycle Hooks

Currently, UDL provides one main lifecycle hook:

onLoad

The onLoad hook is called when a plugin is loaded and initialized. This is where you perform setup, initialize connections, and configure your plugin.

udl.config.js
export function onLoad({ options, config }) {
  // Plugin initialization code
}

The onLoad Hook

When It's Called

The onLoad hook is executed:

  1. After the application's udl.config.js is loaded
  2. For each plugin in the plugins array, in order
  3. Before the GraphQL server starts

Execution Flow

1. Load app config (udl.config.js)
   ↓
2. Execute app's onLoad hook (if defined)
   ↓
3. For each plugin:
   - Resolve plugin path
   - Load plugin's udl.config.js
   - Execute plugin's onLoad hook
   ↓
4. Start GraphQL server

Context Object

The onLoad hook receives a context object with two properties:

options

Plugin-specific configuration passed during registration:

udl.config.js
export const config = {
  plugins: [
    {
      name: 'my-plugin',
      options: {
        apiKey: 'secret',
        timeout: 5000,
      },
    },
  ],
};

// Plugin's onLoad receives these options
export function onLoad({ options }) {
  console.log(options.apiKey);    // 'secret'
  console.log(options.timeout);   // 5000
}

config

The entire application configuration:

udl.config.js
export const config = {
  port: 4000,
  host: 'localhost',
  endpoint: 'http://localhost:4000/graphql',
  plugins: ['my-plugin'],
};

// Plugin's onLoad receives the full config
export function onLoad({ config }) {
  console.log(config.port);      // 4000
  console.log(config.host);      // 'localhost'
  console.log(config.endpoint);  // 'http://localhost:4000/graphql'
}

Async Lifecycle Hooks

The onLoad hook supports async operations. UDL will wait for the promise to resolve before continuing:

udl.config.js
export async function onLoad({ options }) {
  // Async initialization
  await connectToDatabase(options.connectionString);
  await loadDataSchema();
  await warmupCache();

  console.log('Plugin fully initialized');
}

Error Handling

Throwing Errors

If a plugin's onLoad fails, it should throw an error:

udl.config.js
export function onLoad({ options }) {
  if (!options?.apiKey) {
    throw new Error('API key is required');
  }

  if (!validateApiKey(options.apiKey)) {
    throw new Error('Invalid API key format');
  }
}

Graceful Degradation

For non-critical failures, log warnings instead:

udl.config.js
export async function onLoad({ options }) {
  try {
    await warmupCache();
  } catch (error) {
    console.warn('Cache warmup failed, continuing anyway:', error);
  }
}

Async Error Handling

Handle async errors properly:

udl.config.js
export async function onLoad({ options }) {
  try {
    await initializeService(options);
  } catch (error) {
    console.error('Failed to initialize service:', error);
    throw new Error(`Plugin initialization failed: ${error.message}`);
  }
}

Best Practices

1. Validate Required Options Early

udl.config.js
export function onLoad({ options }) {
  const required = ['apiKey', 'secretKey', 'endpoint'];

  for (const key of required) {
    if (!options?.[key]) {
      throw new Error(`Missing required option: ${key}`);
    }
  }

  // Continue with initialization
}

2. Use Async/Await for I/O Operations

udl.config.js
export async function onLoad({ options }) {
  await Promise.all([
    connectToDatabase(options),
    loadRemoteSchema(options),
    initializeCache(options),
  ]);
}

3. Provide Detailed Logging

udl.config.js
export async function onLoad({ options }) {
  console.log(`[${config.name}] Initializing...`);

  await setupClient(options);
  console.log(`[${config.name}] Client configured`);

  await loadSchema();
  console.log(`[${config.name}] Schema loaded`);

  console.log(`[${config.name}] Ready`);
}

4. Export Utility Functions

Make plugin functionality accessible:

udl.config.js
let client;

export async function onLoad({ options }) {
  client = await createClient(options);
}

// Export for use by other plugins or app code
export function getClient() {
  return client;
}

export async function query(query) {
  return client.query(query);
}

Next Steps