Skip to content

Conversation

@eersnington
Copy link
Contributor

Cooked up a working demo of using workflow devkit with fastify, using nitro as the bundler. I will add a docs page and e2e tests as well (following adrian's example for express).

Really quick guide

1. Configure Nitro

Configure nitro.config.ts to load the workflow module and direct all routes to your entry file.

import { defineNitroConfig } from 'nitro/config';

export default defineNitroConfig({
  modules: ['workflow/nitro'],
  vercel: { entryFormat: 'node' },
  routes: {
    '/**': './src/index.ts',
  },
});

2. Server Entry Adapter

In your entry file, bridge Nitro to Fastify by manually emitting the request event to Fastify's underlying server instance.

import Fastify from 'fastify';
import { fromNodeHandler } from 'nitro/h3';

const server = Fastify();
// ... register plugins & routes ...
await server.ready();

export default fromNodeHandler((req, res) => {
  // nitro expects the exported handler to wait until the response is complete
  // fixes FST_ERR_REP_ALREADY_SENT issue
  return new Promise((resolve) => {
    res.on('finish', resolve);
    server.server.emit('request', req, res);
  });
});

3. Create Workflows

Import your workflow and call start() within any standard Fastify route handler.

import { start } from 'workflow/api';
import { myWorkflow } from './workflows';

server.post('/trigger', async (req) => {
  const run = await start(myWorkflow, [req.body.data]); // this will be annotated thanks to the workflow nitro plugin
  return { runId: run.runId };
});

Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
@changeset-bot
Copy link

changeset-bot bot commented Nov 21, 2025

⚠️ No Changeset found

Latest commit: 6612d33

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Contributor

vercel bot commented Nov 21, 2025

@eersnington is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@socket-security
Copy link

socket-security bot commented Nov 21, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​fastify@​5.6.29910010094100

View full report

Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Stream web streams the same way express or hono do. Read the web reader and write each json chunk to reply.raw. Avoids fastifys async iterator quirks that reordered frames and sent undefined data in the output stream tests

Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Comment on lines +67 to +78
if (!workflowFile) {
return reply.code(400).send('No workflowFile query parameter provided');
}
const workflows = allWorkflows[workflowFile as keyof typeof allWorkflows];
if (!workflows) {
return reply.code(400).send(`Workflow file "${workflowFile}" not found`);
}

const workflowFn = (req.query.workflowFn as string) || 'simple';
if (!workflowFn) {
return reply.code(400).send('No workflow query parameter provided');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!workflowFile) {
return reply.code(400).send('No workflowFile query parameter provided');
}
const workflows = allWorkflows[workflowFile as keyof typeof allWorkflows];
if (!workflows) {
return reply.code(400).send(`Workflow file "${workflowFile}" not found`);
}
const workflowFn = (req.query.workflowFn as string) || 'simple';
if (!workflowFn) {
return reply.code(400).send('No workflow query parameter provided');
}

The parameter validation checks on lines 67-68 and 76-77 are dead code and will never execute, because the variables are always assigned non-empty default values using the OR operator above them.

View Details

Analysis

Dead code: unreachable validation checks in POST /api/trigger endpoint

What fails: Lines 67-68 and 76-77 in workbench/fastify/src/index.ts contain unreachable validation checks that will never execute.

How to reproduce: The following code pattern causes dead code because the OR operator guarantees non-falsy values:

const workflowFile = (req.query.workflowFile as string) || 'workflows/99_e2e.ts';
if (!workflowFile) { // This condition will never be true
  return reply.code(400).send('No workflowFile query parameter provided');
}

When req.query.workflowFile is undefined, the expression (undefined || 'workflows/99_e2e.ts') evaluates to the default string 'workflows/99_e2e.ts', which is truthy. The subsequent if (!workflowFile) check will never execute.

Result: Dead code remains in the codebase, creating confusion about intended behavior.

Expected: Since default values are intentionally provided via the OR operator, the validation checks should be removed. The defaults indicate the developer intended these parameters to be optional, making the checks that assume they might be missing logically inconsistent.

Fix: Removed the unreachable validation checks on lines 67-68 and 76-77, keeping only the default value assignments and the subsequent existence checks on the workflow lookup (lines 68-70 and 74-77), which properly validate that the assigned workflow file and function actually exist.

Copy link
Contributor Author

@eersnington eersnington Nov 22, 2025

Choose a reason for hiding this comment

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

Technically correct nit, but this pattern is consistent across all the api/trigger (POST) routes in all benches. Is this change necessary?

Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
Signed-off-by: Sree Narayanan <[email protected]>
@eersnington
Copy link
Contributor Author

got it running on vercel
image
image

@eersnington
Copy link
Contributor Author

I'll wait for a review before updating test matrix and local-build.test.ts CI?

@eersnington eersnington marked this pull request as ready for review November 22, 2025 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant