Plugin System
Overview
The Talawa API Plugin System is a robust, event-driven plugin architecture designed for extending the GraphQL API backend without modifying core code. It provides a complete lifecycle management system for server-side plugins with support for GraphQL extensions, database schema extensions, hooks, webhooks, and Docker container management.
Key Features
- Builder-First GraphQL Extensions: Pothos GraphQL builder integration for type-safe schema extensions
- Database Extensions: Dynamic table creation and management with Drizzle ORM
- Event Hooks: Pre/post event hooks for plugin lifecycle and business logic
- Webhook Support: RESTful webhook endpoints for external integrations
- Docker Integration: Automatic container lifecycle management for plugins requiring services
- Dependency Management: Automatic npm package installation for plugin dependencies
- Event-Driven Architecture: EventEmitter-based pub/sub system for plugin communication
- Database as Source of Truth: Plugin state persisted in database for reliability
- Type-Safe: Full TypeScript support with comprehensive type definitions
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Plugin Manager (Core) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Lifecycle │ │ Extensions │ │ Registry │ │
│ │ Manager │ │ Loader │ │ Manager │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
└─── ──────────────────────────────────────────────────────────┘
│
┌───────────────────┼──────────────────┐
│ │ │
┌───────▼────────┐ ┌──────▼──────┐ ┌────────▼─────────┐
│ Extension │ │ Database │ │ Event System │
│ Registry │ │ Operations │ │ (EventEmitter) │
├────────────────┤ ├─────────────┤ ├──────────────────┤
│ • GraphQL │ │ • Plugin DB │ │ • Lifecycle │
│ (Pothos) │ │ • Tables │ │ Events │
│ • Database │ │ • Enums │ │ • Hook Events │
│ • Hooks │ │ • Relations │ │ • Custom Events │
│ • Webhooks │ │ │ │ │
└────────────────┘ └─────────────┘ └──────────────────┘
Core Files
1. index.ts - Main Entry Point
Purpose: Central export hub for the entire API plugin system.
Responsibilities:
- Exports all public APIs, types, utilities, and managers
- Provides clean interface for consuming the plugin system
- Single source of truth for what's available to external consumers
Key Exports:
PluginManager- Main orchestrator class- Type definitions (
IPluginManifest,IExtensionPoints, etc.) - Utility functions (
validatePluginManifest,loadPluginManifest, etc.) - Registry functions (
initializePluginSystem,createPluginContext)
2. types.ts - Type Definitions
Purpose: Centralized TypeScript type definitions for the entire plugin system.
Key Type Categories:
1. Plugin Manifest Types (IPluginManifest)
{
name: string;
pluginId: string;
version: string;
description: string;
author: string;
main: string;
extensionPoints?: IExtensionPoints;
dependencies?: Record<string, string>;
docker?: {
enabled?: boolean;
composeFile?: string;
service?: string;
// Lifecycle flags
buildOnInstall?: boolean;
upOnActivate?: boolean;
downOnDeactivate?: boolean;
removeOnUninstall?: boolean;
};
}
2. Extension Types
GraphQL Extensions (IGraphQLExtension)
- Uses Pothos builder-first approach
builderDefinition: Function name that defines the GraphQL field- Supports queries, mutations, and subscriptions
- Type-safe field definitions
Database Extensions (IDatabaseExtension)
type: "table" | "enum" | "relation"name: Database object namefile: Path to definition file
Hook Extensions (IHookExtension)
type: "pre" | "post"event: Event name to hook intohandler: Function name to execute
Webhook Extensions (IWebhookExtension)
path: Webhook endpoint pathmethod: HTTP method (GET, POST, PUT, DELETE, PATCH)handler: Function name for request handling
3. Runtime Types
ILoadedPlugin - Plugin instance with loaded components:
{
id: string;
manifest: IPluginManifest;
graphqlResolvers: Record<string, unknown>;
databaseTables: Record<string, Record<string, unknown>>;
hooks: Record<string, Function>;
webhooks: Record<string, Function>;
status: PluginStatus;
}
PluginStatus - Enum for plugin states:
ACTIVE- Plugin is loaded and activeINACTIVE- Plugin is loaded but not activeERROR- Plugin encountered an errorLOADING- Plugin is currently loading
4. Context Types
IPluginContext - Runtime context passed to plugins:
{
db: IDatabaseClient; // Drizzle database client
graphql: unknown; // GraphQL schema/builder
pubsub: unknown; // PubSub for subscriptions
logger: ILogger; // Logger instance
}
3. registry.ts - Plugin Registry & Initialization
Purpose: Provides initialization utilities and global plugin manager access.
Key Functions:
createPluginContext(dependencies)
Creates a plugin context object with all necessary dependencies:
const context = createPluginContext({
db: drizzleClient,
graphql: builder,
pubsub: pubsubInstance,
logger: logger
});
initializePluginSystem(context, pluginsDirectory?)
Initializes the plugin system and returns the plugin manager:
const pluginManager = await initializePluginSystem(context);
Features:
- Singleton pattern - returns same instance on subsequent calls
- Waits for
plugins:readyevent before resolving - Error handling and logging
- Async initialization
getPluginManagerInstance()
Returns the current plugin manager instance:
const manager = getPluginManagerInstance();
isPluginSystemInitialized()
Checks if plugin system is ready:
if (isPluginSystemInitialized()) {
// System is ready
}
destroyPluginSystem()
Gracefully shuts down plugin system (useful for testing):
await destroyPluginSystem();