6.7 KiB
n8n Node (packages/n8n-node)
Community node package @probo/n8n-nodes-probo exposing the Probo API as n8n operations. One Probo node with many resources; each resource maps to a set of GraphQL operations against the Console or Connect API.
Directory structure
packages/n8n-node/
credentials/ProboApi.credentials.ts # API key credential (Bearer token)
nodes/Probo/
Probo.node.ts # Node class — resource picker, dispatch
Probo.node.json # n8n codex metadata
GenericFunctions.ts # GraphQL request helpers, pagination
actions/
index.ts # Resource registry, dispatch, field aggregators
<resource>/
index.ts # Operation dropdown + spread descriptions + re-exports
create.operation.ts # One file per operation
get.operation.ts
getAll.operation.ts
update.operation.ts
delete.operation.ts
...
Resource registration
Two places must be updated when adding a resource:
1. actions/index.ts — import the module and add it to the resources map:
import * as myresource from './myresource';
export const resources: Record<string, ResourceModule> = {
// ... existing resources ...
myresource: myresource as ResourceModule,
};
2. Probo.node.ts — add a Resource dropdown entry in the properties array:
{
name: 'My Resource',
value: 'myresource',
description: 'Manage my resources',
},
The value must match the key in resources and the displayOptions.show.resource in every operation file.
Per-resource file pattern
<resource>/index.ts
- Import each
*.operation.tsas a namespace. - Export
description— the operation dropdown (gated withdisplayOptions.show.resource) plus all spread operation descriptions. - Re-export each operation module with a name matching its
operationvalue.
import * as createOp from './create.operation';
import * as getOp from './get.operation';
import * as getAllOp from './getAll.operation';
export const description: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['myresource'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new resource',
action: 'Create a resource',
},
// ... more operations ...
],
default: 'create',
},
...createOp.description,
...getOp.description,
...getAllOp.description,
];
export {
createOp as create,
getOp as get,
getAllOp as getAll,
};
Export names (create, get, getAll, etc.) must match the operation value strings — getExecuteFunction uses them as keys.
<resource>/<verb>.operation.ts
Each file exports description (field definitions) and execute (the handler):
export const description: INodeProperties[] = [
{
displayName: 'Organization ID',
name: 'organizationId',
type: 'string',
displayOptions: {
show: {
resource: ['myresource'],
operation: ['create'],
},
},
default: '',
required: true,
},
// ... more fields ...
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['myresource'],
operation: ['create'],
},
},
options: [
// optional field definitions
],
},
];
export async function execute(
this: IExecuteFunctions,
itemIndex: number,
): Promise<INodeExecutionData> {
const organizationId = this.getNodeParameter('organizationId', itemIndex) as string;
const name = this.getNodeParameter('name', itemIndex) as string;
const query = `
mutation CreateMyResource($input: CreateMyResourceInput!) {
createMyResource(input: $input) {
myResourceEdge {
node {
id
name
}
}
}
}
`;
const responseData = await proboApiRequest.call(this, query, {
input: { organizationId, name },
});
return {
json: responseData,
pairedItem: { item: itemIndex },
};
}
GraphQL helpers
All helpers live in GenericFunctions.ts.
| Helper | API endpoint | Use case |
|---|---|---|
proboApiRequest |
/api/console/v1/graphql |
Single mutations and queries |
proboConnectApiRequest |
/api/connect/v1/graphql |
Organization/user operations (IAM) |
proboApiRequestAllItems |
Console API | Cursor-paginated list queries |
proboConnectApiRequestAllItems |
Connect API | Cursor-paginated list queries (IAM) |
proboApiMultipartRequest |
Console API | File upload mutations (multipart/form-data) |
Pagination (proboApiRequestAllItems)
Caller supplies a getConnection function that navigates from the raw GraphQL response to the Relay connection object (must have edges and pageInfo):
const items = await proboApiRequestAllItems.call(
this,
query,
{ organizationId },
(response) => {
const data = response?.data as IDataObject | undefined;
const node = data?.node as IDataObject | undefined;
return node?.myResources as IDataObject | undefined;
},
returnAll,
limit,
);
Internal page size is 100. When returnAll is false, stops at limit.
Update pattern
For nullable fields, empty string means "clear the value":
if (additionalFields.description !== undefined) {
input.description = additionalFields.description === '' ? null : additionalFields.description;
}
Adding a new resource — checklist
- Directory — create
nodes/Probo/actions/<resource>/withindex.tsand one*.operation.tsper operation - Operations — each file exports
description(fields gated withdisplayOptions) andexecute(reads params, calls GraphQL, returns{ json, pairedItem }) - Index —
<resource>/index.tsdefines the operation dropdown, spreads all descriptions, re-exports ops with matching value names - Register — import and add to
resourcesmap inactions/index.ts - Node — add Resource dropdown entry in
Probo.node.tsproperties - Verify —
npx n8n-node lintmust pass