npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@egose/n8n-client

v0.5.0

Published

TypeScript client for the n8n Public API with cursor-based pagination and typed resource handles.

Readme

n8n client

What It Is

@egose/n8n-client provides a TypeScript client for the n8n Public API. Instead of manually constructing HTTP requests and parsing responses, you work through resource clients:

const client = new N8nClient({ baseUrl: 'http://localhost:5678', apiKey: 'your-api-key' }); // pragma: allowlist secret

const workflows = await client.workflows().list({ limit: 10 });
const workflow = await client.workflows().get('wf-1');
const executions = await client.executions().list({ workflowId: 'wf-1', status: 'success' });

Why Use It

  • Typed request/response objects for all n8n API endpoints
  • Cursor-based pagination built in
  • Automatic retry on transient server errors (5xx, 429, 408)
  • Consistent resource client pattern across all resource types
  • Supports both API key and Bearer token authentication

Installation

Requirements:

  • Node.js >=20
npm install @egose/n8n-client

Quick Start

import N8nClient from '@egose/n8n-client';

const client = new N8nClient({
  baseUrl: 'http://localhost:5678',
  apiKey: 'your-api-key', // or use bearerToken: 'your-jwt' // pragma: allowlist secret
});

// List workflows
const { data: workflows, nextCursor } = await client.workflows().list({ limit: 10 });

// Create a workflow
const workflow = await client.workflows().create({
  name: 'My Workflow',
  nodes: [{ name: 'Start', type: 'n8n-nodes-base.start', position: [250, 300] }],
  connections: {},
  settings: {},
});

// Activate it
await client.workflows().activate(workflow.id);

// List executions
const { data: executions } = await client.executions().list({ workflowId: workflow.id });

// Create a credential
const credential = await client.credentials().create({
  name: 'My GitHub Token',
  type: 'githubApi',
  data: { accessToken: 'ghp_xxx' },
});

// Test the credential
const testResult = await client.credentials().test(credential.id);

// Manage tags
const tag = await client.tags().create({ name: 'production' });
await client.workflows().updateTags(workflow.id, [{ id: tag.id }]);

// Manage projects
const projects = await client.projects().list();
await client.projects().addMembers(projects.data[0].id, [{ userId: 'user-1', role: 'project:editor' }]);

// Opt into bound resource objects when you want instance methods
const project = await client.projects().getResource(projects.data[0].id);
await project.update({ name: 'Production' });

const workflow = await client.workflows().getResource('wf-1');
await workflow.activate();

// Insights
const summary = await client.insights().getSummary({
  startDate: '2024-01-01T00:00:00Z',
  endDate: '2024-01-31T23:59:59Z',
});

Authentication

The n8n API supports two authentication methods:

API Key

const client = new N8nClient({
  baseUrl: 'http://localhost:5678',
  apiKey: 'your-n8n-api-key', // pragma: allowlist secret
});

Bearer Token (JWT)

const client = new N8nClient({
  baseUrl: 'http://localhost:5678',
  bearerToken: 'your-jwt-token',
});

Resource Clients

Raw collection methods like list() and get() return plain API response objects.

Opt into bound resource objects when you want instance methods:

const project = await client.projects().getResource('proj-1');
await project.update({ name: 'Ops' });

const workflows = await project.workflows().listResources();
await workflows.data[0]?.archive();

const folders = await project.folders().listResources({ take: '10' });
await folders.data[0]?.update({ name: 'Archived Workflows' });
await folders.data[0]?.patch({ parentFolderId: 'archive-root' });

const renamedFolder = await project.folders().updateResource('folder-id', { name: 'Archived Workflows' });
const movedFolder = await project.folders().patchResource('folder-id', { parentFolderId: 'archive-root' });

await project.variables().create({ key: 'API_URL', value: 'https://example.com' });
await project.variables().patch('var-1', { value: 'https://api.example.com' });

