mirror of
https://github.com/n8n-io/n8n-docs.git
synced 2025-11-20 17:48:34 +00:00
Merge branch 'main' into course-l2
This commit is contained in:
commit
34f178641e
11
README.md
11
README.md
@ -16,10 +16,13 @@ This repository hosts the documentation for [n8n](https://n8n.io/), an extendabl
|
||||
git clone https://github.com/n8n-io/n8n-docs.git
|
||||
cd n8n-docs
|
||||
pip install -r requirements.txt
|
||||
# You need a GitHub token for access to Material Insiders.
|
||||
# n8n employees: request this from Deborah
|
||||
pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||
# External contributors: use the free version of Material (most features will still work), or rely on the preview builds on pull requests
|
||||
# n8n organization members:
|
||||
# Outside your docs project, do:
|
||||
git clone https://github.com/n8n-io/mkdocs-material-insiders.git mkdocs-material
|
||||
# Navigate back into the docs project and run:
|
||||
pip install -e <path-to-mkdocs-material>
|
||||
# External contributors: rely on the preview builds on pull requests, or
|
||||
# use the free version of Material for MkDocs (most things are the same, some formatting may be missing)
|
||||
pip install mkdocs-material
|
||||
# Serve a local preview
|
||||
mkdocs serve
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
# Weird ones after IA launch
|
||||
|
||||
|
||||
# Updated during IA work
|
||||
/getting-started/key-components/ /workflows/
|
||||
/getting-started/key-components/connection.html /workflows/connections/
|
||||
/getting-started/key-components/editor-ui.html /editor-ui/
|
||||
/getting-started/key-components/node.html /workflows/nodes/
|
||||
/getting-started/key-components/workflow.html /workflows/workflows/
|
||||
/getting-started/key-concepts/looping.htm /flow-logic/looping/
|
||||
/getting-started/key-concepts/looping.html /flow-logic/looping/
|
||||
/getting-started/key-concepts/modes.html /hosting/scaling/execution-modes-processes/
|
||||
/getting-started/key-concepts/transforming-data.html /data/
|
||||
/getting-started/installation/ /hosting/options/
|
||||
@ -24,12 +27,24 @@
|
||||
/reference/telemetry.html /reference/data-collection/
|
||||
/reference/troubleshooting.html /hosting/installation/npm/
|
||||
/reference/contributing.html /contributing/
|
||||
/reference/release-notes.html /reference/release-notes/
|
||||
/reference/release-notes.html /reference/release-notes/
|
||||
/reference/server-setup.html /hosting/server-setups/
|
||||
|
||||
# The nodes library . . . may the odds be ever in your favour
|
||||
/credentials/* /integrations/credentials/:splat
|
||||
|
||||
/nodes/creating-nodes/* /integrations/creating-nodes/code/:splat
|
||||
/nodes/creating-nodes/ /integrations/creating-nodes/
|
||||
/nodes/creating-nodes/general-guidelines.html /integrations/creating-nodes/code/standards/
|
||||
/nodes/creating-nodes/create-node.html /integrations/creating-nodes/code/create-first-node/
|
||||
/nodes/creating-nodes/create-n8n-nodes-module.html /integrations/creating-nodes/code/create-n8n-nodes-module/
|
||||
/nodes/creating-nodes/create-trigger-node.html /integrations/creating-nodes/code/create-trigger-node/
|
||||
/nodes/creating-nodes/node-dev-cli.html /integrations/creating-nodes/code/node-dev-cli/
|
||||
/nodes/creating-nodes/node-elements.html /integrations/creating-nodes/code/ui-elements/
|
||||
/nodes/creating-nodes/making-http-requests.html /integrations/creating-nodes/code/http-helpers/
|
||||
/nodes/creating-nodes/node-linter.html /integrations/creating-nodes/code/node-linter/
|
||||
/nodes/creating-nodes/node-review-checklist.html /integrations/creating-nodes/code/review-checklist/
|
||||
/nodes/creating-nodes/troubleshooting.html /integrations/creating-nodes/code/troubleshooting-node-development/
|
||||
|
||||
|
||||
/nodes/expressions/* /code-examples/expressions/:splat
|
||||
|
||||
@ -37,35 +52,59 @@
|
||||
/nodes/creating-nodes/ /integrations/creating-nodes/
|
||||
/nodes/credentials/ /integrations/credentials/
|
||||
/nodes/n8n-nodes-base.actionNetwork/ /integrations/nodes/n8n-nodes-base.actionNetwork/
|
||||
/nodes/n8n-nodes-base.actionnetwork/ /integrations/nodes/n8n-nodes-base.actionNetwork/
|
||||
/nodes/n8n-nodes-base.activationTrigger/ /integrations/core-nodes/n8n-nodes-base.activationTrigger/
|
||||
/nodes/n8n-nodes-base.activationtrigger/ /integrations/core-nodes/n8n-nodes-base.activationTrigger/
|
||||
/nodes/n8n-nodes-base.activeCampaign/ /integrations/nodes/n8n-nodes-base.activeCampaign/
|
||||
/nodes/n8n-nodes-base.activecampaign/ /integrations/nodes/n8n-nodes-base.activeCampaign/
|
||||
/nodes/n8n-nodes-base.activeCampaignTrigger/ /integrations/trigger-nodes/n8n-nodes-base.activeCampaignTrigger/
|
||||
/nodes/n8n-nodes-base.activecampaigntrigger/ /integrations/trigger-nodes/n8n-nodes-base.activeCampaignTrigger/
|
||||
/nodes/n8n-nodes-base.acuitySchedulingTrigger/ /integrations/trigger-nodes/n8n-nodes-base.acuitySchedulingTrigger/
|
||||
/nodes/n8n-nodes-base.acuityschedulingtrigger/ /integrations/trigger-nodes/n8n-nodes-base.acuitySchedulingTrigger/
|
||||
/nodes/n8n-nodes-base.affinity/ /integrations/nodes/n8n-nodes-base.affinity/
|
||||
/nodes/n8n-nodes-base.affinityTrigger/ /integrations/trigger-nodes/n8n-nodes-base.affinityTrigger/
|
||||
/nodes/n8n-nodes-base.affinitytrigger/ /integrations/trigger-nodes/n8n-nodes-base.affinityTrigger/
|
||||
/nodes/n8n-nodes-base.agileCrm/ /integrations/nodes/n8n-nodes-base.agileCrm/
|
||||
/nodes/n8n-nodes-base.agilecrm/ /integrations/nodes/n8n-nodes-base.agileCrm/
|
||||
/nodes/n8n-nodes-base.airtable/ /integrations/nodes/n8n-nodes-base.airtable/
|
||||
/nodes/n8n-nodes-base.airtableTrigger/ /integrations/trigger-nodes/n8n-nodes-base.airtableTrigger/
|
||||
/nodes/n8n-nodes-base.airtabletrigger/ /integrations/trigger-nodes/n8n-nodes-base.airtableTrigger/
|
||||
/nodes/n8n-nodes-base.amqp/ /integrations/nodes/n8n-nodes-base.amqp/
|
||||
/nodes/n8n-nodes-base.amqpTrigger/ /integrations/trigger-nodes/n8n-nodes-base.amqpTrigger/
|
||||
/nodes/n8n-nodes-base.amqptrigger/ /integrations/trigger-nodes/n8n-nodes-base.amqpTrigger/
|
||||
/nodes/n8n-nodes-base.apiTemplateIo/ /integrations/nodes/n8n-nodes-base.apiTemplateIo/
|
||||
/nodes/n8n-nodes-base.apitemplateio/ /integrations/nodes/n8n-nodes-base.apiTemplateIo/
|
||||
/nodes/n8n-nodes-base.asana/ /integrations/nodes/n8n-nodes-base.asana/
|
||||
/nodes/n8n-nodes-base.asanaTrigger/ /integrations/trigger-nodes/n8n-nodes-base.asanaTrigger/
|
||||
/nodes/n8n-nodes-base.asanatrigger/ /integrations/trigger-nodes/n8n-nodes-base.asanaTrigger/
|
||||
/nodes/n8n-nodes-base.automizy/ /integrations/nodes/n8n-nodes-base.automizy/
|
||||
/nodes/n8n-nodes-base.autopilot/ /integrations/nodes/n8n-nodes-base.autopilot/
|
||||
/nodes/n8n-nodes-base.autopilotTrigger/ /integrations/trigger-nodes/n8n-nodes-base.autopilotTrigger/
|
||||
/nodes/n8n-nodes-base.autopilottrigger/ /integrations/trigger-nodes/n8n-nodes-base.autopilotTrigger/
|
||||
/nodes/n8n-nodes-base.awsComprehend/ /integrations/nodes/n8n-nodes-base.awsComprehend/
|
||||
/nodes/n8n-nodes-base.awscomprehend/ /integrations/nodes/n8n-nodes-base.awsComprehend/
|
||||
/nodes/n8n-nodes-base.awsDynamoDb/ /integrations/nodes/n8n-nodes-base.awsDynamoDb/
|
||||
/nodes/n8n-nodes-base.awsdynamodb/ /integrations/nodes/n8n-nodes-base.awsDynamoDb/
|
||||
/nodes/n8n-nodes-base.awsLambda/ /integrations/nodes/n8n-nodes-base.awsLambda/
|
||||
/nodes/n8n-nodes-base.awslambda/ /integrations/nodes/n8n-nodes-base.awsLambda/
|
||||
/nodes/n8n-nodes-base.awsRekognition/ /integrations/nodes/n8n-nodes-base.awsRekognition/
|
||||
/nodes/n8n-nodes-base.awsrekognition/ /integrations/nodes/n8n-nodes-base.awsRekognition/
|
||||
/nodes/n8n-nodes-base.awsS3/ /integrations/nodes/n8n-nodes-base.awsS3/
|
||||
/nodes/n8n-nodes-base.aws3/ /integrations/nodes/n8n-nodes-base.awsS3/
|
||||
/nodes/n8n-nodes-base.awsSes/ /integrations/nodes/n8n-nodes-base.awsSes/
|
||||
/nodes/n8n-nodes-base.awsses/ /integrations/nodes/n8n-nodes-base.awsSes/
|
||||
/nodes/n8n-nodes-base.awsSns/ /integrations/nodes/n8n-nodes-base.awsSns/
|
||||
/nodes/n8n-nodes-base.awssns/ /integrations/nodes/n8n-nodes-base.awsSns/
|
||||
/nodes/n8n-nodes-base.awsSnsTrigger/ /integrations/trigger-nodes/n8n-nodes-base.awsSnsTrigger/
|
||||
/nodes/n8n-nodes-base.awssnstrigger/ /integrations/trigger-nodes/n8n-nodes-base.awsSnsTrigger/
|
||||
/nodes/n8n-nodes-base.awsSqs/ /integrations/nodes/n8n-nodes-base.awsSqs/
|
||||
/nodes/n8n-nodes-base.awssqs/ /integrations/nodes/n8n-nodes-base.awsSqs/
|
||||
/nodes/n8n-nodes-base.awsTextract/ /integrations/nodes/n8n-nodes-base.awsTextract/
|
||||
/nodes/n8n-nodes-base.awstextract/ /integrations/nodes/n8n-nodes-base.awsTextract/
|
||||
/nodes/n8n-nodes-base.awsTranscribe/ /integrations/nodes/n8n-nodes-base.awsTranscribe/
|
||||
/nodes/n8n-nodes-base.awstranscribe/ /integrations/nodes/n8n-nodes-base.awsTranscribe/
|
||||
/nodes/n8n-nodes-base.bambooHr/ /integrations/nodes/n8n-nodes-base.bambooHr/
|
||||
/nodes/n8n-nodes-base.bamboohr/ /integrations/nodes/n8n-nodes-base.bambooHr/
|
||||
/nodes/n8n-nodes-base.bannerbear/ /integrations/nodes/n8n-nodes-base.bannerbear/
|
||||
/nodes/n8n-nodes-base.baserow/ /integrations/nodes/n8n-nodes-base.baserow/
|
||||
/nodes/n8n-nodes-base.beeminder/ /integrations/nodes/n8n-nodes-base.beeminder/
|
||||
@ -90,7 +129,7 @@
|
||||
/nodes/n8n-nodes-base.cockpit/ /integrations/nodes/n8n-nodes-base.cockpit/
|
||||
/nodes/n8n-nodes-base.coda/ /integrations/nodes/n8n-nodes-base.coda/
|
||||
/nodes/n8n-nodes-base.coinGecko/ /integrations/nodes/n8n-nodes-base.coinGecko/
|
||||
/nodes/n8n-nodes-base.compression/ /integrations/nodes/core-nodes/n8n-nodes-base.compression/
|
||||
/nodes/n8n-nodes-base.compression/ /integrations/core-nodes/n8n-nodes-base.compression/
|
||||
/nodes/n8n-nodes-base.contentful/ /integrations/nodes/n8n-nodes-base.contentful/
|
||||
/nodes/n8n-nodes-base.convertKit/ /integrations/nodes/n8n-nodes-base.convertKit/
|
||||
/nodes/n8n-nodes-base.convertKitTrigger/ /integrations/trigger-nodes/n8n-nodes-base.convertKitTrigger/
|
||||
@ -98,11 +137,12 @@
|
||||
/nodes/n8n-nodes-base.copperTrigger/ /integrations/trigger-nodes/n8n-nodes-base.copperTrigger/
|
||||
/nodes/n8n-nodes-base.cortex/ /integrations/nodes/n8n-nodes-base.cortex/
|
||||
/nodes/n8n-nodes-base.crateDb/ /integrations/nodes/n8n-nodes-base.crateDb/
|
||||
/nodes/n8n-nodes-base.cron/ /integrations/nodes/core-nodes/n8n-nodes-base.cron/
|
||||
/nodes/n8n-nodes-base.crypto/ /integrations/nodes/core-nodes/n8n-nodes-base.crypto/
|
||||
/nodes/n8n-nodes-base.cron/ /integrations/core-nodes/n8n-nodes-base.cron/
|
||||
/nodes/n8n-nodes-base.crypto/ /integrations/core-nodes/n8n-nodes-base.crypto/
|
||||
/nodes/n8n-nodes-base.customerIo/ /integrations/nodes/n8n-nodes-base.customerIo/
|
||||
/nodes/n8n-nodes-base.customerIoTrigger/ /integrations/trigger-nodes/n8n-nodes-base.customerIoTrigger/
|
||||
/nodes/n8n-nodes-base.dateTime/ /integrations/nodes/core-nodes/n8n-nodes-base.dateTime/
|
||||
/nodes/n8n-nodes-base.dateTime/ /integrations/core-nodes/n8n-nodes-base.dateTime/
|
||||
/nodes/n8n-nodes-base.datetime/ /integrations/nodes/n8n-nodes-base.dateTime/
|
||||
/nodes/n8n-nodes-base.deepL/ /integrations/nodes/n8n-nodes-base.deepL/
|
||||
/nodes/n8n-nodes-base.demio/ /integrations/nodes/n8n-nodes-base.demio/
|
||||
/nodes/n8n-nodes-base.dhl/ /integrations/nodes/n8n-nodes-base.dhl/
|
||||
@ -112,19 +152,21 @@
|
||||
/nodes/n8n-nodes-base.drift/ /integrations/nodes/n8n-nodes-base.drift/
|
||||
/nodes/n8n-nodes-base.dropbox/ /integrations/nodes/n8n-nodes-base.dropbox/
|
||||
/nodes/n8n-nodes-base.dropcontact/ /integrations/nodes/n8n-nodes-base.dropcontact/
|
||||
/nodes/n8n-nodes-base.editImage/ /integrations/nodes/core-nodes/n8n-nodes-base.editImage/
|
||||
/nodes/n8n-nodes-base.editImage/ /integrations/core-nodes/n8n-nodes-base.editImage/
|
||||
/nodes/n8n-nodes-base.egoi/ /integrations/nodes/n8n-nodes-base.egoi/
|
||||
/nodes/n8n-nodes-base.elasticsearch/ /integrations/nodes/n8n-nodes-base.elasticsearch/
|
||||
/nodes/n8n-nodes-base.elasticSecurity/ /integrations/nodes/n8n-nodes-base.elasticSecurity/
|
||||
/nodes/n8n-nodes-base.emailReadImap/ /integrations/core-nodes/n8n-nodes-base.imapEmail/
|
||||
/nodes/n8n-nodes-base.emailSend/ /integrations/core-nodes/n8n-nodes-base.sendEmail/
|
||||
/nodes/n8n-nodes-base.emailsend/ /integrations/core-nodes/n8n-nodes-base.sendEmail/
|
||||
/nodes/n8n-nodes-base.emelia/ /integrations/nodes/n8n-nodes-base.emelia/
|
||||
/nodes/n8n-nodes-base.emeliaTrigger/ /integrations/trigger-nodes/n8n-nodes-base.emeliaTrigger/
|
||||
/nodes/n8n-nodes-base.erpNext/ /integrations/nodes/n8n-nodes-base.erpNext/
|
||||
/nodes/n8n-nodes-base.errorTrigger/ /integrations/core-nodes/n8n-nodes-base.errorTrigger/
|
||||
/nodes/n8n-nodes-base.eventbriteTrigger/ /integrations/trigger-nodes/n8n-nodes-base.eventbriteTrigger/
|
||||
/nodes/n8n-nodes-base.executeCommand/ /integrations/nodes/core-nodes/n8n-nodes-base.executeCommand/
|
||||
/nodes/n8n-nodes-base.executeWorkflow/ /integrations/nodes/core-nodes/n8n-nodes-base.executeWorkflow/
|
||||
/nodes/n8n-nodes-base.executeCommand/ /integrations/core-nodes/n8n-nodes-base.executeCommand/
|
||||
/nodes/n8n-nodes-base.executecommand/ /integrations/core-nodes/n8n-nodes-base.executeCommand/
|
||||
/nodes/n8n-nodes-base.executeWorkflow/ /integrations/core-nodes/n8n-nodes-base.executeWorkflow/
|
||||
/nodes/n8n-nodes-base.facebookGraphApi/ /integrations/nodes/n8n-nodes-base.facebookGraphAPI/
|
||||
/nodes/n8n-nodes-base.facebookTrigger/ /integrations/trigger-nodes/n8n-nodes-base.facebookTrigger/
|
||||
/nodes/n8n-nodes-base.figmaTrigger/ /integrations/trigger-nodes/n8n-nodes-base.figmaTrigger/
|
||||
@ -136,9 +178,10 @@
|
||||
/nodes/n8n-nodes-base.freshdesk/ /integrations/nodes/n8n-nodes-base.freshdesk/
|
||||
/nodes/n8n-nodes-base.freshservice/ /integrations/nodes/n8n-nodes-base.freshservice/
|
||||
/nodes/n8n-nodes-base.freshworksCrm/ /integrations/nodes/n8n-nodes-base.freshworksCrm/
|
||||
/nodes/n8n-nodes-base.ftp/ /integrations/nodes/core-nodes/n8n-nodes-base.ftp/
|
||||
/nodes/n8n-nodes-base.function/ /integrations/nodes/core-nodes/n8n-nodes-base.function/
|
||||
/nodes/n8n-nodes-base.functionItem/ /integrations/nodes/core-nodes/n8n-nodes-base.functionItem/
|
||||
/nodes/n8n-nodes-base.ftp/ /integrations/core-nodes/n8n-nodes-base.ftp/
|
||||
/nodes/n8n-nodes-base.function/ /integrations/core-nodes/n8n-nodes-base.function/
|
||||
/nodes/n8n-nodes-base.functionItem/ /integrations/core-nodes/n8n-nodes-base.functionItem/
|
||||
/nodes/n8n-nodes-base.functionitem/ /integrations/core-nodes/n8n-nodes-base.functionItem/
|
||||
/nodes/n8n-nodes-base.getResponse/ /integrations/nodes/n8n-nodes-base.getResponse/
|
||||
/nodes/n8n-nodes-base.getResponseTrigger/ /integrations/trigger-nodes/n8n-nodes-base.getResponseTrigger/
|
||||
/nodes/n8n-nodes-base.ghost/ /integrations/nodes/n8n-nodes-base.ghost/
|
||||
@ -162,7 +205,8 @@
|
||||
/nodes/n8n-nodes-base.googleFirebaseCloudFirestore/ /integrations/nodes/n8n-nodes-base.googleFirebaseCloudFirestore/
|
||||
/nodes/n8n-nodes-base.googleFirebaseRealtimeDatabase/ /integrations/nodes/n8n-nodes-base.googleFirebaseRealtimeDatabase/
|
||||
/nodes/n8n-nodes-base.googlePerspective/ /integrations/nodes/n8n-nodes-base.googlePerspective/
|
||||
/nodes/n8n-nodes-base.googleSheets/ /integrations/nodes/n8n-nodes-base.googleSheets/
|
||||
/nodes/n8n-nodes-base.googleSheets/ /integrations/nodes/n8n-nodes-base.googleSheets/
|
||||
/nodes/n8n-nodes-base.googlesheets/ /integrations/nodes/n8n-nodes-base.googleSheets/
|
||||
/nodes/n8n-nodes-base.googleSlides/ /integrations/nodes/n8n-nodes-base.googleSlides/
|
||||
/nodes/n8n-nodes-base.googleTasks/ /integrations/nodes/n8n-nodes-base.googleTasks/
|
||||
/nodes/n8n-nodes-base.googleTranslate/ /integrations/nodes/n8n-nodes-base.googleTranslate/
|
||||
@ -181,6 +225,7 @@
|
||||
/nodes/n8n-nodes-base.homeAssistant/ /integrations/nodes/n8n-nodes-base.homeAssistant/
|
||||
/nodes/n8n-nodes-base.htmlExtract/ /integrations/core-nodes/n8n-nodes-base.htmlExtract/
|
||||
/nodes/n8n-nodes-base.httpRequest/ /integrations/core-nodes/n8n-nodes-base.httpRequest/
|
||||
/nodes/n8n-nodes-base.httprequest/ /integrations/core-nodes/n8n-nodes-base.httpRequest/
|
||||
/nodes/n8n-nodes-base.hubspot/ /integrations/nodes/n8n-nodes-base.hubspot/
|
||||
/nodes/n8n-nodes-base.hubspotTrigger/ /integrations/trigger-nodes/n8n-nodes-base.hubspotTrigger/
|
||||
/nodes/n8n-nodes-base.humanticAi/ /integrations/nodes/n8n-nodes-base.humanticAi/
|
||||
@ -191,7 +236,8 @@
|
||||
/nodes/n8n-nodes-base.interval/ /integrations/core-nodes/n8n-nodes-base.interval/
|
||||
/nodes/n8n-nodes-base.invoiceNinja/ /integrations/nodes/n8n-nodes-base.invoiceNinja/
|
||||
/nodes/n8n-nodes-base.invoiceNinjaTrigger/ /integrations/trigger-nodes/n8n-nodes-base.invoiceNinjaTrigger/
|
||||
/nodes/n8n-nodes-base.itemLists/ /integrations/core-core-nodes/n8n-nodes-base.itemLists/
|
||||
/nodes/n8n-nodes-base.itemLists/ /integrations/core-nodes/n8n-nodes-base.itemLists/
|
||||
/nodes/n8n-nodes-base.itemlists/ /integrations/core-nodes/n8n-nodes-base.itemLists/
|
||||
/nodes/n8n-nodes-base.iterable/ /integrations/nodes/n8n-nodes-base.iterable/
|
||||
/nodes/n8n-nodes-base.jenkins/ /integrations/nodes/n8n-nodes-base.jenkins/
|
||||
/nodes/n8n-nodes-base.jira/ /integrations/nodes/n8n-nodes-base.jira/
|
||||
@ -246,7 +292,7 @@
|
||||
/nodes/n8n-nodes-base.mqttTrigger/ /integrations/trigger-nodes/n8n-nodes-base.mqttTrigger/
|
||||
/nodes/n8n-nodes-base.msg91/ /integrations/nodes/n8n-nodes-base.msg91/
|
||||
/nodes/n8n-nodes-base.mySql/ /integrations/nodes/n8n-nodes-base.mySql/
|
||||
/nodes/n8n-nodes-base.n8nTrainingCustomeMessenger/ /integrations/nodes/n8n-nodes-base.n8nTrainingCustomeMessenger/
|
||||
/nodes/n8n-nodes-base.n8nTrainingCustomerMessenger/ /integrations/nodes/n8n-nodes-base.n8nTrainingCustomerMessenger/
|
||||
/nodes/n8n-nodes-base.n8nTrainingCustomerDatastore/ /integrations/nodes/n8n-nodes-base.n8nTrainingCustomerDatastore/
|
||||
/nodes/n8n-nodes-base.n8nTrigger/ /integrations/core-nodes/n8n-nodes-base.n8nTrigger/
|
||||
/nodes/n8n-nodes-base.nasa/ /integrations/nodes/n8n-nodes-base.nasa/
|
||||
@ -319,6 +365,7 @@
|
||||
/nodes/n8n-nodes-base.sms77/ /integrations/nodes/n8n-nodes-base.sms77/
|
||||
/nodes/n8n-nodes-base.snowflake/ /integrations/nodes/n8n-nodes-base.snowflake/
|
||||
/nodes/n8n-nodes-base.splitInBatches/ /integrations/core-nodes/n8n-nodes-base.splitInBatches/
|
||||
/nodes/n8n-nodes-base.splitinbatches/ /integrations/core-nodes/n8n-nodes-base.splitInBatches/
|
||||
/nodes/n8n-nodes-base.splunk/ /integrations/nodes/n8n-nodes-base.splunk/
|
||||
/nodes/n8n-nodes-base.spontit/ /integrations/nodes/n8n-nodes-base.spontit/
|
||||
/nodes/n8n-nodes-base.spotify/ /integrations/nodes/n8n-nodes-base.spotify/
|
||||
@ -343,6 +390,7 @@
|
||||
/nodes/n8n-nodes-base.tapfiliate/ /integrations/nodes/n8n-nodes-base.tapfiliate/
|
||||
/nodes/n8n-nodes-base.telegram/ /integrations/nodes/n8n-nodes-base.telegram/
|
||||
/nodes/n8n-nodes-base.telegramTrigger/ /integrations/trigger-nodes/n8n-nodes-base.telegramTrigger/
|
||||
/nodes/n8n-nodes-base.telegramtrigger/ /integrations/trigger-nodes/n8n-nodes-base.telegramTrigger/
|
||||
/nodes/n8n-nodes-base.theHive/ /integrations/nodes/n8n-nodes-base.theHive/
|
||||
/nodes/n8n-nodes-base.theHiveTrigger/ /integrations/trigger-nodes/n8n-nodes-base.theHiveTrigger/
|
||||
/nodes/n8n-nodes-base.timescaleDb/ /integrations/nodes/n8n-nodes-base.timescaleDb/
|
||||
|
||||
@ -0,0 +1,581 @@
|
||||
# Creating Your First Node
|
||||
|
||||
Today, you will learn how to create your first node for n8n.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You have knowledge of:
|
||||
|
||||
- JavaScript/TypeScript
|
||||
- REST APIs
|
||||
- Expressions in n8n
|
||||
|
||||
Install the following tools:
|
||||
|
||||
- **Git:** You can find instructions on how to install Git [here](https://git-scm.com/downloads).
|
||||
- **Node.js and npm:** You can find instructions how to install both using nvm (Node Version Manager) [here](https://github.com/nvm-sh/nvm). The current minimum version is `14.15`. In case you already have Node.js and npm installed, you can check the current version with the following command:
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
**NOTE:** Use node version `14.x` and npm version `6.x`. If using npm version `7+`, you must enable legacy peer dependencies by setting: `npm config set legacy-peer-deps true`.
|
||||
|
||||
- **Lerna:** You can install lerna globally with the following command:
|
||||
```bash
|
||||
npm install --global lerna
|
||||
```
|
||||
|
||||
## Selecting the node
|
||||
|
||||
The first thing that we have to do is pick the service we want to create the node for. We will use [SendGrid](https://sendgrid.com/) as an example.
|
||||
|
||||
For the sake of brevity, we will only showcase how to add the functionality to create a contact. Since n8n's repository already has a SendGrid node, we will name this node **FriendGrid** to avoid conflicts.
|
||||
|
||||
## Cloning the repository
|
||||
|
||||
In GitHub, fork the [n8n repository](https://github.com/n8n-io/n8n). Clone it by running the following command in your terminal (don't forget to replace `<USERNAME>` with your GitHub username):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<USERNAME>/n8n.git && cd n8n
|
||||
```
|
||||
|
||||
n8n is built from four main packages:
|
||||
|
||||
- cli
|
||||
- core
|
||||
- editor-ui
|
||||
- nodes-base
|
||||
|
||||
All these packages are under the `/packages` folder in the main n8n folder. We will be working in the `nodes-base` folder as it contains everything related to nodes. Specifically, `/packages/nodes-base/nodes`, `packages/nodes-base/credentials`, and `packages/nodes-base/package.json`.
|
||||
|
||||
- The folder `nodes`, contains all the nodes in n8n.
|
||||
- The folder `credentials` contains all the credentials that the different nodes use. Each node can define multiple credentials. For example, OAuth2 or API Key. Each credential requires different parameters that the user will have to input. The credentials data that the user provides is stored in an encrypted format in n8n's database.
|
||||
- The file `package.json` contains all the npm packages that the nodes use. It also contains all the nodes and credentials that are loaded when n8n is started.
|
||||
|
||||

|
||||
|
||||
|
||||
## Creating the node
|
||||
|
||||
1. Go to `packages/nodes-base/nodes`.
|
||||
2. Create a folder called `FriendGrid` (the folder names are PascalCase).
|
||||
3. Within the FriendGrid folder, create a file called `FriendGrid.node.ts` (YourNodeName.node.ts).
|
||||
4. Download and add the FriendGrid [icon](https://symbols.getvecta.com/stencil_95/59_sendgrid-icon.5e86042b30.svg) to the folder. Name it `friendGrid.svg`.
|
||||
- The icon property has to be either a 60x60 pixels PNG or an SVG and must exist in the node’s folder.
|
||||
- An SVG is preferable. In case you have to use a PNG, make sure that it is compressed. A good tool for that is [tinypng](https://tinypng.com).
|
||||
- A good place to find company icons is [gilbarbara/logos](https://github.com/gilbarbara/logos/tree/master/logos).
|
||||
5. Paste the following code in the `FriendGrid.node.ts` file.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
export class FriendGrid implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'FriendGrid',
|
||||
name: 'friendGrid',
|
||||
icon: 'file:friendGrid.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Consume FriendGrid API',
|
||||
defaults: {
|
||||
name: 'FriendGrid',
|
||||
color: '#1A82e2',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
],
|
||||
properties: [
|
||||
// Node properties which the user gets displayed and
|
||||
// can change on the node.
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
return [[]];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your directory structure should now look like the following.
|
||||
|
||||

|
||||
|
||||
|
||||
## Adding the node to Editor UI
|
||||
|
||||
n8n uses the properties set in the property `description` to render the node in the Editor UI. These properties are `displayName`, `name`, `color`, `icon`, `description`, and `subtitle`.
|
||||
|
||||
Check the following figure to see how the properties affect the looks of the node.
|
||||
|
||||

|
||||
|
||||
**Note:** The property description conforms to [INodeTypeDescription](https://github.com/n8n-io/n8n/blob/f2666e92ffed2c3983d08e73b1e45a2bd516b90d/packages/workflow/src/Interfaces.ts#L425).
|
||||
|
||||
Let's see how the node looks in the UI by following these steps:
|
||||
|
||||
1. Go to `/packages/nodes-base/package.json`.
|
||||
2. Paste `"dist/nodes/FriendGrid/FriendGrid.node.js",` in the nodes array to register the node (in an alphabetical order).
|
||||
3. Go to the project's main folder (n8n) in the terminal and run the following commands (it can take a few minutes).
|
||||
- The first command installs all dependencies of all the modules and links them together.
|
||||
- The second command builds all the code.
|
||||
- The third command starts n8n in development mode.
|
||||
|
||||
```bash
|
||||
lerna bootstrap --hoist
|
||||
npm run build
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. Open your browser and go to [localhost:8080](http://localhost:8080/) and you should be able to see the Editor UI.
|
||||
5. Open the ***Create Node*** menu, type `FriendGrid`, and click on it to add the node to the Editor UI.
|
||||
|
||||
**Notes**
|
||||
- On startup, n8n will load all the nodes and credentials (more about credentials later) that are registered in `/packages/nodes-base/package.json`.
|
||||
- The property `description.name` uses camelCase.
|
||||
- The property `description.color` is the company branding's hexadecimal color. This is usually available on the company's website under style guide. In case the website does not include this information, there are other websites that help you get a company’s branding colors. For example, [brandpalettes.com](https://brandpalettes.com/).
|
||||
|
||||
|
||||
## Creating the UI for the node
|
||||
|
||||
Double-clicking on the FriendGrid node will open the Node Editor View. It will be empty since we haven't added any UI components yet. Luckily, n8n provides predefined JSON-based UI components that we can use to ask the user for different types of data.
|
||||
|
||||
SendGrid's [docs](https://sendgrid.com/docs/api-reference/) mention that to create a contact, we need to provide the following pieces of information:
|
||||
|
||||
- email - Required
|
||||
- first_name - Optional
|
||||
- last_name - Optional
|
||||
|
||||
There are more parameters that can be provided to create a contact in FriendGrid, but we will use only these three in this tutorial.
|
||||
|
||||
### Resources and operations
|
||||
|
||||
Now, n8n requires a couple of parameters as well:
|
||||
|
||||
- resource - Required
|
||||
- operation - Required
|
||||
|
||||
You can get the node to work without these two parameters, but these should be added for the sake of consistency with the other nodes. Resources and Operations help in organizing all the functionalities of a node. These ensure that all the functionalities of a node remain easily discoverable as the node grows.
|
||||
|
||||
- The resource value is always singular and its value is the name of the API resource that we want to use. Since we are working with contacts, the resource value would be `contact`.
|
||||
- The operation value is always singular as well and it is the name of the operation to perform over the resource. Since we are creating contacts, the operation value would be `create`.
|
||||
|
||||
You might say that you can “Add a contact” and you are right, but we try to use the same operations (create, delete, get, getAll and update) across all the nodes.
|
||||
|
||||
### Adding required fields
|
||||
|
||||
Let’s make the Node Editor View ask for these parameters:
|
||||
|
||||
1. Add the following under `description.properties` in `packages/nodes-base/nodes/FriendGrid/FriendGrid.node.ts`.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact',
|
||||
},
|
||||
],
|
||||
default: 'contact',
|
||||
required: true,
|
||||
description: 'Resource to consume',
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a contact',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
default:'',
|
||||
description:'Primary email for the contact',
|
||||
},
|
||||
```
|
||||
|
||||
2. Stop the current n8n process by pressing ctrl + c in the terminal in which you are running n8n.
|
||||
3. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
4. Go to [localhost:8080](http://localhost:8080/), refresh the page, and open the node again.
|
||||
|
||||
The node should now look like in the following image.
|
||||
|
||||

|
||||
|
||||
### Adding optional fields
|
||||
|
||||
We have given the node the possibility to ask for all the required parameters needed to create a contact. But, what about the optional parameters?
|
||||
|
||||
We can add them below the email parameter and set `required: false`. However, if we had more than two optional parameters, and most APIs do, the UI would become overwhelming for the users. To avoid this, we use a UI element named **collection** (usually called 'Additional Fields') to group all the optional parameters together.
|
||||
|
||||
1. Add the following below the `email` field in `packages/nodes-base/nodes/FriendGrid/FriendGrid.node.ts`.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
2. Stop the current n8n process by pressing ctrl + c in the terminal in which you are running n8n.
|
||||
3. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
4. Go to [localhost:8080](http://localhost:8080/), refresh the page, and open the node again.
|
||||
|
||||
The node should now look like in the following image.
|
||||
|
||||

|
||||
|
||||
Now all our optional fields are presented in the UI and can be set individually depending on the user’s use-case.
|
||||
|
||||
## Creating the UI for credentials
|
||||
|
||||
Most REST APIs use some sort of authentication mechanism. FriendGrid's REST API uses API Keys. The API Key informs them about who is making the request to their system and gives you access to all the functionality that the API provides. Given all the things it can do, this has to be treated as a sensitive piece of information and should be kept private.
|
||||
|
||||
n8n gives you the ability to ask for sensitive information using credentials. In the credentials, you can use all the generally available UI elements. Additionally, the data that is stored using the credentials would be encrypted before being saved to the database. In order to do that, n8n uses an encryption key.
|
||||
|
||||
With that in mind, let’s create the UI to ask for the user’s FriendGrid API Key. The process of creating and registering credentials is similar to that of creating and registering the node:
|
||||
|
||||
1. Go to `packages/nodes-base/credentials`.
|
||||
2. Within the credentials folder, create a file named `FriendGridApi.credentials.ts`.
|
||||
3. Paste the following code.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class FriendGridApi implements ICredentialType {
|
||||
name = 'friendGridApi';
|
||||
displayName = 'FriendGrid API';
|
||||
documentationUrl = 'friendGrid';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
4. Go to `/packages/nodes-base/package.json`.
|
||||
5. Paste `"dist/credentials/FriendGridApi.credentials.js",` in the credentials array to register the credentials (in an alphabetical order).
|
||||
6. Got to `packages/nodes-base/nodes/FriendGrid/FriendGrid.node.ts`.
|
||||
7. Associate the credentials with the node by adding the following to `description.credentials`.
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'friendGridApi',
|
||||
required: true,
|
||||
},
|
||||
```
|
||||
|
||||
8. Stop the current n8n process by pressing ctrl + c in the terminal in which you are running n8n.
|
||||
9. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
When you go to the Node Editor view, you should see the following.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## Mapping the UI fields to the API
|
||||
|
||||
With the UI that we added, we now have all the data that we need to make a request to the FriendGrid API and create contacts.
|
||||
|
||||
This is where the `execute` method comes into play. Every time the node is executed, this method will be run. Within this method, we can have access to the input items and to the parameters that the user set in the UI, including the credentials.
|
||||
To map the fields to the API, perform the following steps:
|
||||
|
||||
1. Go to `package/nodes-base/nodes/FriendGrid.node.ts`.
|
||||
2. Replace the current `execute` method with the following code.
|
||||
|
||||
```typescript
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
//Get credentials the user provided for this node
|
||||
const credentials = await this.getCredentials('friendGridApi') as IDataObject;
|
||||
|
||||
if (resource === 'contact') {
|
||||
if (operation === 'create') {
|
||||
// get email input
|
||||
const email = this.getNodeParameter('email', 0) as string;
|
||||
// get additional fields input
|
||||
const additionalFields = this.getNodeParameter('additionalFields', 0) as IDataObject;
|
||||
const data: IDataObject = {
|
||||
email,
|
||||
};
|
||||
|
||||
Object.assign(data, additionalFields);
|
||||
|
||||
//Make http request according to <https://sendgrid.com/docs/api-reference/>
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${credentials.apiKey}`,
|
||||
},
|
||||
method: 'PUT',
|
||||
body: {
|
||||
contacts: [
|
||||
data,
|
||||
],
|
||||
},
|
||||
uri: `https://api.sendgrid.com/v3/marketing/contacts`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
responseData = await this.helpers.request(options);
|
||||
}
|
||||
}
|
||||
|
||||
// Map data to n8n data
|
||||
return [this.helpers.returnJsonArray(responseData)];
|
||||
}
|
||||
```
|
||||
|
||||
3. Stop the current n8n process by pressing ctrl + c in the terminal in which you are running n8n.
|
||||
4. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
5. Enter the credentials (FriendGrid API Key), contact parameters, and execute the node.
|
||||
- Instructions to find the FriendGrid API Key can be found [here](https://sendgrid.com/docs/ui/account-and-settings/api-keys/).
|
||||
|
||||
If everything went well, you should see the following.
|
||||
|
||||

|
||||
|
||||
Now we can successfully create contacts in FriendGrid from n8n.
|
||||
|
||||
## Processing multiples items
|
||||
|
||||
In real life, you'll probably have a workflow with more than one node. Our current implementation does not play well with the other nodes. If the data is coming into our FriendGrid node from another node, and that outputs, for example, two contacts, our node will process just the first contact. We want our node to process as many items as it receives.
|
||||
|
||||
This is when the `this.getInputData()` function comes into play. Let's update our node so that it can process multiple items.
|
||||
|
||||
1. In the Editor UI, create a new workflow. Add a Function node and connect it to the Start node.
|
||||
2. Open the function node and replace the existing code with the following.
|
||||
|
||||
```javascript
|
||||
return [
|
||||
{
|
||||
json: {
|
||||
name: 'ricardo@n8n.io'
|
||||
}
|
||||
},
|
||||
{
|
||||
json: {
|
||||
name: 'hello@n8n.io'
|
||||
}
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
3. Execute the Function node. We're using the function node for testing, but you can think of it as any node that is returning “two people” (or more). These two people need to be added to FriendGrid as contacts.
|
||||
|
||||

|
||||
|
||||
4. Add a FriendGrid node to the workflow and connect it to the Function node. Add an expression in the ***Email*** field of the FriendGrid node and reference the ***name*** property that the Function node outputs.
|
||||
|
||||

|
||||
|
||||
5. Replace the existing `execute` method with the following:
|
||||
|
||||
```typescript
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
let responseData;
|
||||
const returnData = [];
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
//Get credentials the user provided for this node
|
||||
const credentials = await this.getCredentials('friendGridApi') as IDataObject;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (resource === 'contact') {
|
||||
if (operation === 'create') {
|
||||
// get email input
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
|
||||
// i = 1 returns ricardo@n8n.io
|
||||
// i = 2 returns hello@n8n.io
|
||||
|
||||
// get additional fields input
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const data: IDataObject = {
|
||||
email,
|
||||
};
|
||||
|
||||
Object.assign(data, additionalFields);
|
||||
|
||||
//Make http request according to <https://sendgrid.com/docs/api-reference/>
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${credentials.apiKey}`,
|
||||
},
|
||||
method: 'PUT',
|
||||
body: {
|
||||
contacts: [
|
||||
data,
|
||||
],
|
||||
},
|
||||
uri: `https://api.sendgrid.com/v3/marketing/contacts`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
responseData = await this.helpers.request(options);
|
||||
returnData.push(responseData);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map data to n8n data structure
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
```
|
||||
|
||||
6. Execute the workflow.
|
||||
|
||||
If you open the FriendGrid node, you should see the following.
|
||||
|
||||

|
||||
|
||||
As showcased above, both the items were processed. That’s how all nodes in n8n work (with a few exceptions). They will automatically iterate over all the items and process them.
|
||||
|
||||
Let’s go over the final version of the `execute` method. We are getting the items returned by the `this.getInputData()` function and iterating over all of them. Additionally, while doing so, we use the item index to get the correct parameter value using the function `this.getNodeParameters()`. For example, with the following input:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{
|
||||
json: {
|
||||
name: 'ricardo@n8n.io'
|
||||
}
|
||||
},
|
||||
{
|
||||
json: {
|
||||
name: 'hello@n8n.io'
|
||||
}
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
The `this.getNodeParameters(ParameterName, index)`function outputs the following:
|
||||
|
||||
| Index | Parameter Name | Output |
|
||||
|-------|----------------|-------------------|
|
||||
| 0 | email | ricardo@n8n.io |
|
||||
| 1 | email | hello@n8n.io |
|
||||
|
||||
We used the `this.helpers.request(options)` method to make the HTTP Request that creates the contact in FriendGrid. The FriendGrid endpoint returns something like this:
|
||||
|
||||
```javascript
|
||||
{
|
||||
job_id: "b82aca74-3640-4097-85ec-7801d833c2cb"
|
||||
}
|
||||
```
|
||||
|
||||
We then used the `this.helpers.returnJsonArray()` method to map the API’s output data to n8n's data structure. The node then ends up returning the data like the following:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{
|
||||
json:{
|
||||
job_id: "b82aca74-3640-4097-85ec-7801d833c2cb"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial, we implemented the "Create a Contact" functionality of the FriendGrid API. First of all, we made the node show up in the Editor UI and in the Create Node menu with FriendGrid's branding. Then, we added the fields necessary to create a contact in FriendGrid. We also added the credentials so that the API Key could be stored safely. Finally, we mapped all the parameters to the FriendGrid API.
|
||||
|
||||
This is just the tip of the iceberg. We built a regular node that consumes a REST API, but a regular node can do everything that can be done with Node.js. Aside from regular nodes you can also build Trigger nodes.
|
||||
|
||||
## Next steps
|
||||
|
||||
Once you have created the node and want to contribute to n8n, please check the [Node Review Checklist](/integrations/creating-nodes/code/node-review-checklist/). Make sure you complete the checklist before creating a pull request.
|
||||
@ -0,0 +1,488 @@
|
||||
# Creating n8n-nodes-module
|
||||
|
||||
In this guide, you’ll learn to create a custom n8n-nodes-module that can be installed separately alongside your n8n instance. The n8n-nodes-module is an npm package that contains the node. Your custom node will get loaded automatically when n8n starts.
|
||||
|
||||
Consider creating n8n-nodes-module if any of the following conditions satisfy your needs:
|
||||
- The nodes are only for yourself, your organization, or a small group of people.
|
||||
- The nodes require external dependencies that are not already available in n8n.
|
||||
|
||||
**NOTE:** n8n-nodes-module can only be installed in self-hosted n8n instances. This functionality is currently not available on n8n.cloud or the desktop app. There are plans to introduce this functionality in the future.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You may already be familiar with creating nodes in n8n. If you are unfamiliar with how to create n8n nodes, you can learn about it following the instructions mentioned in the [Creating Your First Node](https://docs.n8n.io/nodes/creating-nodes/create-node.html) tutorial.
|
||||
|
||||
Install the following tools:
|
||||
|
||||
- **Git:** You can find instructions on how to install Git [here](https://git-scm.com/downloads).
|
||||
- **Node.js and npm:** You can find instructions how to install both using nvm (Node Version Manager) [here](https://github.com/nvm-sh/nvm). The current minimum version is `14.15`. In case you already have Node.js and npm installed, you can check the current version with the following command:
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
|
||||
**NOTE:** Use node version `14.x` and npm version `6.x`. If using npm version `7+`, you must enable legacy peer dependencies by setting: `npm config set legacy-peer-deps true`.
|
||||
|
||||
- **Lerna:** You can install lerna globally with the following command:
|
||||
```bash
|
||||
npm i
|
||||
```
|
||||
|
||||
- Install n8n: Create a new folder and install n8n using the command:
|
||||
|
||||
```bash
|
||||
npm install n8n
|
||||
```
|
||||
|
||||
## Create custom n8n-nodes-module
|
||||
|
||||
You can create multiple n8n-nodes-modules. Each individual n8n-nodes-module should get created in a separate folder since they are different npm packages. A single n8n-nodes-module can contain multiple nodes. If you’re creating multiple nodes in the same module, as a best practice create each node in a separate folder.
|
||||
|
||||
In this tutorial, you will create an n8n-nodes-module for the OpenWeatherMap API. You will name it ***n8n-nodes-weather***.
|
||||
|
||||
To quickly get started, clone the example starter using the following command:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/n8n-io/n8n-nodes-starter.git n8n-nodes-weather.
|
||||
```
|
||||
|
||||
After the repo gets cloned, open the package.json file, and update the value of the name by replacing `n8n-nodes-starter` with `n8n-nodes-weather`.
|
||||
|
||||
**NOTE:** The name of the module has to start with `n8n-nodes-`.
|
||||
|
||||
Open the cloned repository in your code editor, and create a new folder called `Weather`, inside the ***nodes*** folder. Create `Weather.node.ts` file inside the Weather folder and paste the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { OptionsWithUri } from 'request';
|
||||
|
||||
export class Weather implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Weather',
|
||||
name: 'Weather',
|
||||
icon: 'fa:sun',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
description: 'Gets current and future weather information',
|
||||
defaults: {
|
||||
name: 'Weather',
|
||||
color: '#554455',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'weatherApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Current Weather',
|
||||
value: 'currentWeather',
|
||||
description: 'Returns the current weather data',
|
||||
},
|
||||
{
|
||||
name: '5 day Forecast',
|
||||
value: '5DayForecast',
|
||||
description: 'Returns the weather data for the next 5 days',
|
||||
},
|
||||
],
|
||||
default: 'currentWeather',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
{
|
||||
displayName: 'Format',
|
||||
name: 'format',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Imperial',
|
||||
value: 'imperial',
|
||||
description: 'Fahrenheit | miles/hour',
|
||||
},
|
||||
{
|
||||
name: 'Metric',
|
||||
value: 'metric',
|
||||
description: 'Celsius | meter/sec',
|
||||
},
|
||||
{
|
||||
name: 'Scientific',
|
||||
value: 'standard',
|
||||
description: 'Kelvin | meter/sec',
|
||||
},
|
||||
],
|
||||
default: 'metric',
|
||||
description: 'The format in which format the data should be returned.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// Location Information
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Location Selection',
|
||||
name: 'locationSelection',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'City Name',
|
||||
value: 'cityName',
|
||||
},
|
||||
{
|
||||
name: 'City ID',
|
||||
value: 'cityId',
|
||||
},
|
||||
{
|
||||
name: 'Coordinates',
|
||||
value: 'coordinates',
|
||||
},
|
||||
{
|
||||
name: 'Zip Code',
|
||||
value: 'zipCode',
|
||||
},
|
||||
],
|
||||
default: 'cityName',
|
||||
description: 'How to define the location for which to return the weather.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'cityName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'berlin,de',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
locationSelection: [
|
||||
'cityName',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the city to return the weather of.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'City ID',
|
||||
name: 'cityId',
|
||||
type: 'number',
|
||||
default: 160001123,
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
locationSelection: [
|
||||
'cityId',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The id of city to return the weather of. List can be downloaded here: http://bulk.openweathermap.org/sample/',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Latitude',
|
||||
name: 'latitude',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '13.39',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
locationSelection: [
|
||||
'coordinates',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The latitude of the location to return the weather of.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Longitude',
|
||||
name: 'longitude',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '52.52',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
locationSelection: [
|
||||
'coordinates',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The longitude of the location to return the weather of.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Zip Code',
|
||||
name: 'zipCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '10115,de',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
locationSelection: [
|
||||
'zipCode',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The id of city to return the weather of. List can be downloaded here: http://bulk.openweathermap.org/sample/',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'en',
|
||||
required: false,
|
||||
description: 'The two letter language code to get your output in (eg. en, de, ...).',
|
||||
},
|
||||
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
const credentials = await this.getCredentials('openWeatherMapApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
||||
}
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
let endpoint = '';
|
||||
let locationSelection;
|
||||
let language;
|
||||
|
||||
let qs: IDataObject;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
||||
try {
|
||||
|
||||
// Set base data
|
||||
qs = {
|
||||
APPID: credentials.accessToken,
|
||||
units: this.getNodeParameter('format', i) as string,
|
||||
};
|
||||
|
||||
// Get the location
|
||||
locationSelection = this.getNodeParameter('locationSelection', i) as string;
|
||||
if (locationSelection === 'cityName') {
|
||||
qs.q = this.getNodeParameter('cityName', i) as string;
|
||||
} else if (locationSelection === 'cityId') {
|
||||
qs.id = this.getNodeParameter('cityId', i) as number;
|
||||
} else if (locationSelection === 'coordinates') {
|
||||
qs.lat = this.getNodeParameter('latitude', i) as string;
|
||||
qs.lon = this.getNodeParameter('longitude', i) as string;
|
||||
} else if (locationSelection === 'zipCode') {
|
||||
qs.zip = this.getNodeParameter('zipCode', i) as string;
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), `The locationSelection "${locationSelection}" is not known!`);
|
||||
}
|
||||
|
||||
// Get the language
|
||||
language = this.getNodeParameter('language', i) as string;
|
||||
if (language) {
|
||||
qs.lang = language;
|
||||
}
|
||||
|
||||
if (operation === 'currentWeather') {
|
||||
// ----------------------------------
|
||||
// currentWeather
|
||||
// ----------------------------------
|
||||
|
||||
endpoint = 'weather';
|
||||
} else if (operation === '5DayForecast') {
|
||||
// ----------------------------------
|
||||
// 5DayForecast
|
||||
// ----------------------------------
|
||||
|
||||
endpoint = 'forecast';
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
|
||||
}
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
method: 'GET',
|
||||
qs,
|
||||
uri: `https://api.openweathermap.org/data/2.5/${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
let responseData;
|
||||
try {
|
||||
responseData = await this.helpers.request(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
|
||||
|
||||
returnData.push(responseData as IDataObject);
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({json:{ error: error.message }});
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The OpenWeatherMap API requires credentials to return results successfully. Create `WeatherApi.credentials.ts` file in the ***Credentials*** folder and paste the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
ICredentialType,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export class WeatherApi implements ICredentialType {
|
||||
name = 'weatherApi';
|
||||
displayName = 'Weather API';
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Access Token',
|
||||
name: 'accessToken',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
Add the newly created node and the credential to the package.json file. Add `"dist/nodes/Weather/Weather.node.js"` to the ***nodes*** array in the ***n8n*** object (`n8n.nodes`). Similarly, add `"dist/credentials/WeatherApi.credentials.js"` to the ***credentials*** array in the ***n8n*** object (`n8n.credentials`).
|
||||
|
||||
## Develop and test the module
|
||||
|
||||
Once you’ve created the n8n-nodes-module, you need to build the code and publish the package locally to test it. Run the following commands:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build the code
|
||||
npm run build
|
||||
|
||||
# "Publish" the package locally
|
||||
npm link
|
||||
```
|
||||
|
||||
**NOTE:** If you get permission errors, run the command as a root user with `sudo`, for example `sudo npm link`.
|
||||
|
||||
In the terminal, open the folder where you installed n8n. Run the following command to install the locally published module.
|
||||
|
||||
```bash
|
||||
# "Install" the above locally published module
|
||||
npm link n8n-nodes-weather
|
||||
```
|
||||
|
||||
Start n8n with the below command
|
||||
|
||||
```bash
|
||||
./node_modules/n8n/bin/n8n start
|
||||
```
|
||||
|
||||
You will now be able to test and use your newly created n8n-nodes-module.
|
||||
|
||||
## Publish the n8n-nodes-module
|
||||
|
||||
As mentioned, the n8n-nodes-module is an npm package. To make it available to others, you can publish it to the npm registry. Refer to the [Publishing unscoped public packages](https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages#publishing-unscoped-public-packages) guide to learn about publishing packages.
|
||||
|
||||
Following the steps mentioned above, you can create multiple nodes within a single n8n-nodes-module. You can also create nodes that require dependencies that are not present in n8n. When creating an n8n-nodes-module make sure that you follow the following guidelines:
|
||||
|
||||
- The name of the module should start with `n8n-nodes-`.
|
||||
- The `package.json` file has to contain a key `n8n` with the paths to nodes and credentials.
|
||||
- The module has to be installed alongside n8n.
|
||||
|
||||
## Use the n8n-nodes-module in production
|
||||
|
||||
Once you test and publish your n8n-nodes-module you would want to use it in your production environment.
|
||||
|
||||
If you’re running n8n via Docker, you will have to create a Docker image with the node module installed in n8n. Follow the steps below to create your Docker image:
|
||||
|
||||
1. Create a Dockerfile and paste the code from [this Dockerfile](https://github.com/n8n-io/n8n/blob/master/docker/images/n8n/Dockerfile).
|
||||
2. Add the following command in your Dockerfile before the font installation command.
|
||||
|
||||
```Dockerfile
|
||||
RUN cd /usr/local/lib/node_modules/n8n && npm install n8n-nodes-weather
|
||||
```
|
||||
|
||||
Your Dockerfile should be as follow:
|
||||
|
||||
```Dockerfile
|
||||
FROM node:14.15-alpine
|
||||
|
||||
# ARG N8N_VERSION
|
||||
|
||||
RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" ; exit 1; fi
|
||||
|
||||
# Update everything and install needed dependencies
|
||||
RUN apk add --update graphicsmagick tzdata git tini su-exec
|
||||
|
||||
# # Set a custom user to not have n8n run as root
|
||||
USER root
|
||||
|
||||
# Install n8n and the also temporary all the packages
|
||||
# it needs to build it correctly.
|
||||
RUN apk --update add --virtual build-dependencies python build-base ca-certificates && \
|
||||
npm_config_user=root npm install -g full-icu n8n && ls -a && \
|
||||
apk del build-dependencies \
|
||||
&& rm -rf /root /tmp/* /var/cache/apk/* && mkdir /root;
|
||||
|
||||
# Install n8n-nodes-weather module
|
||||
RUN cd /usr/local/lib/node_modules/n8n && npm install n8n-nodes-weather
|
||||
|
||||
# Install fonts
|
||||
RUN apk --no-cache add --virtual fonts msttcorefonts-installer fontconfig && \
|
||||
update-ms-fonts && \
|
||||
fc-cache -f && \
|
||||
apk del fonts && \
|
||||
find /usr/share/fonts/truetype/msttcorefonts/ -type l -exec unlink {} \; \
|
||||
&& rm -rf /root /tmp/* /var/cache/apk/* && mkdir /root
|
||||
|
||||
ENV NODE_ICU_DATA /usr/local/lib/node_modules/full-icu
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"]
|
||||
|
||||
EXPOSE 5678/tcp
|
||||
```
|
||||
|
||||
**NOTE:** Replace n8n-nodes-weather with the name of your n8n-nodes-module
|
||||
|
||||
3. Build your Docker image using the `docker build .` command.
|
||||
|
||||
You will now be able to use your n8n-nodes-module in Docker.
|
||||
|
||||
If you’re running either by installing it globally or via PM2, make sure that you install your n8n-nodes-module inside n8n. n8n will find the module and load it automatically.
|
||||
@ -0,0 +1,493 @@
|
||||
# Creating Your First Trigger Node
|
||||
|
||||
Today, you will learn how to create your first trigger node for n8n.
|
||||
|
||||
## Prerequisites
|
||||
You have knowledge of:
|
||||
|
||||
- JavaScript/TypeScript
|
||||
- REST APIs
|
||||
- [Webhooks](https://www.getvero.com/resources/webhooks/)
|
||||
- Expressions in n8n
|
||||
|
||||
Install the following tools:
|
||||
|
||||
- **Git:** You can find instructions on how to install Git [here](https://git-scm.com/downloads).
|
||||
- **Node.js and npm:** You can find instructions on how to install both using nvm (Node Version Manager) [here](https://github.com/nvm-sh/nvm). The current minimum version is `14.15`. In case you already have Node.js and npm installed, you can check the current version with the following command:
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
**NOTE:** Use node version `14.x` and npm version `6.x`
|
||||
|
||||
- **Lerna:** Install [lerna](https://lerna.js.org/) globally with the following command:
|
||||
```bash
|
||||
npm install --global lerna
|
||||
```
|
||||
|
||||
## Selecting the node
|
||||
|
||||
The first thing that we have to do is pick the service we want to create the node for. We will use [Autopilot](https://www.autopilothq.com/) as an example.
|
||||
|
||||
|
||||
Since n8n's repository already has a Autopilot Trigger node, we will name this node **Autofriend Trigger** to avoid conflicts.
|
||||
|
||||
## Cloning the repository
|
||||
|
||||
In GitHub, fork the [n8n repository](https://github.com/n8n-io/n8n). Clone it by running the following command in your terminal (don't forget to replace `<USERNAME>` with your GitHub username):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<USERNAME>/n8n.git && cd n8n
|
||||
```
|
||||
|
||||
n8n is built from four main packages:
|
||||
|
||||
- cli
|
||||
- core
|
||||
- editor-ui
|
||||
- nodes-base
|
||||
|
||||
All these packages are under the `/packages` folder in the main n8n folder. We will be working in the `nodes-base` folder as it contains everything related to nodes. Specifically, `/packages/nodes-base/nodes`, `packages/nodes-base/credentials`, and `packages/nodes-base/package.json`.
|
||||
|
||||
- The folder `nodes`, contains all the nodes in n8n.
|
||||
- The folder `credentials` contains all the credentials that the different nodes use. Each node can define multiple credentials. For example, OAuth2 or API Key. Each credential requires different parameters that the user will have to input. The credentials data that the user provides is stored in an encrypted format in n8n's database.
|
||||
- The file `package.json` contains all the npm packages that the nodes use. It also contains all the nodes and credentials that are loaded when n8n is started.
|
||||
|
||||

|
||||
|
||||
|
||||
## Creating the node
|
||||
|
||||
1. Go to `packages/nodes-base/nodes`.
|
||||
2. Create a folder called `Autofriend` (the folder names are PascalCase).
|
||||
3. Within the Autofriend folder, create a file called `AutofriendTrigger.node.ts` (YourNodeNameTrigger.node.ts).
|
||||
4. Download and add the Autofriend [icon](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Autopilot/autopilot.svg) to the folder. Name it `autopilot.svg`.
|
||||
- The icon property has to be either a 60x60 pixels PNG or an SVG and must exist in the node’s folder.
|
||||
- An SVG is preferable. In case you have to use a PNG, make sure that it is compressed. A good tool for that is [tinypng](https://tinypng.com).
|
||||
- A good place to find company icons is [gilbarbara/logos](https://github.com/gilbarbara/logos/tree/master/logos).
|
||||
5. Paste the following code in the `AutofriendTrigger.node.ts` file.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
/*
|
||||
import {
|
||||
autofriendApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
snakeCase,
|
||||
} from 'change-case';
|
||||
*/
|
||||
|
||||
|
||||
export class AutofriendTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Autofriend Trigger',
|
||||
name: 'autofriendTrigger',
|
||||
icon: 'file:autofriend.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["event"]}}',
|
||||
description: 'Handle Autofriend events via webhooks',
|
||||
defaults: {
|
||||
name: 'Autofriend Trigger',
|
||||
color: '#6ad7b9',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: ['main'],
|
||||
credentials: [],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [],
|
||||
};
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
return {
|
||||
workflowData: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your directory structure should now look like the following.
|
||||
|
||||

|
||||
|
||||
|
||||
## Adding the node to Editor UI
|
||||
|
||||
n8n uses the properties set in the property `description` to render the node in the Editor UI. These properties are `displayName`, `name`, `color`, `icon`, `description`, and `subtitle`.
|
||||
|
||||
Check the following figure to see how the properties affect the looks of the node.
|
||||
|
||||

|
||||
|
||||
**Note:** The property description conforms to [INodeTypeDescription](https://github.com/n8n-io/n8n/blob/f2666e92ffed2c3983d08e73b1e45a2bd516b90d/packages/workflow/src/Interfaces.ts#L425).
|
||||
|
||||
Let's see how the node looks in the UI by following these steps:
|
||||
|
||||
1. Go to `/packages/nodes-base/package.json`.
|
||||
2. Paste `"dist/nodes/Autofriend/AutofriendTrigger.node.js",` in the nodes array to register the node (in an alphabetical order).
|
||||
3. Go to the project's main folder (n8n) in the terminal and run the following commands (it can take a few minutes).
|
||||
- The first command installs all dependencies of all the modules and links them together.
|
||||
- The second command builds all the code.
|
||||
- The third command starts n8n in development mode.
|
||||
|
||||
```bash
|
||||
lerna bootstrap --hoist
|
||||
npm run build
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. Open your browser and go to [localhost:8080](http://localhost:8080/) and you should be able to see the Editor UI.
|
||||
5. Open the ***Create Node*** menu, select the ***Trigger*** tab, type `Autofriend`, and click on it to add the node to the Editor UI.
|
||||
|
||||
**Notes**
|
||||
|
||||
- On startup, n8n will load all the nodes and credentials (more about credentials later) that are registered in `/packages/nodes-base/package.json`.
|
||||
- The property `description.name` uses camelCase.
|
||||
- The property `description.color` is the company's branding color in hexadecimal. In case the website does not include this information, there are other websites that help you get a company’s branding colors. For example, [brandpalettes.com](https://brandpalettes.com/).
|
||||
|
||||
|
||||
## Creating the UI for the node
|
||||
|
||||
Double-clicking on the Autofriend Trigger node will open the Node Editor View. It will be empty since we haven't added any UI components yet. Luckily, n8n provides predefined JSON-based UI components that we can use to ask the user for different types of data.
|
||||
|
||||
Autopilots's [docs](https://autopilot.docs.apiary.io/#reference/rest-hooks/register-rest-hook/register-a-rest-hook) mention that to create a hook, we need to provide the following pieces of information:
|
||||
|
||||
- event - Required
|
||||
- target_url - Required
|
||||
|
||||
In the `event` parameter, we provide the name of the event for which we want to be notified. For example, `contact_added`. As the name implies, by providing `contact_added` as the event, we will be notified every time a contact is added to Autofriend.
|
||||
|
||||
In the `target_url` parameter, we provide the URL where Autofriend will notify us when the event defined in the event parameter takes place. We don't need to ask the user for this parameter as n8n provides us with a method to obtain it.
|
||||
|
||||
|
||||
### Adding the fields
|
||||
|
||||
Let’s make the Node Editor View ask for these parameters:
|
||||
1. Add the following under `description.properties` in `packages/nodes-base/nodes/Autofriend/AutofriendTrigger.node.ts.`.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Event',
|
||||
name: 'event',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Contact Added',
|
||||
value: 'contactAdded',
|
||||
},
|
||||
{
|
||||
name: 'Contact Added To List',
|
||||
value: 'contactAddedToList',
|
||||
},
|
||||
{
|
||||
name: 'Contact Entered Segment',
|
||||
value: 'contactEnteredSegment',
|
||||
},
|
||||
{
|
||||
name: 'Contact Left Segment',
|
||||
value: 'contactLeftSegment',
|
||||
},
|
||||
{
|
||||
name: 'Contact Removed From List',
|
||||
value: 'contactRemovedFromList',
|
||||
},
|
||||
{
|
||||
name: 'Contact Unsubscribed',
|
||||
value: 'contactUnsubscribed',
|
||||
},
|
||||
{
|
||||
name: 'Contact Updated',
|
||||
value: 'contactUpdated',
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
2. Stop the current n8n process by pressing `ctrl + c` in the terminal in which you are running n8n.
|
||||
3. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
4. Go to [localhost:8080](http://localhost:8080/), refresh the page, and open the node again.
|
||||
|
||||
The node should now look like in the following image.
|
||||
|
||||

|
||||
|
||||
|
||||
## Creating the UI for credentials
|
||||
|
||||
Most REST APIs use some sort of authentication mechanism. Autofriend's REST API uses API Keys. The API Key informs them about who is making the request to their system and gives you access to all the functionality that the API provides. Given all the things it can do, this has to be treated as a sensitive piece of information and should be kept private.
|
||||
|
||||
n8n gives you the ability to ask for sensitive information using credentials. In the credentials, you can use all the generally available UI elements. Additionally, the data that is stored using the credentials would be encrypted before being saved to the database. In order to do that, n8n uses an encryption key.
|
||||
|
||||
With that in mind, let’s create the UI to ask for the user’s Autofriend API Key. The process of creating and registering credentials is similar to that of creating and registering the node:
|
||||
|
||||
1. Go to `packages/nodes-base/credentials`.
|
||||
2. Within the credentials folder, create a file named `AutofriendApi.credentials.ts`.
|
||||
3. Paste the following code.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class AutofriendApi implements ICredentialType {
|
||||
name = 'autofriendApi';
|
||||
displayName = 'Autofriend API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
4. Go to `/packages/nodes-base/package.json`.
|
||||
5. Paste `"dist/credentials/AutofriendApi.credentials.js",` in the credentials array to register the credentials (in an alphabetical order).
|
||||
6. Got to `packages/nodes-base/nodes/Autofriend/AutofriendTrigger.node.ts`.
|
||||
7. Associate the credentials with the node by adding the following to `description.credentials`.
|
||||
|
||||
```typescript
|
||||
credentials: [
|
||||
{
|
||||
name: 'autofriendApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
8. Stop the current n8n process by pressing `ctrl + c` in the terminal in which you are running n8n.
|
||||
9. Run again, by entering the following in the terminal.
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
When you go to the Node Editor view, you should see the following.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## Understanding the life cycle for the webhook method
|
||||
|
||||
When a Trigger node is executed either in test or production mode, the following happens:
|
||||
|
||||
### n8n persists all the webhooks defined in description.webhooks
|
||||
|
||||
The persisted data will be used later to verify if the incoming requests to the n8n’s webhook endpoint are valid.
|
||||
|
||||
The property webhooks implements the interface **IWebhookDescription**. The interface has four properties.
|
||||
|
||||
1. **name:** The property name where n8n will look for the life cycle methods.
|
||||
2. **httpMethod:** The HTTP method.
|
||||
3. **responseMode:** When the trigger will respond. When developing a trigger node, this property must be set to `onReceived`.
|
||||
4. **path:** The path added to the base URL.
|
||||
|
||||
For example, for a Trigger node with the following `webhooks` property, n8n will create the following webhooks URLs.
|
||||
|
||||
```typescript
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMethod: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
- **Test:** POST {{WEBHOOK_TUNNEL_URL || localhost}}/webhook-test/{{uuid}}/{{path}}
|
||||
- **Production:** POST {{WEBHOOK_TUNNEL_URL || localhost}}/webhook/{{uuid}}/{{path}}
|
||||
|
||||
|
||||
These URLs can be found in the node under the `Webhook URLs` label.
|
||||
|
||||
These webhook URLs will be used as the notification URL (also known as the callback URL or target URL) when creating the webhook in the external system.
|
||||
|
||||
**Note:** In test mode, the webhooks are persisted in memory. In production mode, they are persisted in the database.
|
||||
|
||||
|
||||
### n8n executes the life cycle methods
|
||||
|
||||
The life cycle methods allow us to create, delete, and check if the webhook exists in the external system.
|
||||
|
||||
**Methods**
|
||||
|
||||
- `checkExist`: This is the first method that gets called. It checks if the webhook with the current path is already registered in the external system or not. If the webhook is already registered, n8n persists the webhook ID. If the webhook is not registered with the external system, the `create` method gets executed.
|
||||
- `create`: This method gets called if the `checkExist` method returns false (if the webhook with the current path does not exist in the external system). This method registers the webhook in the external system and stores the webhook ID in n8n.
|
||||
- `delete`: This method gets called when the trigger is either stopped manually or when the workflow is deactivated. It uses the ID previously persisted by either the create or the checkExist method to delete the webhook from the external system.
|
||||
|
||||

|
||||
|
||||
|
||||
### Wait for new events to trigger the workflow
|
||||
|
||||
Every time the external system notifies us about a change, by making an HTTP Request to the URL we previously registered in the `create` method, the `execute` method is called. Within this method, we have access to the request object and everything it contains. For example, body, headers, querystring, etc. The data the method returns is the data we want the rest of the workflow to have access to.
|
||||
|
||||
Let’s see how this would look for our current use-case:
|
||||
|
||||
1. Go to `packages/nodes-base/nodes/Autofriend`, create a file named `GenericFunctions.ts`, and paste the following code.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function autofriendApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const credentials = await this.getCredentials('autofriendApi') as IDataObject;
|
||||
|
||||
const apiKey = credentials.apiKey;
|
||||
|
||||
const endpoint = 'https://api2.autopilothq.com/v1';
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
autopilotapikey: apiKey,
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
uri: uri || `${endpoint}${resource}`,
|
||||
json: true,
|
||||
};
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
if (!Object.keys(query).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
const errorMessage = error.response.body.message || error.response.body.description || error.message;
|
||||
throw new Error(`Autopilot error response [${error.statusCode}]: ${errorMessage}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Go to `packages/nodes-base/nodes/AutofriendTrigger.node.ts` and add the following code after the property description.
|
||||
|
||||
```typescript
|
||||
// @ts-ignore
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
const { hooks: webhooks } = await autofriendApiRequest.call(this, 'GET', '/hooks');
|
||||
for (const webhook of webhooks) {
|
||||
if (webhook.target_url === webhookUrl && webhook.event === snakeCase(event)) {
|
||||
webhookData.webhookId = webhook.hook_id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
const body: IDataObject = {
|
||||
event: snakeCase(event),
|
||||
target_url: webhookUrl,
|
||||
};
|
||||
const webhook = await autofriendApiRequest.call(this, 'POST', '/hook', body);
|
||||
webhookData.webhookId = webhook.hook_id;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
try {
|
||||
await autofriendApiRequest.call(this, 'DELETE', `/hook/${webhookData.webhookId}`);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
delete webhookData.webhookId;
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
3. Replace the `webhook` function with the following.
|
||||
```typescript
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const req = this.getRequestObject();
|
||||
return {
|
||||
workflowData: [
|
||||
this.helpers.returnJsonArray(req.body),
|
||||
],
|
||||
};
|
||||
```
|
||||
4. In the same file, uncomment the code snippet on the top to import `autoFriendApiRequest` and `snakeCase`.
|
||||
5. Stop the current n8n process by pressing `ctrl + c` in the terminal where you are running n8n.
|
||||
6. Run the project using a tunnel by entering `./packages/cli/bin/n8n start --tunnel` in the terminal. Access the n8n Editor UI at [localhost:5678](http://localhost:5678/workflow).
|
||||
7. Enter the API key in the credentials. Instructions to find the API Key can be found [here](../credentials/autopilot).
|
||||
8. Go to the workflow editor, save your workflow, and execute the node.
|
||||
|
||||

|
||||
|
||||
9. Log into Autopilot and update a contact. Keep in mind that this should be done within two minutes after you executed the node. After that time frame, the webhook will be unregistered automatically and you will not be able to receive the event. If it takes you longer than that, please execute the node and update the contact again.
|
||||
|
||||

|
||||
|
||||
The trigger node is now receiving events. Sometimes it might take a bit longer for the payload to arrive.
|
||||
|
||||
You probably noticed that this time we did not run the project using `npm run dev`, but instead using `./packages/cli/bin/n8n start --tunnel`.
|
||||
|
||||
Since our server is running locally, we need a tool that lets us proxy all requests to our local machine so that n8n receives and handles the events from the external service (Autopilot). This gets achieved using a tunnel. The details on how a tunnel works are out of the scope of this tutorial. If you want to know about it, you can check this [link](http://localtunnel.github.io/www/). Keep in mind that the tunnel is meant for development purposes only and should not be used in production.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial, we implemented one functionality of the Autofriend webhook API. We made the node show up in the Editor UI and in the Create Node menu with Autofriend's branding. Then, we added the fields necessary to create a webhook in the external service. We also added the credentials so that the API Key could be stored safely. Finally, we mapped all the parameters to the Autofriend API.
|
||||
|
||||
## Next steps
|
||||
|
||||
Once you have created the node and want to contribute to n8n, please check the [Node Review Checklist](/integrations/creating-nodes/code/node-review-checklist/). Make sure you complete the checklist before creating a pull request.
|
||||
@ -0,0 +1,96 @@
|
||||
|
||||
# Making HTTP Requests
|
||||
|
||||
While creating nodes it is very commonn to call external APIs or make HTTP requests to other services.
|
||||
|
||||
This plays a major role during node development, maintenance, and improvements.
|
||||
|
||||
We provide a very flexible helper for making HTTP requests that abstracts away most of the complexity with a simple to use interface.
|
||||
|
||||
## How to use
|
||||
|
||||
In the node code, inside the `execute` function you can easily call:
|
||||
|
||||
```typescript
|
||||
const response = await this.helpers.httpRequest(options);
|
||||
```
|
||||
|
||||
Where `options` is an object in this format:
|
||||
|
||||
```typescript
|
||||
{
|
||||
url: string;
|
||||
headers?: object;
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD';
|
||||
body?: FormData | Array | string | number | object | Buffer | URLSearchParams;
|
||||
qs?: object;
|
||||
arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma';
|
||||
auth?: {
|
||||
username: string,
|
||||
password: string,
|
||||
};
|
||||
disableFollowRedirect?: boolean;
|
||||
encoding?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
|
||||
skipSslCertificateValidation?: boolean;
|
||||
returnFullResponse?: boolean;
|
||||
proxy?: {
|
||||
host: string;
|
||||
port: string | number;
|
||||
auth?: {
|
||||
username: string;
|
||||
password: string;
|
||||
},
|
||||
protocol?: string;
|
||||
};
|
||||
timeout?: number;
|
||||
json?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Where `url` is the only mandatory field. The default method is `GET`.
|
||||
|
||||
Some notes about the possible fields:
|
||||
|
||||
- **body**: You can use a regular Javascript Object for JSON payload, a Buffer for file uploads, an instance of FormData for `multipart/form-data` and `URLSearchParams` for `application/x-www-form-urlencoded`.
|
||||
- **headers**: A simple key-value pair.
|
||||
* If `body` is an instance of `FormData` then `content-type: multipart/form-data` is injected automatically.
|
||||
* If `body` is an instance of `URLSearchParams`, then `content-type: application/x-www-form-urlencoded` is added.
|
||||
* To override this behavior, you can set any `content-type` header you wish and it won't be overridden.
|
||||
- **arrayFormat**: If your query string contains an array of data, let's say `const qs = {IDs: [15,17]}`, the values set to `arrayFormat` define how it will be sent.
|
||||
* `indices` (default): `{ a: ['b', 'c'] }` will be formatted as `a[0]=b&a[1]=c`
|
||||
* `brackets`: `{ a: ['b', 'c'] }` will be formatted as `a[]=b&a[]=c`
|
||||
* `repeat`: `{ a: ['b', 'c'] }` will be formatted as `a=b&a=c`
|
||||
* `comma`: `{ a: ['b', 'c'] }` will be formatted as `a=b,c`
|
||||
- **auth**: Used for Basic auth. Provide `username` and `password`.
|
||||
- **disableFollowRedirect**: By default, we'll follow redirects. You can set this to false to prevent this from happening
|
||||
- **skipSslCertificateValidation**: Used for calling HTTPS services without proper certificate
|
||||
- **returnFullResponse**: Instead of returning only the body, returns an object with more data in the following format: `{body: body, headers: object, statusCode: 200, statusMessage: 'OK'}`
|
||||
- **encoding**: We usually detect the content type correctly but you can specify `arrayBuffer` to receive a Buffer you can read from and interact with.
|
||||
|
||||
## Deprecation of the previous helper
|
||||
|
||||
The previous helper implementation using `this.helpers.request(options)` used and exposed the `request-promise` library which was deprecated.
|
||||
|
||||
In an effort to keep maximum compatibility, we made a transparent conversion to another library called `axios`.
|
||||
|
||||
If you are having issues, please report them in our [Community Forums](https://community.n8n.io/) or on [Github](https://github.com/n8n-io/n8n/issues).
|
||||
|
||||
Also, you can temporarily enable n8n to use the deprecated library by setting the environment variable `N8N_USE_DEPRECATED_REQUEST_LIB=true`.
|
||||
|
||||
**Please note:** This behavior is permanent and we will be removing the `request-promise` library entirely in the future so please report any errors you have so we can fix them.
|
||||
|
||||
## Migration guide to the new helper
|
||||
|
||||
As mentioned above, the previous helper is deprecated and will be replaced in the future. The new helper is much more robust, library agnostic, and easier to use.
|
||||
|
||||
New nodes should all use the new helper, and if you have built custom nodes we strongly suggest you migrate to the new helper. Here are the main considerations when migrating:
|
||||
|
||||
- Only `url` is accepted. Previously `uri` was also accepted
|
||||
- `encoding: null` now must be `encoding: arrayBuffer`
|
||||
- `rejectUnauthorized: false` is now `skipSslCertificateValidation: true`
|
||||
- Use `body` according to `content-type` headers to clarify what is being sent
|
||||
- `resolveWithFullResponse` is now `returnFullResponse` and has similar behavior
|
||||
|
||||
## Example
|
||||
|
||||
For an example, please check the [Mattermost node](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts).
|
||||
@ -0,0 +1 @@
|
||||
# Creating nodes: programmatic approach
|
||||
@ -0,0 +1,93 @@
|
||||
# Using the Node Dev CLI
|
||||
|
||||
Using the Node Dev CLI makes sense if you do not want to ever share the node that you create. For example, for internal systems or something very specific to your internal tooling. Also, the CLI only works if there are no additional dependencies required by the node as it does not support installing additional node modules.
|
||||
|
||||
If that is not the case, it is best to do follow the [creating your first node](/integrations/creating-nodes/code/create-first-node/) tutorial or create your own custom node-package.
|
||||
|
||||
## Create the first basic node
|
||||
|
||||
1. Install the n8n-node-dev CLI: `npm install -g n8n-node-dev`
|
||||
1. Create and go into the newly created folder in which you want to keep the code of the node
|
||||
1. Use CLI to create boilerplate node code: `n8n-node-dev new`
|
||||
1. Answer the questions (the “Execute” node type is the regular node type that you probably want to create).
|
||||
It will then create the node in the current folder.
|
||||
1. Program… Add the functionality to the node
|
||||
1. Build the node and copy to correct location: `n8n-node-dev build`
|
||||
That command will build the JavaScript version of the node from the TypeScript code and copy it to the user folder where custom nodes get read from `~/.n8n/custom/`
|
||||
1. Restart n8n and refresh the window so that the new node gets displayed
|
||||
|
||||
|
||||
## Create own custom n8n-nodes-module
|
||||
|
||||
If you want to create multiple custom nodes which are either:
|
||||
|
||||
- Only for yourself/your company
|
||||
- Are only useful for a small number of people
|
||||
- Require many or large dependencies
|
||||
|
||||
!!! note
|
||||
To learn how to develop and test n8n-nodes-module, refer to the [Create n8n-nodes-module](/integrations/creating-nodes/code/create-n8n-nodes-module/) documentation.
|
||||
|
||||
|
||||
It is best to create your own `n8n-nodes-module` which can be installed separately.
|
||||
That is an npm package that contains the nodes and is set up in a way
|
||||
that n8n can automatically find and load them on startup.
|
||||
|
||||
When creating such a module the following rules have to be followed that n8n
|
||||
can automatically find the nodes in the module:
|
||||
|
||||
- The name of the module has to start with `n8n-nodes-`
|
||||
- The `package.json` file has to contain a key `n8n` with the paths to nodes and credentials
|
||||
- The module has to be installed alongside n8n
|
||||
|
||||
An example starter module which contains one node and credentials and implements
|
||||
the above can be found here:
|
||||
|
||||
[https://github.com/n8n-io/n8n-nodes-starter](https://github.com/n8n-io/n8n-nodes-starter)
|
||||
|
||||
|
||||
### Setup to use n8n-nodes-module
|
||||
|
||||
To use a custom `n8n-nodes-module`, it needs to be installed alongside n8n.
|
||||
For example like this:
|
||||
|
||||
```bash
|
||||
# Create folder for n8n installation
|
||||
mkdir my-n8n
|
||||
cd my-n8n
|
||||
|
||||
# Install n8n
|
||||
npm install n8n
|
||||
|
||||
# Install custom nodes module
|
||||
npm install n8n-nodes-my-custom-nodes
|
||||
|
||||
# Start n8n
|
||||
n8n
|
||||
```
|
||||
|
||||
|
||||
### Development/Testing of custom n8n-nodes-module
|
||||
|
||||
This works in the same way as for any other npm module.
|
||||
|
||||
Execute in the folder which contains the code of the custom `n8n-nodes-module`
|
||||
which should be loaded with n8n:
|
||||
|
||||
```bash
|
||||
# Build the code
|
||||
npm run build
|
||||
|
||||
# "Publish" the package locally
|
||||
npm link
|
||||
```
|
||||
|
||||
Then in the folder in which n8n is installed:
|
||||
|
||||
```bash
|
||||
# "Install" the above locally published module
|
||||
npm link n8n-nodes-my-custom-nodes
|
||||
|
||||
# Start n8n
|
||||
n8n
|
||||
```
|
||||
@ -0,0 +1,83 @@
|
||||
# Nodelinter
|
||||
|
||||
[Nodelinter](https://github.com/n8n-io/nodelinter) is an extensible static analysis tool for checking your n8n node files to ensure n8n recommended best practices are followed when developing new nodes.
|
||||
|
||||
This includes rules for:
|
||||
* Alphabetization of node parameters and options
|
||||
* Casing for display names and descriptions
|
||||
* Default values per parameter type
|
||||
* Required and optional key-value pairs
|
||||
|
||||
See the full linting list [here](https://github.com/n8n-io/nodelinter/blob/master/src/lintings.ts) for more details.
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
Nodelinter is a dependency of the `nodes-base` package and available upon [installing](/hosting/installation/) n8n.
|
||||
|
||||
You can run Nodelinter from the `packages/nodes-base` directory as follows:
|
||||
|
||||
```sh
|
||||
npm run nodelinter -- --<options>
|
||||
```
|
||||
|
||||
!!! note "Keep in mind"
|
||||
Be sure to run Nodelinter and verify your code before submitting a pull request.
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description | Example |
|
||||
| ----------------- | -------------------------------------------------- | -------- |
|
||||
| `--target` | Path of the file or directory to lint | Lint a single file:<br>`--target=nodes/Stripe/Stripe.node.ts` <br><br>Lint all files in a directory:<br>`--target=nodes/Stripe` |
|
||||
| `--config` | Path of the [custom config](#custom-config) to use | `--config=/Users/john/Documents/myConfig.json` |
|
||||
| `--patterns` | Lintable file patterns | `--patterns:.node.ts,Description.ts` |
|
||||
| `--print` | Print output to JSON<br><br>A custom filename can optionally be specified. | `--print=myLintOutput` |
|
||||
| `--errors-only` | Enable error logs only |
|
||||
| `--warnings-only` | Enable warning logs only |
|
||||
| `--infos-only` | Enable info logs only |
|
||||
|
||||
### Custom config
|
||||
|
||||
The Nodelinter [default config](https://github.com/n8n-io/nodelinter/blob/master/src/defaultConfig.ts) can be overridden to, for example, change the areas and issues linted.
|
||||
|
||||
To do so create a JSON file containing the key values you want to override. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"target": "/Users/john/n8n/packages/nodes-base/nodes/Notion/Notion.node.ts",
|
||||
"patterns": [".node.ts"],
|
||||
"sortMethod": "lineNumber",
|
||||
"lintings": {
|
||||
"PARAM_DESCRIPTION_MISSING_WHERE_OPTIONAL": {
|
||||
"enabled": false
|
||||
},
|
||||
"NAME_WITH_NO_CAMELCASE": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Name this file `nodelinter.config.json` and place it anywhere in your `nodes-base` directory and it will be automatically detected. Alternatively, you can specify the custom config file and location using the `--config` option.
|
||||
|
||||
## Lint exceptions
|
||||
|
||||
You can create exceptions for individual lines of code from any or all linting rules as follows:
|
||||
|
||||
```
|
||||
// nodelinter-ignore-next-line <LINTING_NAME> <LINTING_NAME>
|
||||
```
|
||||
|
||||
If no specific linting name is provided that line will be excepted from all rules. For example:
|
||||
|
||||
Exception for one rule:
|
||||
```
|
||||
// nodelinter-ignore-next-line PARAM_DESCRIPTION_WITH_EXCESS_WHITESPACE
|
||||
description: 'Time zone used in the response. The default is the time zone of the calendar.',
|
||||
```
|
||||
|
||||
Exception for all rules:
|
||||
```
|
||||
// nodelinter-ignore-next-line
|
||||
description: 'Time zone used in the response. The default is the time zone of the calendar.',
|
||||
```
|
||||
@ -0,0 +1,73 @@
|
||||
# Node Review Checklist
|
||||
|
||||
If you want to create a new node for a service - that's great, thank you! We recommend you take a look at the [existing nodes](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes) to get an idea of how your code should look and work like.
|
||||
|
||||
There are several things to keep in mind when creating the node. To help you, we prepared a checklist that covers the requirements for creating nodes, from preparation to submission.
|
||||
|
||||
Make sure you tick the boxes below before submitting a node for review, as this will help our team review your PR easier and faster.
|
||||
|
||||
## Preparation
|
||||
<input type="checkbox"> Set up your editor for code formatting (indentation, new lines, linting). If you use Visual Studio Code, you can use the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-tslint-plugin">TSLint extension</a> for linting.</input> <br>
|
||||
<input type="checkbox"> Get credentials (e.g., Client ID, Client Secret, API key, user login, user password, website URL) for the service you are building a node for.</input>
|
||||
|
||||
## Development
|
||||
|
||||
<input type="checkbox"> Open a pull request as early as possible with `WIP` in the pull request title.</input><br>
|
||||
<input type="checkbox"> If you are creating a node requested by a community member, make sure to comment on the feature request in the [community forum](https://community.n8n.io/c/feature-requests/5).</input><br>
|
||||
<input type="checkbox"> Ensure complementary operations to each resource (e.g., create, delete) have been added.</input><br>
|
||||
<input type="checkbox"> Ensure the node works with multiple items via one input.</input><br>
|
||||
<input type="checkbox"> Ensure the parameters have the correct type.</input><br>
|
||||
<input type="checkbox"> Mind the defaults: if the service has a default as true, keep it as true. Changing default values can break the existing workflows of the users.</input><br>
|
||||
<input type="checkbox"> Check if the node disposes of everything properly, in particular, if connections were properly closed.</input><br>
|
||||
<input type="checkbox"> Check your code using <a href="https://docs.n8n.io/nodes/creating-nodes/nodelinter.html">Nodelinter</a> to ensure a clean lint <strong>before</strong> submitting your pull request</input><br>
|
||||
|
||||
## Testing
|
||||
|
||||
<input type="checkbox"> Test "create" and "update" operations with all fields/operations.</input><br>
|
||||
<input type="checkbox"> Test the `continueOnFail` option with a Function node. (For example, a Widget node has a GET operation that takes a widgetId and returns information on the widget. To test that the workflow continues on fail, set the Widget node to continue on fail, create a Function node, return a valid and an invalid widgetId, connect the Function node to Widget node, and run the workflow. The Widget node should show two items: one with information on the widget and another one with the error from having passed an invalid ID.)</input><br>
|
||||
|
||||
## Code formatting
|
||||
|
||||
<input type="checkbox"> Ensure the branch lints cleanly by running `npm run lint`.</input><br>
|
||||
<input type="checkbox"> Ensure the indentation is correct. Check this in the editorconfig.</input><br>
|
||||
<input type="checkbox"> Ensure there are no extra spaces. Check this in the editorconfig.</input><br>
|
||||
<input type="checkbox"> Code comment dividers inside if-branches.</input><br>
|
||||
<input type="checkbox"> Use "create/delete" verbs for operations, except for tags, where you should use "add/remove".</input><br>
|
||||
|
||||
## Errors and Outputs
|
||||
|
||||
<input type="checkbox"> Ensure empty API responses return `{ success: true }`.</input><br>
|
||||
<input type="checkbox"> Ensure the error responses are handled and displayed correctly (e.g., malformed requests, requests with invalid credentials) and use the current format. You can check this by making failing requests to the API.</input><br>
|
||||
<input type="checkbox"> Check if the response can be simplified and add a simplify function (e.g., <a href="https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/SecurityScorecard/GenericFunctions.ts">SecurityScorecard node</a>).</input><br>
|
||||
<input type="checkbox"> Ensure the response from `Create` is consistent with `Get`.</input><br>
|
||||
<input type="checkbox"> Ensure the response from `Get All` is consistent with `Get`.</input><br>
|
||||
|
||||
## Presentation
|
||||
|
||||
<input type="checkbox"> Ensure the primary menu contains only required parameters.</input><br>
|
||||
<input type="checkbox"> Ensure a JSON object is not shown in a single column in Table view.</input><br>
|
||||
<input type="checkbox"> Make sure all GetAll operations have the fields `return` and `limit`.</input><br>
|
||||
<input type="checkbox"> Ensure the property subtitle is set.</input><br>
|
||||
<input type="checkbox"> Make sure the pagination (if any) is working correctly. Set Limit 1.</input><br>
|
||||
|
||||
## Writing
|
||||
|
||||
<input type="checkbox"> Ensure all descriptions are correct and end with a period.</input><br>
|
||||
<input type="checkbox"> Ensure that most descriptions exist, excluding redundant ones.</input><br>
|
||||
<input type="checkbox"> Ensure IDs in displayNames are capitalized (i.e.: "IDs", not "ids" or "Ids").</input><br>
|
||||
<input type="checkbox"> Ensure that IDs, if multiple, have descriptive qualifiers.</input><br>
|
||||
<input type="checkbox"> Ensure the `name` property in `description` in the node class is written in camelCase.</input><br>
|
||||
<input type="checkbox"> Ensure the file name and the Class name are identical.</input><br>
|
||||
|
||||
## Branding
|
||||
|
||||
<input type="checkbox"> Ensure the name of the service is written correctly (e.g., "GitHub" not "Github"). If the node is a trigger node, ensure it is named as such, by adding "Trigger" after the service name (e.g., "Trello Trigger").</input><br>
|
||||
<input type="checkbox"> Ensure the logo is either a PNG or SVG, ideally the latter. <a href="https://vecta.io/symbols">Vecta</a> is a good website to find SVGs of different applications.</input><br>
|
||||
<input type="checkbox"> If the logo is an SVG, ensure the canvas is a perfect square. If the logo is PNG, ensure it is 60x60 pixels and compressed.</input><br>
|
||||
<input type="checkbox"> Ensure the border color of the node matches the branding of the service.</input><br>
|
||||
|
||||
## Nice-to-haves (optional)
|
||||
<input type="checkbox"> Add handler for `continueOnFail`. This feature is included in some of the newest nodes (e.g <a href="https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Lemlist/Lemlist.node.ts">Lemlist node</a>) to continue the workflow even if the node's execution fails.</input><br>
|
||||
<input type="checkbox"> Remove `required: false` and `description: ''` in the node descriptions (e.g., <a href="https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes/Lemlist/descriptions">Lemlist node</a>).</input><br>
|
||||
<input type="checkbox"> At call site, specify first `body` and then `qs`.</input><br>
|
||||
<input type="checkbox"> At call site, prepend the endpoint with slash `/` (e.g., "/campaign").</input><br>
|
||||
@ -0,0 +1,60 @@
|
||||
# General Guidelines
|
||||
|
||||
Please make sure that everything works correctly and that no unnecessary code gets added. It is important to follow the following guidelines:
|
||||
|
||||
## Do not change incoming data
|
||||
|
||||
Never change the incoming data a node receives (which can be queried with `this.getInputData()`) as it gets shared by all nodes. If data has to get added, changed or deleted it has to be cloned and the new data returned. If that is not done, sibling nodes which execute after the current one will operate on the altered data and would process different data than they were supposed to.
|
||||
It is however not needed to always clone all the data. If a node for, example only, changes only the binary data but not the JSON data, a new item can be created which reuses the reference to the JSON item.
|
||||
|
||||
An example can be seen in the code of the [ReadBinaryFile-Node](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/ReadBinaryFile.node.ts#L69-L83).
|
||||
|
||||
|
||||
## Write nodes in TypeScript
|
||||
|
||||
All code of n8n is written in TypeScript and hence, the nodes should also be written in TypeScript. That makes development easier, faster, and avoids at least some bugs.
|
||||
|
||||
|
||||
## Use the built in request library
|
||||
|
||||
Some third-party services have their own libraries on npm which make it easier to create an integration. It can be quite tempting to use them. The problem with those is that you add another dependency and not only one, you add but also all the dependencies of the dependencies. This means more and more code gets added, has to get loaded, can introduce security vulnerabilities, bugs, and so on. So please use the built-in module which can be used like this:
|
||||
|
||||
```typescript
|
||||
const response = await this.helpers.httpRequest(options);
|
||||
```
|
||||
|
||||
The full documentation and migration instructions from the deprecated `this.helpers.request` can be found [here](/integrations/creating-nodes/code/http-helpers/).
|
||||
|
||||
That is using the npm package [`request-promise-native`](https://github.com/request/request-promise-native) which is the basic npm `request` module but with promises. For a full set of `options` consider looking at [the underlying `request` options documentation](https://github.com/request/request#requestoptions-callback).
|
||||
|
||||
## Reuse parameter names
|
||||
|
||||
When a node can perform multiple operations like edit and delete some kind of entity, for both operations, it would need an entity-id. Do not call them "editId" and "deleteId", call them "id". n8n can handle multiple parameters with the same name without a problem as long as only one is visible. To make sure that is the case, the "displayOptions" can be used. By keeping the same name, the value can be kept if a user switches the operation from "edit" to "delete".
|
||||
|
||||
## Create an 'Additional Fields' parameter
|
||||
|
||||
Some nodes may need a lot of options. Add only the very important ones to the top level and for all others, create an 'Additional Fields' parameter where they can be added if needed. This ensures that the interface stays clean and does not unnecessarily confuse people. A good example of that would be the XML node.
|
||||
|
||||
## Follow existing parameter naming guideline
|
||||
|
||||
There is not much of a guideline yet but if your node can do multiple things, call the parameter which sets the behavior either "mode" (like "Merge" and "XML" node) or "operation" like the most other ones. If these operations can be done on different resources (like "User" or "Order) create a "resource" parameter (like "Pipedrive" and "Trello" node).
|
||||
|
||||
## Node icons
|
||||
|
||||
Check existing node icons as a reference when you create own ones. The resolution of an icon should be 60x60px and saved as PNG.
|
||||
|
||||
## Node versions
|
||||
|
||||
n8n now supports node versioning and it's a blast! You can make changes to existing nodes without breaking the existing behavior by introducing a new version. You can check an example of a versioned node by browsing the [Mattermost node](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts).
|
||||
|
||||
Node versioning in a glimpse:
|
||||
|
||||
- The main node file should now extend `NodeVersionedType` instead of `INodeType`
|
||||
- The main node file now only contains a base description containing the `defaultVersion` (usually the latest) and a list of versions
|
||||
- We recommend you use `v1`, `v2`, etc. for version folder names
|
||||
- A new code separation has been created and can be seen in the Mattermost node above. Highlights:
|
||||
* `actions` folder with description and implementation of each possible action
|
||||
* `methods` is an optional folder with the loading dynamic parameters' functions
|
||||
* `transport` is a folder with all the communication implementation
|
||||
|
||||
**Note:** For the `actions` folder we recommend using `resources` and `operations` names as subfolders hierarchically. For the implementation an description you can use separate files. Our recommendation is to use `execute.ts` and `description.ts` as file names. This make browsing through the code a lot easier. This can be simplified for nodes that have a less complicated structure.
|
||||
@ -0,0 +1,36 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Credentials
|
||||
|
||||
### Error message: 'Credentials of type “*” are not known'
|
||||
|
||||
Ensure that the name in the credentials array matches the name used in the property name of the credentials' class.
|
||||
|
||||

|
||||
|
||||
|
||||
## Editor UI
|
||||
|
||||
### Error message: 'There was a problem loading init data: API-Server can not be reached. It is probably down'
|
||||
|
||||
- Ensure that the node’s file name, class’s name, and node’s folder name matches the path added to `packages/nodes-base/package.json`.
|
||||
- Ensure the names used in the `displayOptions` property are names used by UI elements in the node.
|
||||
|
||||
### Node icon doesn't show up in the Create Node menu and the Editor UI
|
||||
|
||||
- Ensure that the icon is in the same folder as the node.
|
||||
- Ensure that it's either in PNG or SVG format.
|
||||
- When the icon is referenced in the 'icon' property, ensure that it includes the logo extension (`.png` or `.svg`) and that it is preceded by the world `file:`. For example, `file:friendGrid.png` or `file:friendGrid.svg`.
|
||||
|
||||
### Node icon does not fit correctly
|
||||
|
||||
- If you are using an SVG file, make sure the canvas size is square. You can find instructions to change the canvas size of an SVG file using GIMP [here](https://docs.gimp.org/2.10/en/gimp-image-resize.html).
|
||||
- If you are using a PNG file, make sure that it's 60x60 pixels.
|
||||
|
||||
### Node does not show up in the Create Node menu
|
||||
|
||||
Ensure that the node is registered in the `packages/nodes-base/package.json` file.
|
||||
|
||||
### Changes to the description properties do not show in the UI on refreshing
|
||||
|
||||
Every time a change is made to the description properties, you have to stop the current n8n process (ctrl + c) and run it again (npm run dev).
|
||||
@ -0,0 +1,306 @@
|
||||
# Node UI Elements
|
||||
|
||||
n8n provides a set of predefined UI components (based on a JSON file) that allow users to input all sorts of data types. Currently, the following UI elements are available in n8n.
|
||||
|
||||
[[toc]]
|
||||
|
||||
## String
|
||||
|
||||
The `string` type is used to input string values.
|
||||
|
||||
Basic configuration
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: Name, // The value the user would see in the UI
|
||||
name: name, // The name use to reference the element UI within the code
|
||||
type: string,
|
||||
required: true, // Whether the field is required or not
|
||||
default: 'n8n', // Value that would be set by default
|
||||
description: 'The name of the user',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/string.png" width="350">
|
||||
|
||||
Variation for inputting passwords
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Password',
|
||||
name: 'password',
|
||||
type: 'string',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
default: '',
|
||||
description: `User's password`,
|
||||
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/password.png" width="350">
|
||||
|
||||
Variation with multiple rows
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
rows: 4,
|
||||
},
|
||||
default: '',
|
||||
description: 'Description',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/multiple-rows.png" width="350">
|
||||
|
||||
|
||||
## Number
|
||||
|
||||
The `number` type is used to input numbers.
|
||||
|
||||
Basic configuration
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Age',
|
||||
name: 'age',
|
||||
type: 'number',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
maxValue: 10,
|
||||
minValue: 0,
|
||||
numberStepSize: 1,
|
||||
},
|
||||
default: 10,
|
||||
description: 'Your current age',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/number.png" width="350">
|
||||
|
||||
Variation with decimal points
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Amount',
|
||||
name: 'amount',
|
||||
type: 'number',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
numberPrecision: 2,
|
||||
},
|
||||
default: 10.00,
|
||||
description: 'Your current amount',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/decimal.png" width="350">
|
||||
|
||||
|
||||
## Collection
|
||||
|
||||
The `collection` type is used to input a collection of fields. For example, additional fields (or optional fields).
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Automated',
|
||||
value: 'automated',
|
||||
},
|
||||
{
|
||||
name: 'Past',
|
||||
value: 'past',
|
||||
},
|
||||
{
|
||||
name: 'Upcoming',
|
||||
value: 'upcoming',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/collection.png" width="350">
|
||||
|
||||
|
||||
## Datetime
|
||||
|
||||
The `dateTime` type provides a calendar from which you can pick a specific date and time.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Modified Since',
|
||||
name: 'modified_since',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'The date and time when the file was last modified',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/datetime.png" width="350">
|
||||
|
||||
|
||||
## Boolean
|
||||
|
||||
The `boolean` type is used to input a value that is either true or false. It is shown as a toggle that can be either on or off.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Wait for Image',
|
||||
name: 'waitForImage',
|
||||
type: 'boolean',
|
||||
default: true, // Initial state of the toggle
|
||||
description: 'Whether to wait for the image or not',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/boolean.png" width="350">
|
||||
|
||||
|
||||
## Color
|
||||
|
||||
The `color` type provides a color palette from which a specific color can be selected.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Background Color',
|
||||
name: 'backgroundColor',
|
||||
type: 'color',
|
||||
default: '', // Initially selected color
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/color.png" width="300">
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
The `options` type is used to provide options from which a single one has to be selected.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Image',
|
||||
value: 'image',
|
||||
},
|
||||
{
|
||||
name: 'Template',
|
||||
value: 'template',
|
||||
},
|
||||
],
|
||||
default: 'image', // The initially selected option
|
||||
description: 'Resource to consume',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/options.png" width="350">
|
||||
|
||||
|
||||
## Multi Options
|
||||
|
||||
The `multiOptions` type is used to provide options from which many can be selected.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Events',
|
||||
name: 'events',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'Plan Created',
|
||||
value: 'planCreated',
|
||||
},
|
||||
{
|
||||
name: 'Plan Deleted',
|
||||
value: 'planDeleted',
|
||||
},
|
||||
],
|
||||
default: [], // Initially selected options
|
||||
description: 'The events to be monitored',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/multioptions.png" width="350">
|
||||
|
||||
|
||||
## Fixed Collection
|
||||
|
||||
The `fixedCollection? type is used to present groups of fields that are semantically related.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Metadata',
|
||||
name: 'metadataUi',
|
||||
placeholder: 'Add Metadata',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: '',
|
||||
options: [
|
||||
{
|
||||
name: 'metadataValues',
|
||||
displayName: 'Metadata',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: 'Name of the metadata key to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value to set for the metadata key.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/fixed-collection.png" width="350">
|
||||
|
||||
|
||||
## JSON
|
||||
|
||||
The `json` type is used to input data formatted as JSON.
|
||||
|
||||
```typescript
|
||||
{
|
||||
displayName: 'Content (JSON)',
|
||||
name: 'content',
|
||||
type: 'json',
|
||||
default: '',
|
||||
description: '',
|
||||
},
|
||||
```
|
||||
|
||||
<img src="./images/json.png" width="350">
|
||||
12
docs/integrations/credentials/kobotoolbox.md
Normal file
12
docs/integrations/credentials/kobotoolbox.md
Normal file
@ -0,0 +1,12 @@
|
||||
# KoBo Toolbox
|
||||
|
||||
You can use these credentials to authenticate the following nodes:
|
||||
|
||||
* [KoBo Toolbox trigger](/integrations/trigger-nodes/n8n-nodes-base.koBoToolboxTrigger/)
|
||||
* [KoBo Toolbox](/integrations/nodes/n8n-nodes-base.koBoToolbox/)
|
||||
|
||||
KoBo Toolbox uses an API key for authentication:
|
||||
|
||||
1. Sign up for a [KoBo Toolbox](https://www.kobotoolbox.org/) account.
|
||||
2. Follow the [KoBo Toolbox API documentation](https://support.kobotoolbox.org/api.html) to get your API token.
|
||||
3. Copy your new key and use it with your KoBo Toolbox node credentials in n8n.
|
||||
@ -1,7 +1,9 @@
|
||||
# Linear
|
||||
|
||||
You can use these credentials to authenticate the following nodes with Linear.
|
||||
- [Linear Trigger](/integrations/trigger-nodes/n8n-nodes-base.linearTrigger/)
|
||||
|
||||
* [Linear Trigger](/integrations/trigger-nodes/n8n-nodes-base.linearTrigger/)
|
||||
* [Linear](/integrations/nodes/n8n-nodes-base.linear/)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# Xero
|
||||
|
||||
You can use these credentials to authenticate the following nodes with Xero.
|
||||
|
||||
- [Xero](/integrations/nodes/n8n-nodes-base.xero/)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
164
docs/integrations/nodes/n8n-nodes-base.koBoToolbox.md
Normal file
164
docs/integrations/nodes/n8n-nodes-base.koBoToolbox.md
Normal file
@ -0,0 +1,164 @@
|
||||
# KoBo Toolbox
|
||||
|
||||
[KoBo toolbox](https://www.kobotoolbox.org/) is a field survey and data collection tool that makes it easy to design interactive forms to be completed offline from mobile devices. It is available both as a free cloud solution or as a self-hosted version.
|
||||
|
||||
!!! note "Credentials"
|
||||
You can find authentication information for this node [here](/integrations/credentials/kobotoolbox/).
|
||||
|
||||
## Operations
|
||||
|
||||
* Form
|
||||
* Get
|
||||
* GetAll
|
||||
* Hook
|
||||
* Get
|
||||
* GetAll
|
||||
* Logs
|
||||
* Retry All
|
||||
* Retry One
|
||||
* Submission
|
||||
* Delete
|
||||
* Get
|
||||
* Get All
|
||||
* Get Validation Status
|
||||
* Update Validation Status
|
||||
|
||||
## Options
|
||||
|
||||
### Query Options
|
||||
|
||||
The Query Submission operation supports query options:
|
||||
|
||||
* In the main section of the **Parameters** panel:
|
||||
* **Start** controls the index offset to start the query from (to use the API pagination logic).
|
||||
* **Limit** sets the maximum number of records to return. Note that the API always has a limit of 30,000 returned records, whatver value you provide.
|
||||
* In the **Query Options** section, you can activate the following parameters:
|
||||
* **Query** lets you specify filter predicates in MongoDB's JSON query format. For example: `{"status": "success", "_submission_time": {"$lt": "2021-11-01T01:02:03"}}` queries for all submissions with the value `success` for the field `status`, and submitted before November 1st, 2021, 01:02:03.
|
||||
* **Fields** lets you specifiy the list of fields you want to fetch, to make the response lighter.
|
||||
* **Sort** lets you provide a list of sorting criteria in MongoDB JSON format. For example, `{"status": 1, "_submission_time": -1}` specifies a sort order by ascending status, and then descending submission time.
|
||||
|
||||
More details about these options can be found in the [Formhub API docs](https://github.com/SEL-Columbia/formhub/wiki/Formhub-Access-Points-(API)#api-parameters)
|
||||
|
||||
### Submission options
|
||||
|
||||
All operations that return form submission data offer options to tweak the response. These include:
|
||||
|
||||
- Download options lets you download any attachment linked to each particular form submissions, such as pictures and videos. It also lets you select the naming pattern, and the file size to download (if available - typically for images).
|
||||
- Formatting options perform some reformatting as described in [About reformatting](#about-reformatting).
|
||||
|
||||
#### About reformatting
|
||||
|
||||
The default JSON format for KoBoToolbox submission data is sometimes hard to deal with, because it is not schema-aware, and all fields are therefore returned as strings.
|
||||
|
||||
This node provides a lightweight opinionated reformatting logic, enabled with the **Reformat?** parameter, available on all operations that return form submissions: the submission query, get, and the attachment download operations.
|
||||
|
||||
When enabled, the reformatting:
|
||||
|
||||
- Reorganizes the JSON into a multi-level hierarchy following the form's groups. By default, question grouping hierarchy is materialized by a `/` character in the field names, for example `Group1/Question1`. With reformatting enabled, n8n reorganizes these into `Group1.Question1`, as nested JSON objects.
|
||||
- Renames fields to trim `_` (not supported by many downstream systems).
|
||||
- Parses all geospatial fields (Point, Line, and Area question types) into their standard GeoJSON equivalent.
|
||||
- Splits all fields matching any of the the **Multiselect Mask** wildcard masks into an array. Since the multi-select fields appear as space-separated strings, they can't be guessed algorithmically, so you must provide a field naming mask. Format the masks as a comma-separated list. Lists support the `*` wildcard.
|
||||
- Converts all fields matching any of the **Number Mask** wildcard masks into a JSON float.
|
||||
|
||||
Here's a detailed example in JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": 471987,
|
||||
"formhub/uuid": "189436bb09a54957bfcc798e338b54d6",
|
||||
"start": "2021-12-05T16:13:38.527+02:00",
|
||||
"end": "2021-12-05T16:15:33.407+02:00",
|
||||
"Field_Details/Field_Name": "Test Fields",
|
||||
"Field_Details/Field_Location": "-1.932914 30.078211 1421 165",
|
||||
"Field_Details/Field_Shape": "-1.932914 30.078211 1421 165;-1.933011 30.078085 0 0;-1.933257 30.078004 0 0;-1.933338 30.078197 0 0;-1.933107 30.078299 0 0;-1.932914 30.078211 1421 165",
|
||||
"Field_Details/Crops_Grown": "maize beans avocado",
|
||||
"Field_Details/Field_Size_sqm": "2300",
|
||||
"__version__": "veGcULpqP6JNFKRJbbMvMs",
|
||||
"meta/instanceID": "uuid:2356cbbe-c1fd-414d-85c8-84f33e92618a",
|
||||
"_xform_id_string": "ajXVJpBkTD5tB4Nu9QXpgm",
|
||||
"_uuid": "2356cbbe-c1fd-414d-85c8-84f33e92618a",
|
||||
"_attachments": [],
|
||||
"_status": "submitted_via_web",
|
||||
"_geolocation": [
|
||||
-1.932914,
|
||||
30.078211
|
||||
],
|
||||
"_submission_time": "2021-12-05T14:15:44",
|
||||
"_tags": [],
|
||||
"_notes": [],
|
||||
"_validation_status": {},
|
||||
"_submitted_by": null
|
||||
}
|
||||
```
|
||||
|
||||
With reformatting enabled, and the appropriate masks for multi-select and number formatting (for example, `Crops_*` and `*_sqm` respecitvely), n8n parses it into:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 471987,
|
||||
"formhub": {
|
||||
"uuid": "189436bb09a54957bfcc798e338b54d6"
|
||||
},
|
||||
"start": "2021-12-05T16:13:38.527+02:00",
|
||||
"end": "2021-12-05T16:15:33.407+02:00",
|
||||
"Field_Details": {
|
||||
"Field_Name": "Test Fields",
|
||||
"Field_Location": {
|
||||
"lat": -1.932914,
|
||||
"lon": 30.078211
|
||||
},
|
||||
"Field_Shape": {
|
||||
"type": "polygon",
|
||||
"coordinates": [
|
||||
{
|
||||
"lat": -1.932914,
|
||||
"lon": 30.078211
|
||||
},
|
||||
{
|
||||
"lat": -1.933011,
|
||||
"lon": 30.078085
|
||||
},
|
||||
{
|
||||
"lat": -1.933257,
|
||||
"lon": 30.078004
|
||||
},
|
||||
{
|
||||
"lat": -1.933338,
|
||||
"lon": 30.078197
|
||||
},
|
||||
{
|
||||
"lat": -1.933107,
|
||||
"lon": 30.078299
|
||||
},
|
||||
{
|
||||
"lat": -1.932914,
|
||||
"lon": 30.078211
|
||||
}
|
||||
]
|
||||
},
|
||||
"Crops_Grown": [
|
||||
"maize",
|
||||
"beans",
|
||||
"avocado"
|
||||
],
|
||||
"Field_Size_sqm": 2300
|
||||
},
|
||||
"version": "veGcULpqP6JNFKRJbbMvMs",
|
||||
"meta": {
|
||||
"instanceID": "uuid:2356cbbe-c1fd-414d-85c8-84f33e92618a"
|
||||
},
|
||||
"xform_id_string": "ajXVJpBkTD5tB4Nu9QXpgm",
|
||||
"uuid": "2356cbbe-c1fd-414d-85c8-84f33e92618a",
|
||||
"attachments": [],
|
||||
"status": "submitted_via_web",
|
||||
"geolocation": {
|
||||
"lat": -1.932914,
|
||||
"lon": 30.078211
|
||||
},
|
||||
"submission_time": "2021-12-05T14:15:44",
|
||||
"tags": [],
|
||||
"notes": [],
|
||||
"validation_status": {},
|
||||
"submitted_by": null
|
||||
}
|
||||
```
|
||||
15
docs/integrations/nodes/n8n-nodes-base.linear.md
Normal file
15
docs/integrations/nodes/n8n-nodes-base.linear.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Linear
|
||||
|
||||
[Linear](https://linear.app/) is a SaaS issue tracking tool.
|
||||
|
||||
!!! note "Credentials"
|
||||
You can find authentication information for this node [here](/integrations/credentials/linear/).
|
||||
|
||||
## Operations
|
||||
|
||||
* Issue
|
||||
* Create
|
||||
* Delete
|
||||
* Get
|
||||
* Get All
|
||||
* Update
|
||||
@ -0,0 +1,10 @@
|
||||
# KoBo Toolbox trigger
|
||||
|
||||
[KoBo toolbox](https://www.kobotoolbox.org/) is a field survey and data collection tool that makes it easy to design interactive forms to be completed offline from mobile devices. It is available both as a free cloud solution or as a self-hosted version.
|
||||
|
||||
!!! note "Credentials"
|
||||
You can find authentication information for this node [here](/integrations/credentials/kobotoolbox/).
|
||||
|
||||
This node starts a workflow upon new submissions of a specified form. The trigger node handles the creation/deletion of the hook, so you don't need to do any setup in KoBo Toolbox.
|
||||
|
||||
It works the same way as the Get Submission operation in the [KoBoToolbox](/integrations/nodes/n8n-nodes-base.koBoToolbox/) node, including supporting the same reformatting options.
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
[Linear](https://linear.app/) is a SaaS issue tracking tool.
|
||||
|
||||
!!! note "🔑 Credentials"
|
||||
!!! note "Credentials"
|
||||
You can find authentication information for this node [here](/integrations/credentials/linear/).
|
||||
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ View the [commits](https://github.com/n8n-io/n8n/compare/n8n@0.171.1...n8n@0.172
|
||||
|
||||
### Node enhancements
|
||||
<br>
|
||||
[Magento 2 Node:](/integrations/nodes/n8n-nodes-base.magento2/) text="Added credential tests.
|
||||
[Magento 2 Node:](/integrations/nodes/n8n-nodes-base.magento2/) Added credential tests.
|
||||
[PayPal Node:](/integrations/nodes/n8n-nodes-base.payPal/) Added credential tests and updated the API URL.
|
||||
|
||||
### Bug fixes
|
||||
@ -150,7 +150,7 @@ Read more about the new license in [License](/reference/license/).
|
||||
### Enhanced nodes
|
||||
|
||||
* [HTTP Request Node:](/integrations/core-nodes/n8n-nodes-base.httpRequest/) Allow Delete requests with body.
|
||||
* [KoBoToolbox Node:](/integrations/nodes/n8n-nodes-base.kobo/) Add KoBoToolbox Regular and Trigger Node.
|
||||
* [KoBoToolbox Node:](/integrations/nodes/n8n-nodes-base.koBoToolbox/) Add KoBoToolbox Regular and Trigger Node.
|
||||
* [Mailjet Node:](/integrations/nodes/n8n-nodes-base.mailjet/) Add credential tests and support for sandbox, JSON parameters & variables.
|
||||
* [Mattermost Node:](/integrations/nodes/n8n-nodes-base.mattermost/) Add support for Channel search.
|
||||
|
||||
|
||||
@ -65,7 +65,6 @@ nav:
|
||||
- Connections: workflows/connections.md
|
||||
- Nodes: workflows/nodes.md
|
||||
- Items: workflows/items.md
|
||||
- Workflow notes: workflows/workflow-notes.md
|
||||
- Flow logic:
|
||||
- Overview: flow-logic/index.md
|
||||
- Merging: flow-logic/merging.md
|
||||
|
||||
@ -22,12 +22,14 @@
|
||||
"/getting-started/installation/advanced/server-setup.html#docker-compose-example": "/hosting/server-setups/docker-compose/",
|
||||
"/getting-started/installation/advanced/server-setup.html#hosting-with-caddy-server": "/hosting/server-setups/caddy/",
|
||||
"/reference/faq.html#integrations": "/integrations/#requesting-new-integrations-or-integration-features",
|
||||
"/reference/faq.html": "/reference/license/",
|
||||
"/reference/faq.html#license": "/reference/license/",
|
||||
"/reference/faq.html#n8n-cloud": "/hosting/installation/cloud/",
|
||||
// High traffic anchor tag landing pages (if not covered by the above or by /docs/_redirects)
|
||||
"/#what-is-n8n": "/",
|
||||
"/getting-started/tutorials.html#blogposts": "https://n8n.io/blog/tag/tutorial/",
|
||||
"/#/server-setup": "/hosting/server-setups/"
|
||||
"/#/server-setup": "/hosting/server-setups/",
|
||||
"/code-examples/expressions/expressions.html": "/code-examples/expressions/"
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user