Describe the issue/error/question
Currently n8n calls custom node functions using nodeType.execute.call(thisArgs)
which works fine, except if this
is necessary in custom node class as Function.prototype.call()
overrides this
context.
Example
Abstract class:
import {
IExecuteFunctions,
INodeExecutionData,
INodeType }
from 'n8n-workflow'
export abstract class AbstractNode<TParams> implements Omit<INodeType, 'description'> {
abstract run(t: TParams): Promise<INodeExecutionData>
async execute(): Promise<INodeExecutionData[][]> {
const executeFunctions = this as unknown as IExecuteFunctions
// THIS LINE DOES NOT WORK
// ERROR: TypeError: this.run is not a function
// It's no possible to access any instance method from class (any call with "this")
// Because "this" was override by .call(thisArgs)
await this.run({ prompts: ['hello', 'world'] } as TParams)
return this.executeFunctions.prepareOutputData([
{ json: { answer: 'Sample answer' } },
])
}
}
Node class
import { Logger } from '@nestjs/common'
import { FirefliesContext } from '@src/common'
import { AbstractNode } from '@src/n8n'
import { INodeExecutionData } from 'n8n-workflow'
type CodexParams = { prompts: string[] }
export class Codex extends AbstractNode<CodexParams> {
run({ prompts }: CodexParams): Promise<INodeExecutionData> {
console.log(`Prompts="${prompts.join(', ')}"`)
}
}
Suggestion
I’d like to suggest to change every call using: .call(thisArgs)
to .call(thisArgs, thisArgs)
that way thisArgs
overrides this
and is passed as function argument as well.
So what about the overriding context thing? That can be fixed by changing execute to arrow function instead which keeps instance scope, the change would be:
import {
IExecuteFunctions,
INodeExecutionData,
INodeType }
from 'n8n-workflow'
export abstract class AbstractNode<TParams> implements Omit<INodeType, 'description'> {
abstract run(t: TParams): Promise<INodeExecutionData>
execute = async (...args: any[]): Promise<INodeExecutionData[][]> => {
const executeFunctions = args as unknown as IExecuteFunctions
// NOW IT WORKS as "this" is instance "this"
await this.run({ prompts: ['hello', 'world'] } as TParams)
return this.executeFunctions.prepareOutputData([
{ json: { answer: 'Sample answer' } },
])
}
}
I can open the PR, but first I’d like to bring this for discussion.
Information on your n8n setup
- n8n version: 0.213.0
- Database you’re using (default: SQLite): Postgres
- Running n8n with the execution process [own(default), main]: main
- Running n8n via [Docker, npm, n8n.cloud, desktop app]: Docker