Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ module.exports = {
"/code/nodejs/auth/",
"/code/nodejs/http-requests/",
"/code/nodejs/working-with-files/",
"/code/nodejs/async/",
],
},
"/code/python/",
Expand Down
101 changes: 45 additions & 56 deletions docs/docs/code/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,18 @@ It's important to understand the core difference between Node.js and the JavaScr
## Adding a code step

1. Click the **+** button below any step of your workflow.
2. Select the option to **Run Node.js code**.

<div>
<img alt="Code step" src="./images/new-code-step.png">
</div>
2. Select the option to **Run custom code**.
3. Select the `nodejs14.x` runtime.

You can add any Node.js code in the editor that appears. For example, try:

```javascript
defineComponent({
export default defineComponent({
async run({ steps, $ }) {
console.log('This is Node.js code');
$.export('test', 'Some test data');
return 'Test data';
})
}
});
```

Expand All @@ -44,7 +41,7 @@ You can make code steps reusable by allowing them to accept props. Instead of ha
For example, let's define a `firstName` prop. This will allow us to freely enter text from the workflow builder.

```javascript
defineComponent({
export default defineComponent({
props: {
firstName: {
type: 'string',
Expand All @@ -68,37 +65,43 @@ Accepting a single string is just one example, you can build a step to accept ar

[Read the props reference for the full list of options](/components/api/#props).

## `async` function declaration
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section still referenced the old function declaration so I modified to discuss components. lmk if you think this can be improved

## How Pipedream Node.js components work

You'll notice an [`async` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) declaration that appears when you add a new Node.js code step:
When you add a new Node.js code step or use the examples in this doc, you'll notice a common structure to the code:

```javascript
defineComponent({
export default defineComponent({
async run({ steps, $ }) {
// this Node.js code will execute when your workflow is triggered
})
// this Node.js code will execute when the step runs
}
});
```

This communicates a couple of key concepts:
This defines [a Node.js component](/components/api/). Components let you:

- Any async code within a code step [**must** be run synchronously](/workflows/steps/code/async/), using the `await` keyword or with a Promise chain, using `.then()`, `.catch()`, and related methods.
- Pipedream passes the variables `event` and `steps` to every code step. `event` is a read-only object that contains the data that triggered your event, for example the HTTP request sent to your workflow's endpoint. `steps` is also an object, and contains the [data exported from previous steps](/workflows/steps/#step-exports) in your workflow.
- Pass input to steps using [props](/code/nodejs/#passing-props-to-code-steps)
- [Connect an account to a step](/connected-accounts/#from-a-code-step)
- [Issue HTTP responses](/workflows/steps/triggers/#customizing-the-http-response)
- Perform workflow-level flow control, like [ending a workflow early](#ending-a-workflow-early)

If you're using [step props](/code/nodejs/#passing-props-to-code-steps) or [connect an account to a step](/connected-accounts/#from-a-code-step), they are available under `this` within the `run` function of the step.
When the step runs, Pipedream executes the `run` method:

- Any asynchronous code within a code step [**must** be run synchronously](/workflows/steps/code/async/), using the `await` keyword or with a Promise chain, using `.then()`, `.catch()`, and related methods.
- Pipedream passes the `steps` variable to the run method. `steps` is also an object, and contains the [data exported from previous steps](/workflows/steps/#step-exports) in your workflow.
- You also have access to the `$` variable, which gives you access to methods like `$.respond`, `$.export`, [and more](/components/api/#actions).

If you're using [props](/code/nodejs/#passing-props-to-code-steps) or [connect an account to a step](/connected-accounts/#from-a-code-step), the component exposes them in the variable `this`, which refers to the current step:

```javascript
export default defineComponent({
async run({ steps, $ }) {
// this Node.js code will execute when your workflow is triggered

})
// `this` refers to the running component. Props, connected accounts, etc. are exposed here
console.log(this)
}
});
```

When you use [step parameters](/code/nodejs/#passing-props-to-code-steps), Pipedream passes the `props` object (named pairs of prop key and its associated value) to the function.

When you [connect an account to a step](/connected-accounts/#from-a-code-step), Pipedream passes the API keys to [`this.appName.$auths` object](/workflows/steps/code/auth/#the-auths-object) to the function.
When you [connect an account to a step](/connected-accounts/#from-a-code-step), Pipedream exposes the auth info in the variable [`this.appName.$auth`](/workflows/steps/code/auth/#the-auths-object).

## Logs

Expand All @@ -116,7 +119,7 @@ export default defineComponent({
console.dir({
name: "Luke"
})
})
}
});
```