const createdWorkflow = await project.workflows().create({
  name: 'Sync',
  nodes: [],
  connections: {},
  settings: {},
});

const createdFolder = await project.folders().create({ name: 'Operations' });

const table = await project.dataTables().createResource({
  name: 'Users',
  columns: [{ name: 'email', type: 'string' }],
});

await table.createColumn({ name: 'active', type: 'boolean' });

const recentRuns = await project.executions().listResources({ limit: 10, status: 'success' });
await recentRuns.data[0]?.getTags();

const workflowResource = await client.workflows().getResource('wf-1');
const workflowRuns = await workflowResource.executions().listResources({ limit: 10 });

Nested collections follow the same pairing pattern when the API can support it:

const rawWorkflow = await project.workflows().get('wf-1');
const workflowResource = await project.workflows().getResource('wf-1');

const rawFolder = await project.folders().update('folder-id', { name: 'Archive' });
const folderResource = await project.folders().updateResource('folder-id', { name: 'Archive' });
const patchedWorkflow = await project.workflows().patch('wf-1', { name: 'Archive Sync' });
const patchedWorkflowResource = await project.workflows().patchResource('wf-1', { name: 'Archive Sync v2' });

The same rule applies at the top-level clients: use get() for raw API objects and getResource() for bound instances.

The current resource layer covers projects, workflows, credentials, folders, tags, and users:

const credential = await client.credentials().getResource('cred-1');
await credential.test();

const folder = await client.folders('proj-1').getResource('folder-1');
await folder.update({ name: 'Archived Workflows' });

const user = await client.users().getResource('user-1', { includeRole: true });
await user.changeRole('global:admin');

It also includes resources for executions, variables, and data tables:

const execution = await client.executions().getResource(42, { includeData: true });
await execution.stop();

const variable = await client.variables().getResource('var-1');
await variable.update({ key: 'API_URL', value: 'https://example.com' });
await variable.patch({ value: 'https://api.internal.example.com' });

const dataTable = await client.dataTables().createResource({
  name: 'Users',
  columns: [{ name: 'email', type: 'string' }],
});

await dataTable.createColumn({ name: 'active', type: 'boolean' });

Community packages use install/uninstall terminology instead of create/delete:

const pkg = await client.communityPackages().installResource({
  name: 'n8n-nodes-foo',
});

await pkg.update({ version: '2.0.0' });
await pkg.patch({ verify: true });
await pkg.uninstall();

When the create endpoint returns the created entity, the client also exposes createResource():

const workflow = await client.workflows().createResource({
  name: 'Sync',
  nodes: [],
  connections: {},
  settings: {},
});

const credential = await client.credentials().createResource({
  name: 'Internal API Token',
  type: 'httpHeaderAuth',
  data: { name: 'Authorization', value: 'Bearer token' },
});

Rule of thumb:

  • create() mirrors the underlying API/client return type
  • createResource() returns a bound resource instance when the API returns the created entity
  • updateResource() returns a bound resource instance when the updated result can be represented honestly, either from the update response itself or from a verified follow-up fetch
  • patch() / patchResource() are convenience helpers on bound resources and nested collections that merge your partial fields over the current resource snapshot before calling the underlying update path

projects().createResource() is intentionally not available because POST /projects returns no entity or identifier.

Workflow

const workflowApi = client.workflows();

// List with filters
const { data, nextCursor } = await workflowApi.list({
  limit: 10,
  active: true,
  tags: 'production',
  name: 'deploy',
  projectId: 'proj-1',
});

// CRUD
const workflow = await workflowApi.get('wf-1');
const workflowResource = await workflowApi.getResource('wf-1');
const created = await workflowApi.create({ name: 'New', nodes: [], connections: {}, settings: {} });
const updated = await workflowApi.update('wf-1', {
  name: 'Updated',
  nodes: workflow.nodes,
  connections: workflow.connections,
  settings: workflow.settings ?? {},
});
const updatedResource = await workflowApi.updateResource('wf-1', {
  name: 'Updated Again',
  nodes: [],
  connections: {},
  settings: {},
});
await workflowApi.delete('wf-1');

