> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withnubo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js pack

> How Raypacks builds Node apps and bots

The Node pack covers anything driven by a `package.json`: REST APIs, Discord bots, Express servers, Next.js (non-SSR builds), the lot. It auto-picks your package manager from the lockfile.

## When it runs

There's a `package.json` at the repo root.

## Package manager detection

Raypacks reads your lockfile to decide what to install with:

| Lockfile                  | Manager | Base image      |
| ------------------------- | ------- | --------------- |
| `bun.lockb` or `bun.lock` | bun     | `oven/bun:slim` |
| `pnpm-lock.yaml`          | pnpm    | `node:22-slim`  |
| `yarn.lock`               | yarn    | `node:22-slim`  |
| (none of the above)       | npm     | `node:22-slim`  |

Commit your lockfile. Without it Raypacks falls back to npm.

## Build steps

For every Node project:

```
<pm> install           # ci for npm, --frozen-lockfile for pnpm/yarn
<pm> run build         # only if package.json has a "build" script
```

If you don't have a `build` script (a bare API or bot), only the install step runs.

### Command overrides

Most projects need nothing here: Raypacks picks the install and build steps from your lockfile and `package.json`. For a project that doesn't fit the defaults, a Frame exposes **Install command** and **Build command** overrides in **Frame Settings**, under **Build**. Set either one to replace the auto-detected step (for example a custom install flag, or a build script that isn't named `build`); the build command runs after install. Leave them blank to let Raypacks pick.

## Runtime

| pm            | Entrypoint                                                                                                |
| ------------- | --------------------------------------------------------------------------------------------------------- |
| bun           | `bun run start`                                                                                           |
| anything else | `npm start` if you have a `start` script, otherwise `node <package.json#main>`, otherwise `node index.js` |

| Field       | Value                                     |
| ----------- | ----------------------------------------- |
| Working dir | `/app`                                    |
| Port        | from Frame settings, default 3000         |
| Env         | `NODE_ENV=production`, `PORT=<your port>` |

Your `start` script is the contract Raypacks expects. Define one and the pack lights up:

```json package.json theme={null}
{
  "scripts": {
    "start": "node server.js",
    "build": "tsc -p ."
  }
}
```

## Limits today

* No `engines.node` enforcement (we ship `node:22-slim` regardless).
* Workspaces (`pnpm-workspace.yaml`, npm workspaces) build everything but the root scripts run.

## Common errors

**App starts and exits immediately**: your `start` script ran something that wasn't a long-running server. Make sure it boots an HTTP listener.

**Port mismatch**: your app listens on `process.env.PORT` but you set the Frame port to something else. Read `PORT` from env, don't hardcode.

**Native modules fail to build**: `node:22-slim` doesn't include build toolchains. For now, prefer prebuilt binaries (`better-sqlite3` with prebuilt, etc.) or switch to the Paketo builder.