Expand Down Expand Up @@ -222,7 +225,7 @@ In general, if you just need to make an HTTP request but don't care about the re

## Returning HTTP responses

You can return HTTP responses from [HTTP-triggered workflows](/workflows/steps/triggers/#http) using the [`$respond()` function](/workflows/steps/triggers/#customizing-the-http-response).
You can return HTTP responses from [HTTP-triggered workflows](/workflows/steps/triggers/#http) using the [`$.respond()` function](/workflows/steps/triggers/#customizing-the-http-response).

## Managing state

Expand All @@ -243,15 +246,15 @@ For more information on what functionality is available for those languages, ple
By default, Node.js steps don't have access to the database service. It needs to be injected by defining it as a `prop`.

```javascript
defineComponent({
export default defineComponent({
props: {
// Define that the "db" variable in our component is a database
db: "$.service.db",
},
async run({ steps, $ }) {
// Now we can access the database at "this.db"
this.db.set("name", "Dylan")
})
}
});
```

Expand Down Expand Up @@ -336,7 +339,7 @@ export default defineComponent({

// If the current email being passed from our webhook is already in our list, exit early
if(emails.includes(email)) {
$.flow.exit('Already welcomed this user');
return $.flow.exit('Already welcomed this user');
}

// Add the current email to the list of past emails so we can detect it in the future runs
Expand All @@ -352,7 +355,7 @@ The `$.service.db` is only currently available in Node.js code steps. It is not
In addition, `$.service.db` can hold up to {{ $site.themeConfig.SERVICE_DB_SIZE_LIMIT }} per step.


## `$end`
## Ending a workflow early
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some references to $end still here, just modifying


Sometimes you want to end your workflow early, or otherwise stop or cancel the execution or a workflow under certain conditions. For example:

Expand All @@ -361,60 +364,46 @@ Sometimes you want to end your workflow early, or otherwise stop or cancel the e
- You only want to run your workflow for users in the United States. If you receive a request from outside the U.S., you don't want the rest of the code in your workflow to run.
- You may use the `user_id` contained in the event to look up information in an external API. If you can't find data in the API tied to that user, you don't want to proceed.

**In any code step, calling the `$.flow.exit()` function will end the execution of the workflow immediately.** No remaining code in that step, and no code or destination steps below, will run for the current event.
**In any code step, calling `return $.flow.exit()` will end the execution of the workflow immediately.** No remaining code in that step, and no code or destination steps below, will run for the current event.

```javascript
defineComponent({
export default defineComponent({
async run({ steps, $ }) {
$.flow.exit();
return $.flow.exit();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always forget that using $.flow.exit() requires an explicit return. Not a great DX but you can talk to Giao about this one... we've faced some challenges here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow I didn't know that. My other examples are broken then. I thought it was the same behavior as $.end

console.log("This code will not run, since $.flow.exit() was called above it");
})
}
});
```

You can pass any string as an argument to `$.flow.exit()`:

```javascript
defineComponent({
export default defineComponent({
async run({ steps, $ }) {
$.end("Event doesn't have the correct schema");
})
return $.flow.exit("End message");
}
});
```

This message will appear in the Inspector in the **Messages** column for the event where `$.flow.exit()` was called:

<div>
<img alt="Dollar end message in inspector" src="./images/dollar-end.png" width="300px">
</div>

Like any other code, `$.flow.exit()` can be called conditionally:

```javascript
defineComponent({
export default defineComponent({
async run({ steps, $ }) {
// Flip a coin, running $.flow.exit() for 50% of events
if (Math.random() > 0.5) {
$.flow.exit();
return $.flow.exit();
}
console.log("This code will only run 50% of the time");
})
}
});
```

## Errors

[Errors](https://nodejs.org/dist/latest-v10.x/docs/api/errors.html#errors_errors) raised in a code step will stop the execution of code or destinations that follow.

You'll see the message associated with the error in the Inspector and the code step where the error was raised.

<div>
<img alt="Exception message" src="./images/exception.png">
</div>

## Using secrets in code

Workflow code is private by default, but [you can make a workflow public](/public-workflows/). In either case, we recommend you don't include secrets — API keys, tokens, or other sensitive values — directly in code steps.
Workflow code is private. Still, we recommend you don't include secrets — API keys, tokens, or other sensitive values — directly in code steps.

Pipedream supports [environment variables](/environment-variables/) for keeping secrets separate from code. Once you create an environment variable in Pipedream, you can reference it in any workflow using `process.env.VARIABLE_NAME`. The values of environment variables are private.

Expand Down Expand Up @@ -449,12 +438,12 @@ When you're searching for how to do something in JavaScript, some of the code yo
Many of the most basic JavaScript tutorials are geared towards writing code for a web browser to run. This is great for learning — a webpage is one of the coolest things you can build with code. We recommend starting with these general JavaScript tutorials and trying the code you learn on Pipedream:

- [JavaScript For Cats](http://jsforcats.com/)
- [Mozilla - Java​Script First Steps](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps)
- [Mozilla - JavaScript First Steps](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps)
- [StackOverflow](https://stackoverflow.com/) operates a programming Q&A site that typically has the first Google result when you're searching for something specific. It's a great place to find answers to common questions.

### I know how to code, but don't know JavaScript

- [A re-introduction to Java​Script (JS tutorial)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
- [A re-introduction to JavaScript (JS tutorial)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
- [MDN language overview](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
- [Eloquent Javascript](https://eloquentjavascript.net/)
- [Node School](https://nodeschool.io/)
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/code/nodejs/async/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Running asynchronous code
# Running asynchronous code in Node.js

If you're not familiar with asynchronous programming concepts like [callback functions](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) or [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises), see [this overview](https://eloquentjavascript.net/11_async.html).

Expand Down
22 changes: 6 additions & 16 deletions docs/docs/code/nodejs/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default defineComponent({
}
},
async run({ steps, $ }) {
const web = new WebClient(this.slack.$auths.oauth_access_token)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing references from $auths -> $auth. Data is exposed in the singular object

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, good catch

const web = new WebClient(this.slack.$auth.oauth_access_token)

return await web.chat.postMessage({
text: "Hello, world!",
Expand All @@ -38,12 +38,12 @@ Now the step in the workflow builder will allow you to connect your Slack accoun

[[toc]]

## Accessing API tokens with `this.appName.$auths`
## Accessing connected account data with `this.appName.$auth`

In our Slack example above, we created a Slack `WebClient` using the Slack OAuth access token:

```javascript
const web = new WebClient(this.slack.$auths.oauth_access_token);
const web = new WebClient(this.slack.$auth.oauth_access_token);
```

Where did `this.slack` come from? Good question. It was generated by the definition we made in `props`:
Expand All @@ -62,7 +62,7 @@ export default defineComponent({
// ... rest of the Node.js step
```

The Slack access token is generated by Pipedream, and is available to this step in the `this.slack.$auths` object:
The Slack access token is generated by Pipedream, and is available to this step in the `this.slack.$auth` object:

```javascript
export default defineComponent({
Expand All @@ -83,18 +83,12 @@ export default defineComponent({

`this.appName.$auths` contains named properties for each account you connect to the associated step. Here, we connected Slack, so `this.slack.$auths` contains the Slack auth info (the `oauth_access_token`).

You can view the named properties of the `this.appName.$auths` object for connected accounts next to the account connections:

<div>
<img alt="Slack auths object" width="500" src="./images/auths-property.png">
</div>

The names of the properties for each connected account will differ with the account. Pipedream typically exposes OAuth access tokens as `oauth_access_token`, and API keys under the property `api_key`. But if there's a service-specific name for the tokens (for example, if the service calls it `server_token`), we prefer that name, instead.

To list the `this.[app name].$auths` properties available to you for a given app, run `Object.keys` on the app:

```javascript
console.log(Object.keys(this.slack.$auths)) // Replace auths.slack with your app's name
console.log(Object.keys(this.slack.$auth)) // Replace this.slack with your app's name
```

and run your workflow. You'll see the property names in the logs below your step.
Expand All @@ -111,11 +105,7 @@ When you search for an app in a step:

1. Click the **+** button below any step.
2. Search for the app you're looking for and select it from the list.
3. Select the option to **Run Node.js code with [app]**.

<div>
<img alt="Slack test request" width="400" src="./images/run-node-with-slack.png">
</div>
3. Select the option to **Use any [app] API**.

This code operates as a template you can extend, and comes preconfigured with the connection to the target app and the code for authorizing requests to the API. You can modify this code however you'd like.

Expand Down
Loading