Skip to content

Node.js

Node.js Function tools run on Node.js 24 in a container. Unlike the JavaScript runtime (a synchronous V8 sandbox), Node.js gives you the full platform: async/await, the standard Promise-based fetch, Node’s built-in modules, and npm packages declared in a package.json.


Handler signature

Your main file must be named cortexone_function.js and must export a function called cortexone_handler. The handler may be async:

async function cortexone_handler(event, context) {
return {
statusCode: 200,
body: JSON.stringify({ message: "Hello, World!" })
};
}
module.exports = { cortexone_handler };
ParameterTypeDescription
eventobjectThe input payload sent by the caller
contextobjectExecution metadata - see The context object

The return value should be an object with statusCode and body.

CommonJS or ES modules

cortexone_function.js is loaded as CommonJS (require / module.exports). To write ES modules instead, name the entry file cortexone_function.mjs and export the handler with export:

cortexone_function.mjs
import { createHash } from "node:crypto";
export async function cortexone_handler(event, context) {
const digest = createHash("sha256").update(String(event.text)).digest("hex");
return { statusCode: 200, body: JSON.stringify({ digest }) };
}

.cjs also resolves. TypeScript (.ts) is not loaded - compile to JavaScript first.


The event object

event is whatever JSON the caller passes:

async function cortexone_handler(event, context) {
const numbers = event.num;
if (!Array.isArray(numbers) || numbers.length === 0) {
return {
statusCode: 400,
body: JSON.stringify({ error: "'num' must be a non-empty array of numbers." })
};
}
const sum = numbers.reduce((a, b) => a + b, 0);
return {
statusCode: 200,
body: JSON.stringify({ sum, mean: sum / numbers.length })
};
}
module.exports = { cortexone_handler };

When the caller uploads files, each one is written to a temporary directory on the container. event.files[i].path holds its local path, and so does event.<field_name> for the form field it was uploaded through. Read it with fs:

const fs = require("node:fs/promises");
async function cortexone_handler(event, context) {
const text = await fs.readFile(event.files[0].path, "utf8");
return { statusCode: 200, body: JSON.stringify({ lines: text.split("\n").length }) };
}
module.exports = { cortexone_handler };

Multiple files

You can create and upload multiple files. The main entry file must be cortexone_function.js (or .mjs / .cjs) and must export cortexone_handler. Load your other files with relative paths:

cortexone_function.js
const { slugify } = require("./utils");
async function cortexone_handler(event, context) {
return { statusCode: 200, body: JSON.stringify({ slug: slugify(event.title) }) };
}
module.exports = { cortexone_handler };
utils.js
function slugify(s) {
return String(s).toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
}
module.exports = { slugify };

Supported file types: .js, .mjs, .cjs, .json, plus package.json and package-lock.json.


Dependencies

Add npm packages to package.json, alongside cortexone_function.js:

{
"dependencies": {
"ms": "^2.1.3"
}
}

Then require them as usual:

const ms = require("ms");
async function cortexone_handler(event, context) {
return { statusCode: 200, body: JSON.stringify({ oneHour: ms("1h") }) };
}
module.exports = { cortexone_handler };

The platform installs your dependencies on the first run. If you ship a package-lock.json, it installs exactly those versions with npm ci; otherwise it runs npm install --omit=dev. New tools start with an empty package.json, and Studio keeps it - it can’t be deleted.

Node’s built-in modules (fs, path, crypto, url, and the rest) need no entry in package.json.


Secrets

Store credentials as Secrets at /user/secrets, attach them to your tool from the Code tab in Studio, and read them through process.env:

async function cortexone_handler(event, context) {
const apiKey = process.env.MY_API_KEY;
return { statusCode: 200, body: JSON.stringify({ ready: Boolean(apiKey) }) };
}
module.exports = { cortexone_handler };

See Secrets for the full workflow.


HTTP requests

Node.js tools have outbound internet access. fetch() is the standard WHATWG fetch - it returns a Promise of a Response, so await it:

async function cortexone_handler(event, context) {
const response = await fetch("https://api.example.com/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.MY_API_KEY}`
},
body: JSON.stringify({ key: "value" })
});
const data = await response.json();
return { statusCode: response.status, body: JSON.stringify(data) };
}
module.exports = { cortexone_handler };

npm HTTP clients such as axios work too - add them to package.json.


The context object

FieldDescription
context.functionNameThe tool’s function identifier
context.memoryLimitInMBAllocated memory in MB
context.getRemainingTimeInMillis()Milliseconds remaining before timeout

Cold starts and warm containers

The first run on a fresh container starts Node and installs your dependencies, so it takes noticeably longer than later runs. Later runs reuse the warm container and skip both steps.

Because containers are reused, module-level code (for example, creating an API client at the top of the file) runs once per container rather than once per call. That is a useful way to avoid repeating setup work, but don’t rely on it for state - a call can always land on a fresh container.


Resource limits

LimitDefault
Max memory512 MB
Max runtime300 seconds
CPU limit2.0 cores

GPU compute is only available on Python 3.13.


Error responses

If your handler throws, or its Promise rejects, the platform catches it and returns statusCode: 500 with the error in the body. You can also return non-200 status codes intentionally:

async function cortexone_handler(event, context) {
if (!event.id) {
return { statusCode: 400, body: JSON.stringify({ error: "id is required" }) };
}
return { statusCode: 200, body: JSON.stringify({ ok: true }) };
}
module.exports = { cortexone_handler };

Node.js vs JavaScript (V8)

JavaScript (V8)Node.js
HandlerTop-level function cortexone_handlerMust be exported (module.exports or ESM export)
async / awaitNot supportedSupported
fetch()Synchronous, returns { status, body }Standard fetch, returns a Promise of a Response
PackagesNoneBuilt-in modules and npm packages via package.json
FilesSingle fileMultiple files
Secretsprocess.envprocess.env
contextNot populatedfunctionName, memoryLimitInMB, getRemainingTimeInMillis()
Cold startMillisecondsA container plus npm install on the first run; warm afterwards

Pick JavaScript (V8) for small, self-contained logic where start-up time matters most. Pick Node.js when you need npm packages, async code, or more than one file.


Testing your code

Save and run sample payloads from the Preview tab in Studio. The panel shows the returned statusCode, body, and any console.log output from your handler. Expect the first run after creating the tool or changing package.json to be slower while dependencies install. See Testing & Executing for the full workflow.


Notes

  • The main file must be named cortexone_function.js (or .mjs / .cjs) and must export cortexone_handler.
  • The handler may be async; fetch() returns a Promise.
  • Declare npm packages in package.json; they install on the first run.
  • The runtime is fixed when the tool is created - an existing JavaScript (V8) tool can’t be switched to Node.js. Create a new tool instead.