# What breaks when you move Pipedream code steps to n8n Cloud

> Source: https://workflowexit.com/pipedream-code-steps-n8n-cloud · Updated 2026-10-13

On n8n Cloud, the Code node can only load two modules, `crypto` and `moment`, and Python code can't import anything, not even the standard library ([n8n Code node docs](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/)). Pipedream code steps that import `axios`, `date-fns`, an SDK or any other npm package fail until you replace them with n8n built-ins like `this.helpers.httpRequest` and Luxon, or move to self-hosted n8n with `NODE_FUNCTION_ALLOW_EXTERNAL`. Pipedream's own helpers (`steps`, `$.export`, `$.respond`, data stores, `process.env`) also need n8n equivalents.

Pipedream Workflows has been in maintenance mode since September 30, 2026 and stops running on March 31, 2027, so every code step needs a new runtime by then. The npm limit is the part that takes the most time, because Pipedream made npm free to use: you wrote `import axios from "axios"` and Pipedream installed it on deploy ([Pipedream Node.js docs](https://pipedream.com/docs/workflows/building-workflows/code/nodejs)). One person reviewing real Pipedream exports in the n8n community forum put it this way: "About 7 in 10 of the code steps I looked at import a package or call an API" ([n8n community](https://community.n8n.io/t/moving-off-pipedream-workflows-before-31-march-2027-what-maps-to-what-in-n8n/320920)).

## Pipedream vs n8n Cloud vs self-hosted n8n

| Capability | Pipedream code step | n8n Cloud Code node | Self-hosted n8n Code node |
|---|---|---|---|
| npm packages | Any, installed on deploy | `crypto` and `moment` only | Any installed module listed in `NODE_FUNCTION_ALLOW_EXTERNAL` |
| Node.js built-ins (`fs`, `path`) | Yes | No | If listed in `NODE_FUNCTION_ALLOW_BUILTIN` |
| Module syntax | `import` or `require` | `require` only | `require` only |
| Python packages | Any PyPI package | None, not even the standard library | If included in the task runner image and allowlisted |
| HTTP requests | Any client | `this.helpers.httpRequest` or HTTP Request node | Same, or an allowed npm client |
| Dates | Any library | Luxon `DateTime` built in, plus `moment` | Same, or an allowed library |
| Environment variables | `process.env` | `$vars` on Pro and Enterprise | `$vars` on Business and Enterprise, or `$env` if enabled |
| Connected account tokens | `this.app.$auth` | Code nodes can't read credentials | Same |
| Key-value state | Data stores | Static data or Data Tables | Same |
| Files | `/tmp` and File Stores | No file system | `fs` if allowed, or file nodes |

Sources: [Code node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/), [enable modules](https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/enable-modules-in-code-node/), [common issues](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/common-issues/).

## 1. HTTP clients: axios, got, node-fetch

n8n exposes `this.helpers.httpRequest` inside the Code node ([options reference](https://docs.n8n.io/connect/create-nodes/build-your-node/reference/http-request-helpers/)). It returns the response body by default, which conveniently matches what `@pipedream/platform` axios returned on success.

Before, in Pipedream:

```javascript
import axios from "axios";

export default defineComponent({
  async run({ steps, $ }) {
    const { data } = await axios.get("https://api.example.com/orders", {
      params: { status: "open" },
      headers: { Authorization: `Bearer ${process.env.SHOP_TOKEN}` },
    });
    return data;
  },
});
```

After, in an n8n Code node (mode: Run Once for All Items):

```javascript
const orders = await this.helpers.httpRequest({
  method: "GET",
  url: "https://api.example.com/orders",
  qs: { status: "open" },
  headers: { Authorization: `Bearer ${$vars.SHOP_TOKEN}` },
  json: true,
});

// An item's json must be an object, so wrap the array.
return [{ json: { orders } }];
```

The option names differ from axios: `params` becomes `qs`, `data` becomes `body`. Set `returnFullResponse: true` when you need status and headers; you then get `{ body, headers, statusCode, statusMessage }`.

For calls that use a connected account, don't move the token into code at all. n8n Code nodes can't access credentials by design ([n8n docs](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/common-issues/)). Put that request in an HTTP Request node, pick the app's predefined credential type, and let n8n handle OAuth refresh. A step that read `this.github.$auth.oauth_access_token` becomes an HTTP Request node with a GitHub credential.

## 2. Dates: date-fns, dayjs, moment-timezone

n8n ships Luxon, and its [date docs](https://docs.n8n.io/build/work-with-data/handle-special-data-types/work-with-dates-and-times/) call `DateTime` the recommended approach. This example comes from Pipedream's own GitHub Sync sample workflow.

Before:

```javascript
import { parseISO, endOfDay } from "date-fns";

export default defineComponent({
  props: { timestamp: { type: "string" } },
  async run({ steps, $ }) {
    return endOfDay(parseISO(this.timestamp)).toISOString();
  },
});
```

After:

```javascript
const ts = $input.first().json.timestamp; // e.g. "2026-10-13T09:00:00.000Z"
const end = DateTime.fromISO(ts, { zone: "utc" }).endOf("day");
return [{ json: { endOfDay: end.toISO() } }]; // "2026-10-13T23:59:59.999Z"
```

`date-fns` computed `endOfDay` in the server's local time zone. Pass a zone explicitly in Luxon so the result doesn't depend on the n8n instance's timezone setting.

`formatInTimeZone(parseISO(start), tz, "H:mm")` from `date-fns-tz` becomes `DateTime.fromISO(start).setZone(tz).toFormat("H:mm")`. Plain `moment` works on n8n Cloud through `require("moment")`, but `moment-timezone` does not.

## 3. steps, $.export and return values

Pipedream passes earlier results in `steps`. n8n reads earlier nodes with `$('node name')`, and a Code node must return an array of `{ json: {...} }` items.

Before:

```javascript
export default defineComponent({
  async run({ steps, $ }) {
    const { email } = steps.trigger.event.body;
    if (!email) return $.flow.exit("no email");
    $.export("domain", email.split("@")[1]);
    return { email };
  },
});
```

After:

```javascript
const { email } = $('trigger').first().json.body;
if (!email) return []; // no items: downstream nodes don't run, like $.flow.exit
return [{ json: { domain: email.split("@")[1], $return_value: { email } } }];
```

Downstream nodes then read `{{ $('validate').first().json.domain }}` where Pipedream used `{{steps.validate.domain}}`, assuming the node is named after the Pipedream step. Pipedream code props (`this.firstName`) have no direct equivalent: inline the values, or read them from an Edit Fields node placed before the Code node.

## 4. $.respond, delays and suspends

These are workflow-level features in n8n, not code:

- **`$.respond()`** becomes a [Respond to Webhook](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.respondtowebhook/) node, with the Webhook node's Respond option set to "Using 'Respond to Webhook' Node". Have the Code node output the status and body, then reference them in the Respond node. If the workflow finishes without reaching that node, n8n returns a standard 200 message, where Pipedream returned a 400 "No $.respond called in workflow."
- **`$.flow.delay(ms)`** means splitting the step in two with a [Wait](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/) node (After Time Interval) in between.
- **`$.flow.suspend()`** becomes a Wait node with Resume: On Webhook Call.

## 5. process.env and secrets

Before: `process.env.STRIPE_KEY`. After, pick one:

- **`$vars.STRIPE_KEY`.** n8n [Variables](https://docs.n8n.io/build/code-in-n8n/define-custom-variables/) are read-only strings (values up to 1,000 characters), set in the UI. Available on n8n Cloud Pro and Enterprise, and self-hosted Business and Enterprise.
- **`$env.STRIPE_KEY`** on self-hosted n8n. Since n8n 2.0, `N8N_BLOCK_ENV_ACCESS_IN_NODE` defaults to `true`, so this only works if you set it to `false` ([n8n 2.0 breaking changes](https://docs.n8n.io/changelog/v20-breaking-changes/)).
- **A credential on an HTTP Request node.** Usually the better home for API keys, and the practical choice on n8n Cloud Starter, which doesn't include Variables.

## 6. Data stores

A typical counter in Pipedream, using a [data store prop](https://pipedream.com/docs/workflows/building-workflows/code/nodejs/using-data-stores):

```javascript
export default defineComponent({
  props: { db: { type: "data_store" } },
  async run({ steps, $ }) {
    const count = ((await this.db.get("count")) ?? 0) + 1;
    await this.db.set("count", count);
    return count;
  },
});
```

The same with n8n [workflow static data](https://docs.n8n.io/build/code-in-n8n/cookbook/built-in-methods-and-variables-examples/getworkflowstaticdata/):

```javascript
const store = $getWorkflowStaticData("global");
store.count = (store.count ?? 0) + 1;
return [{ json: { count: store.count } }];
```

Know the differences before you rely on it:

- Static data is saved only when a published workflow runs from a trigger or webhook. Manual test runs don't persist it.
- It belongs to one workflow. Pipedream data stores could be shared by many workflows.
- n8n says it "should be small," and there is no TTL like `set(key, value, { ttl })`.
- It works in JavaScript Code nodes only, not Python.

For shared or larger data, use n8n [Data Tables](https://docs.n8n.io/build/work-with-data/data-tables/) through the Data Table node (Code nodes can't access them directly; default limit 200 MiB per instance), or an external database. Either way, data store records are not in the Pipedream export, so copy them out before April 30, 2027.

## 7. /tmp and File Stores

Pipedream gave each execution a writable `/tmp`, and File Stores for longer storage. On n8n Cloud the Code node has no file system access. Pass files between nodes as binary data, use the Convert to File and Extract From File nodes, or store them in S3 or Google Drive. On self-hosted n8n you can allow `fs` with `NODE_FUNCTION_ALLOW_BUILTIN` or use the Read/Write Files from Disk node. Download File Store contents from Pipedream before the shutdown, because they are not exported.

## 8. Python code steps

On n8n Cloud, the Python Code node can't import any library. On self-hosted n8n 2.x, Python runs natively on task runners: imports work only if the `n8nio/runners` image includes the package and it is allowlisted, the code reads `_items` or `_item` instead of `pd.steps`, and fields use bracket access (`item["json"]["email"]`). Pyodide-based Python was removed in n8n 2.0. If your Python steps mostly use `requests`, rewriting them in JavaScript with `this.helpers.httpRequest` is often faster.

## 9. Self-hosted: allow your packages

Install the modules into your n8n image, then allow them:

```bash
NODE_FUNCTION_ALLOW_BUILTIN=crypto,fs
NODE_FUNCTION_ALLOW_EXTERNAL=date-fns,lodash,stripe
```

n8n's docs note that if your instance uses task runners, these variables go on the task runners rather than the main n8n process. Convert `import` statements to `require`.

## How Workflow Exit handles code steps

The [free migration report](/report) lists every npm package each code step imports and whether n8n Cloud can run it, along with environment variables, data stores, `$.flow` calls and connected accounts. It runs in your browser.

The paid conversion turns each Node.js step into an n8n Code node with a compatibility layer above your original code. The layer rebuilds `steps`, `$.export`, `$.flow.exit`, `this` props, data stores (on workflow static data), `process.env` lookups (a secrets block at the top of the Code node, or n8n Variables on paid plans) and `@pipedream/platform` axios, `axios` and `node-fetch` calls (on `this.helpers.httpRequest`). It was tested by running converted workflows in n8n 2.x. For packages n8n Cloud can't load, an optional AI rewrite replaces them with n8n built-ins or HTTP calls. Delay blocks become Wait nodes. A `$.flow.delay` or `$.flow.suspend` call inside code means the step has to be split around a Wait node, so the report flags those steps and they need a review after conversion. Connected accounts still need n8n credentials, and the generated `CHECKLIST.md` lists each one.

Plans start at $49 for up to 25 workflows and the first workflow is free ([pricing](/pricing)). For the full process, see the [Pipedream to n8n migration guide](/pipedream-to-n8n), and for the bigger picture, [Pipedream vs n8n](/pipedream-vs-n8n). If you haven't exported yet, start with [exporting your workflows](/export-pipedream-workflows), then run the [report](/report) to see how many of your steps hit these limits.

## Frequently asked questions

### Can I use npm packages in the n8n Cloud Code node?

No. n8n's Code node documentation says you can't import external npm modules on n8n Cloud. Two modules are available: the crypto Node.js module and the moment package. Self-hosted n8n can allow other installed modules with NODE_FUNCTION_ALLOW_EXTERNAL.

### Does axios work in an n8n Code node?

Not on n8n Cloud, because axios is an external module. Use this.helpers.httpRequest inside the Code node, or an HTTP Request node. On self-hosted n8n you can install axios and add it to NODE_FUNCTION_ALLOW_EXTERNAL.

### What replaces process.env from Pipedream in n8n?

Use n8n Variables ($vars.NAME), available on n8n Cloud Pro and Enterprise and self-hosted Business and Enterprise. On self-hosted n8n you can also use $env.NAME, but since n8n 2.0 Code nodes can only read environment variables if N8N_BLOCK_ENV_ACCESS_IN_NODE is set to false.

### What replaces Pipedream data stores in n8n?

For small per-workflow state, workflow static data via $getWorkflowStaticData('global') in a Code node. For data shared across workflows or larger tables, n8n Data Tables through the Data Table node, or an external database. Static data has no TTL and is only saved on production executions.

### Can n8n Cloud run my Pipedream Python code steps?

Only if they import nothing. On n8n Cloud the Python Code node can't import any library, including the standard library. Rewrite those steps in JavaScript, or self-host n8n and add the packages to the task runner image.

### Why does my code say 'import and export may only appear at the top level' in n8n?

The n8n Code node does not support import or export statements. Pipedream steps use export default defineComponent and import, so remove the wrapper, put the body of run() in the Code node, and load allowed modules with require.