// Actions
await workflowApi.activate('wf-1');
await workflowApi.deactivate('wf-1');
await workflowApi.archive('wf-1');
await workflowApi.unarchive('wf-1');
await workflowApi.transfer('wf-1', 'proj-2');

// Tags
const tags = await workflowApi.getTags('wf-1');
await workflowApi.updateTags('wf-1', [{ id: 'tag-1' }]);

// Versioning
const version = await workflowApi.getVersion('wf-1', 'v-1');

// Nested executions
const runs = await workflowResource.executions().listResources({ limit: 10 });

Execution

const executionApi = client.executions();

const { data } = await executionApi.list({ status: 'success', workflowId: 'wf-1' });
const execution = await executionApi.get(1, { includeData: true });
await executionApi.delete(1);
await executionApi.retry(1, { loadWorkflow: true });
await executionApi.stop(1);
await executionApi.stopMany({ status: ['running', 'waiting'] });

const tags = await executionApi.getTags(1);
await executionApi.updateTags(1, [{ id: 'tag-1' }]);

Credential

const credentialApi = client.credentials();

const { data } = await credentialApi.list({ limit: 10 });
const credential = await credentialApi.get('c-1');
const created = await credentialApi.create({
  name: 'GitHub',
  type: 'githubApi',
  data: { accessToken: 'ghp_xxx' },
});
const updated = await credentialApi.update('c-1', { name: 'GitHub Updated' });
const updatedResource = await credentialApi.updateResource('c-1', { name: 'GitHub Updated Again' });
await credentialApi.delete('c-1');
await credentialApi.test('c-1');
await credentialApi.transfer('c-1', 'proj-2');
const schema = await credentialApi.getSchema('githubApi');

Tag

const tagApi = client.tags();

const { data } = await tagApi.list({ limit: 10 });
const tag = await tagApi.get('t-1');
const created = await tagApi.create({ name: 'production' });
const updated = await tagApi.update('t-1', { name: 'prod' });
const updatedResource = await tagApi.updateResource('t-1', { name: 'production' });
await tagApi.delete('t-1');

User

const userApi = client.users();

const { data } = await userApi.list({ limit: 10 });
const user = await userApi.get('u-1');
const created = await userApi.create([{ email: '[email protected]', role: 'global:member' }]);
await userApi.delete('u-1');
await userApi.changeRole('u-1', 'global:admin');

Variable

const variableApi = client.variables();

const { data } = await variableApi.list({ projectId: 'proj-1' });
await variableApi.create({ key: 'MY_VAR', value: 'hello', projectId: 'proj-1' });
await variableApi.update('v-1', { key: 'MY_VAR', value: 'world' });
await variableApi.delete('v-1');

Project

const projectApi = client.projects();

const { data } = await projectApi.list({ limit: 10 });
await projectApi.create({ name: 'New Project' });
await projectApi.update('p-1', { name: 'Updated' });
await projectApi.delete('p-1');

// Members
const members = await projectApi.listMembers('p-1');
await projectApi.addMembers('p-1', [{ userId: 'u-1', role: 'project:viewer' }]);
await projectApi.changeMemberRole('p-1', 'u-1', 'project:editor');
await projectApi.removeMember('p-1', 'u-1');

DataTable

const dataTableApi = client.dataTables();

const { data } = await dataTableApi.list({ limit: 10 });
const table = await dataTableApi.get('dt-1');
const created = await dataTableApi.create({
  name: 'Users',
  columns: [
    { name: 'email', type: 'string' },
    { name: 'age', type: 'number' },
  ],
});
await dataTableApi.update('dt-1', { name: 'Users Updated' });
const updatedTable = await dataTableApi.updateResource('dt-1', { name: 'Users Final' });
await dataTableApi.delete('dt-1');

// Rows
const rows = await dataTableApi.listRows('dt-1', { limit: 25 });
await dataTableApi.insertRows('dt-1', { data: [{ email: '[email protected]', age: 30 }] });

// Columns
const columns = await dataTableApi.listColumns('dt-1');
await dataTableApi.createColumn('dt-1', { name: 'active', type: 'boolean' });
await dataTableApi.updateColumn('dt-1', 'col-1', { name: 'is_active' });
await dataTableApi.deleteColumn('dt-1', 'col-1');

Folder

const folderApi = client.folders('proj-1');

const { data } = await folderApi.list({ take: '10' });
const folder = await folderApi.get('f-1');
const created = await folderApi.create({ name: 'My Folder' });
const updated = await folderApi.update('f-1', { name: 'Renamed' });
const updatedResource = await folderApi.updateResource('f-1', { name: 'Renamed Again' });
await folderApi.delete('f-1', 'f-2'); // transfer contents to f-2

Community Package

const pkgApi = client.communityPackages();

const packages = await pkgApi.list();
const installed = await pkgApi.install({ name: 'n8n-nodes-foo' });
await pkgApi.update('n8n-nodes-foo', { version: '2.0.0' });
const updatedResource = await pkgApi.updateResource('n8n-nodes-foo', { version: '2.1.0' });
await pkgApi.uninstall('n8n-nodes-foo');

Audit

const audit = await client.audit().generate({
  additionalOptions: {
    daysAbandonedWorkflow: 30,
    categories: ['credentials', 'nodes'],
  },
});

Insights

const summary = await client.insights().getSummary({
  startDate: '2024-01-01T00:00:00Z',
  endDate: '2024-01-31T23:59:59Z',
  projectId: 'proj-1',
});

Source Control

const files = await client.sourceControl().pull({ force: false, autoPublish: 'none' });

Discover

const capabilities = await client.discover().get({ include: 'schemas' });

Error Handling

import { HttpError } from '@egose/n8n-client';

try {
  await client.workflows().get('nonexistent');
} catch (error) {
  if (error instanceof HttpError) {
    console.log(error.status); // 404
    console.log(error.message); // "Not Found"
    console.log(error.data); // response body
  }
}

Transient errors (408, 429, 5xx) are automatically retried up to 3 times with exponential backoff.

Main Entry Points

N8nClient

| Method | Returns | Description | | --------------------- | ------------------------ | ------------------------------------------------ | | workflows() | WorkflowClient | Workflow CRUD + activate/deactivate/archive/tags | | executions() | ExecutionClient | Execution list/get/delete/retry/stop/tags | | credentials() | CredentialClient | Credential CRUD + test/transfer/schema | | tags() | TagClient | Tag CRUD | | users() | UserClient | User list/get/create/delete/changeRole | | variables() | VariableClient | Variable CRUD | | projects() | ProjectClient | Project CRUD + member management | | dataTables() | DataTableClient | DataTable CRUD + rows/columns | | folders(projectId) | FolderClient | Folder CRUD scoped to project | | communityPackages() | CommunityPackageClient | Package install/update/uninstall | | audit() | AuditClient | Generate security audit | | insights() | InsightsClient | Execution insights summary | | sourceControl() | SourceControlClient | Git-based source control pull | | discover() | DiscoverClient | API capability discovery | | n8nPackage() | N8nPackageClient | Package export/import (beta) |

Documentation

  • Site: https://n8n-client.pages.dev
  • Quick Start: https://n8n-client.pages.dev/about/quick-start/
  • API Reference: https://n8n-client.pages.dev/api/
  • n8n API Docs: https://docs.n8n.io/api/

Development

npm install
npm run build
npm test

Test Layers

  • npm run test:unit — Runs mocked unit tests in tests/implementation-*.spec.ts. These verify the client's HTTP method selection, parameter forwarding, and response handling.
  • npm run test:integration — Reserved for live integration tests against a running n8n instance.
  • npm test — Runs the full suite.

License

Apache-2.0