Skip to main content
Glama
n8n-api-client.ts9.13 kB
/** * n8n REST API Client * Handles all communication with n8n instance via REST API */ import { N8nWorkflow, N8nExecution, N8nListResponse, N8nConfig, N8nNode, N8nConnections, N8nWorkflowSettings } from '../types'; import { logger } from '../utils/logger'; import { parseHttpError, withRetry, N8nError } from '../utils/errors'; export interface N8nApiClientOptions { timeout?: number; maxRetries?: number; } export class N8nApiClient { private baseUrl: string; private apiKey: string; private timeout: number; private maxRetries: number; constructor(config: N8nConfig, options: N8nApiClientOptions = {}) { // Remove trailing slash from URL this.baseUrl = config.apiUrl.replace(/\/$/, ''); this.apiKey = config.apiKey; this.timeout = options.timeout || 30000; this.maxRetries = options.maxRetries || 3; } /** * Make authenticated request to n8n API with retry logic */ private async request<T>( endpoint: string, options: RequestInit = {} ): Promise<T> { const url = `${this.baseUrl}/api/v1${endpoint}`; const headers: Record<string, string> = { 'X-N8N-API-KEY': this.apiKey, 'Content-Type': 'application/json', ...((options.headers as Record<string, string>) || {}), }; logger.debug(`API Request: ${options.method || 'GET'} ${url}`); return withRetry(async () => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(url, { ...options, headers, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorBody = await response.text(); throw parseHttpError(response.status, errorBody); } // Handle empty responses (like DELETE) const text = await response.text(); if (!text) { return {} as T; } return JSON.parse(text) as T; } catch (error) { clearTimeout(timeoutId); if (error instanceof N8nError) { throw error; } if (error instanceof Error) { if (error.name === 'AbortError') { throw new Error('Request timed out'); } logger.error(`API Error: ${error.message}`); throw error; } throw new Error('Unknown API error'); } }, { maxRetries: this.maxRetries }); } // ============================================================================ // Health Check // ============================================================================ /** * Check n8n API connectivity */ async healthCheck(): Promise<{ status: string; version?: string }> { try { // Try to list workflows with limit 1 to verify connection await this.request<N8nListResponse<N8nWorkflow>>('/workflows?limit=1'); return { status: 'connected' }; } catch (error) { return { status: 'error', version: error instanceof Error ? error.message : 'Unknown error' }; } } // ============================================================================ // Workflow Operations // ============================================================================ /** * List all workflows */ async listWorkflows(options?: { limit?: number; cursor?: string; active?: boolean; tags?: string[]; }): Promise<N8nListResponse<N8nWorkflow>> { const params = new URLSearchParams(); if (options?.limit) params.set('limit', String(options.limit)); if (options?.cursor) params.set('cursor', options.cursor); if (options?.active !== undefined) params.set('active', String(options.active)); if (options?.tags?.length) params.set('tags', options.tags.join(',')); const queryString = params.toString(); const endpoint = `/workflows${queryString ? `?${queryString}` : ''}`; return this.request<N8nListResponse<N8nWorkflow>>(endpoint); } /** * Get a specific workflow by ID */ async getWorkflow(id: string): Promise<N8nWorkflow> { return this.request<N8nWorkflow>(`/workflows/${id}`); } /** * Create a new workflow */ async createWorkflow(workflow: { name: string; nodes: N8nNode[]; connections: N8nConnections; settings?: N8nWorkflowSettings; }): Promise<N8nWorkflow> { return this.request<N8nWorkflow>('/workflows', { method: 'POST', body: JSON.stringify(workflow), }); } /** * Update an existing workflow */ async updateWorkflow( id: string, workflow: Partial<{ name: string; nodes: N8nNode[]; connections: N8nConnections; settings: N8nWorkflowSettings; active: boolean; }> ): Promise<N8nWorkflow> { return this.request<N8nWorkflow>(`/workflows/${id}`, { method: 'PATCH', body: JSON.stringify(workflow), }); } /** * Delete a workflow */ async deleteWorkflow(id: string): Promise<void> { await this.request<void>(`/workflows/${id}`, { method: 'DELETE', }); } /** * Activate a workflow */ async activateWorkflow(id: string): Promise<N8nWorkflow> { return this.request<N8nWorkflow>(`/workflows/${id}/activate`, { method: 'POST', }); } /** * Deactivate a workflow */ async deactivateWorkflow(id: string): Promise<N8nWorkflow> { return this.request<N8nWorkflow>(`/workflows/${id}/deactivate`, { method: 'POST', }); } // ============================================================================ // Execution Operations // ============================================================================ /** * List executions */ async listExecutions(options?: { limit?: number; cursor?: string; workflowId?: string; status?: 'success' | 'error' | 'waiting'; includeData?: boolean; }): Promise<N8nListResponse<N8nExecution>> { const params = new URLSearchParams(); if (options?.limit) params.set('limit', String(options.limit)); if (options?.cursor) params.set('cursor', options.cursor); if (options?.workflowId) params.set('workflowId', options.workflowId); if (options?.status) params.set('status', options.status); if (options?.includeData !== undefined) params.set('includeData', String(options.includeData)); const queryString = params.toString(); const endpoint = `/executions${queryString ? `?${queryString}` : ''}`; return this.request<N8nListResponse<N8nExecution>>(endpoint); } /** * Get a specific execution by ID */ async getExecution(id: string, includeData = false): Promise<N8nExecution> { const params = new URLSearchParams(); if (includeData) params.set('includeData', 'true'); const queryString = params.toString(); const endpoint = `/executions/${id}${queryString ? `?${queryString}` : ''}`; return this.request<N8nExecution>(endpoint); } /** * Delete an execution */ async deleteExecution(id: string): Promise<void> { await this.request<void>(`/executions/${id}`, { method: 'DELETE', }); } // ============================================================================ // Webhook Operations // ============================================================================ /** * Trigger a workflow via webhook * Note: This calls the webhook URL directly, not the n8n API */ async triggerWebhook( webhookUrl: string, options?: { method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; data?: Record<string, unknown>; headers?: Record<string, string>; } ): Promise<unknown> { const method = options?.method || 'POST'; logger.debug(`Triggering webhook: ${method} ${webhookUrl}`); const fetchOptions: RequestInit = { method, headers: { 'Content-Type': 'application/json', ...(options?.headers || {}), }, }; if (options?.data && method !== 'GET') { fetchOptions.body = JSON.stringify(options.data); } try { const response = await fetch(webhookUrl, fetchOptions); const text = await response.text(); if (!response.ok) { throw new Error(`Webhook error: HTTP ${response.status} - ${text}`); } try { return JSON.parse(text); } catch { return { response: text }; } } catch (error) { if (error instanceof Error) { logger.error(`Webhook error: ${error.message}`); throw error; } throw new Error('Unknown webhook error'); } } } /** * Create N8nApiClient from environment variables */ export function createN8nApiClient(): N8nApiClient { const apiUrl = process.env.N8N_API_URL; const apiKey = process.env.N8N_API_KEY; if (!apiUrl) { throw new Error('N8N_API_URL environment variable is required'); } if (!apiKey) { throw new Error('N8N_API_KEY environment variable is required'); } return new N8nApiClient({ apiUrl, apiKey }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alicankiraz1/cursor-n8n-builder'

If you have feedback or need assistance with the MCP directory API, please join our Discord server