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

# Express

> Add Nelkap to an Express app, from your keys to your first run.

This guide adds Nelkap to an Express 5 app. By the end, your app will run a workflow that creates a new user's profile, waits a day, and sends them a welcome email, and you'll follow each step in your project.

You need a Nelkap project with its endpoint, an API key, and its secret. If you don't have them yet, follow the first three steps of the [quickstart](https://docs.nelkap.com/quickstart.md).

**1. Install the SDK**

```bash npm theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
npm install @nelkap/sdk
```

```bash pnpm theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pnpm add @nelkap/sdk
```

```bash yarn theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
yarn add @nelkap/sdk
```

**2. Add your keys**

Set your project's API key and secret as environment variables wherever your app runs:

```bash .env theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
NELKAP_API_KEY=your-api-key
NELKAP_SECRET=your-project-secret
```

The SDK reads both values from `process.env`. Node.js doesn't load `.env` on its own: start your app with `node --env-file=.env`, or set the variables in your host's settings. The API key lets your app start runs and save steps. The secret lets your app check that each call really comes from Nelkap.

**3. Create your client and your first workflow**

Create `src/nelkap.ts`:

```ts src/nelkap.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import { Nelkap } from '@nelkap/sdk';

import {
  createProfile,
  sendWelcomeEmail,
} from './activities';

export const nelkap = new Nelkap();

export async function onboardUser(userId: string) {
  await nelkap.step.run('profile', () =>
    createProfile(userId),
  );

  await nelkap.step.sleep('1 day');

  await nelkap.step.run('welcome', () =>
    sendWelcomeEmail(userId),
  );
}

nelkap.registerWorkflows([
  { name: 'onboardUser', handler: onboardUser },
]);
```

* `onboardUser` is your workflow: an async function that takes the run's payload, here a user ID.
* Each `step.run` saves what its function returns. When the run picks up after the sleep, the profile step gives back its saved result instead of creating the profile again.
* `step.sleep` pauses the run for a day. Nothing runs in your app during that day.
* `registerWorkflows` tells Nelkap which workflows your app runs, as soon as the file is loaded.

Replace `createProfile` and `sendWelcomeEmail` with your own functions.

**4. Add the route Nelkap calls**

`nelkap.handle` takes a standard web `Request`, while Express gives you its own request object. The route below builds a standard `Request` from it, passes it to `handle`, and sends back the answer:

```ts src/index.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import express from 'express';
import { Readable } from 'node:stream';

import { nelkap } from './nelkap';

const app = express();

app.post('/api/nelkap', async (req, res) => {
  const request = new Request(
    new URL(
      req.originalUrl,
      `${req.protocol}://${req.get('host')}`,
    ),
    {
      method: 'POST',
      headers: {
        authorization: req.get('authorization') ?? '',
      },
      body: Readable.toWeb(req) as ReadableStream,
      duplex: 'half',
    },
  );

  const response = await nelkap.handle(request);

  res.status(response.status).json(await response.json());
});

app.use(express.json());

app.listen(3000);
```

* The request's body streams straight into `handle`, so a run with many saved steps isn't held back by a body size limit.
* Add this route before `app.use(express.json())`. Otherwise Express reads the body first, and `handle` gets an empty one.
* `handle` checks the secret, answers Nelkap at once, and runs the workflow in the background. Your server keeps running after the answer, so the run goes on until it completes or pauses.

If your app checks sign-in on every route, leave `/api/nelkap` out: `handle` checks Nelkap's secret instead.

**5. Deploy your app**

Deploy your app with its two environment variables, so Nelkap can reach the route over the internet. Your project's endpoint must be the route's exact URL, like `https://your-app.com/api/nelkap`.

**6. Start a run**

Start the workflow from any route of your app. Here, a sign-up route starts it once the user is created:

```ts src/index.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import express from 'express';
import { Readable } from 'node:stream';

import { nelkap, onboardUser } from './nelkap';
import { createUser } from './users';

const app = express();

app.post('/api/nelkap', async (req, res) => {
  const request = new Request(
    new URL(
      req.originalUrl,
      `${req.protocol}://${req.get('host')}`,
    ),
    {
      method: 'POST',
      headers: {
        authorization: req.get('authorization') ?? '',
      },
      body: Readable.toWeb(req) as ReadableStream,
      duplex: 'half',
    },
  );

  const response = await nelkap.handle(request);

  res.status(response.status).json(await response.json());
});

app.use(express.json());

app.post('/sign-up', async (req, res) => {
  const user = await createUser(req.body);

  await nelkap.run(onboardUser, user.id);

  res.json({ userId: user.id });
});

app.listen(3000);
```

`run` returns as soon as Nelkap has the run, so the sign-up doesn't wait for the workflow. Its second argument, `user.id`, is the run's payload: Nelkap passes it to `onboardUser`.

**7. Follow your run**

Open your project's runs. The new run shows the profile step with what it returned, then sleeps. A day later, Nelkap calls your route again, the welcome email goes out, and the run completes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.