Skip to content

Start typing to search the documentation.

Routing

Last updated View as Markdown

src/app.ts is an optional entrypoint for providing your own HTTP application in a bapX project. Add this file when your application needs authentication, health checks, route prefixes, or custom routes alongside the agents, workflows, and channels exposed by bapX.

It is an ordinary Hono application, so you can compose bapX routes with your own routes and middleware.

app.ts

Without src/app.ts, bapX generates an application that mounts its public routes at /. When you add src/app.ts, export a Hono application and mount bapX() explicitly:

import { bapX } from '@bapX/runtime/routing';
import { Hono, type MiddlewareHandler } from 'hono';
import { authenticate } from './auth.ts';

const requireUser: MiddlewareHandler = async (c, next) => {
  const user = await authenticate(c.req.raw);

  if (!user) {
    return c.json({ error: 'Unauthorized' }, 401);
  }

  await next();
};

const app = new Hono();

app.get('/health', (c) => c.json({ ok: true }));

app.use('/agents/*', requireUser);
app.use('/workflows/*', requireUser);
app.use('/channels/*', requireUser);
app.route('/', bapX());

export default app;

In this application, /health is application-owned, while bapX() serves exposed agents, workflow invocation routes, and discovered channels. Workflow modules authorize their own optional run resources with runs middleware.

Use broader middleware for requirements shared by a group of routes, such as requiring an authenticated user. When access depends on a specific selected resource, apply that check as well: for example, an agent route should verify that the caller may access the agent instance named by its id, and an application that publishes workflow run reads should authorize access to the selected run.

Because your authored application imports Hono, include hono in your application dependencies. See Project Layout for alternative source directories supported by existing projects.

Add custom routes

A custom application can serve any route your service needs. It can also accept an external event, verify and normalize it, and deliver it to an agent without exposing a direct prompt route for that event source:

import { dispatch } from '@bapX/runtime';
import { bapX } from '@bapX/runtime/routing';
import { Hono } from 'hono';
import supportAssistant from './agents/support-assistant.ts';
import { parseVerifiedSupportComment } from './support-webhooks.ts';

const app = new Hono();

app.post('/webhooks/support-comments', async (c) => {
  const event = await parseVerifiedSupportComment(c.req.raw);
  const receipt = await dispatch(supportAssistant, {
    id: event.ticketId,
    message: {
      kind: 'signal',
      type: 'support.comment.created',
      body: event.text,
      attributes: { commentId: event.commentId },
    },
  });

  return c.json(receipt, 202);
});

app.route('/', bapX());

export default app;

Here, the webhook route belongs to your application: it determines which requests are valid and which agent instance receives the accepted message. dispatch(...) delivers that message asynchronously to the continuing agent session. See Agents for agent interaction patterns and Channels for provider integrations.

Customized routing

For most applications, mount bapX at the root with app.route('/', bapX()). You can instead mount it beneath a prefix when bapX is one part of a larger API:

import { bapX } from '@bapX/runtime/routing';
import { Hono } from 'hono';

const app = new Hono();

app.get('/health', (c) => c.json({ ok: true }));
app.route('/api', bapX());

export default app;

With this mount, an exposed support-assistant agent is available beneath /api/agents/support-assistant/:id, an exposed summarize-ticket workflow is available beneath /api/workflows/summarize-ticket, and channels/github.ts publishes its webhook beneath /api/channels/github/webhook. Optional workflow run resources use the same prefix. SDK consumers should include the mount pathname in baseUrl, such as createBapxClient({ baseUrl: 'https://example.com/api' }).

Apply application-wide middleware to the mounted paths you publish. Per-workflow runs middleware remains responsible for exposing and authorizing each run resource.

Discovered channel filenames and provider route suffixes are fixed beneath the bapX() mount. An authored app.ts can prefix all Bapx routes but cannot relocate one channel independently. Use an ordinary application-owned route outside channels/ when you need complete path control.

Exposing agents and workflows

Mounting bapX() does not make every discovered agent or workflow directly invocable. Each module opts into its public transports:

Module exportAvailable through the mounted Bapx application
Agent routeHTTP prompts at POST /agents/:name/:id and event streaming at GET /agents/:name/:id beneath the mount path.
Workflow routeHTTP invocation at POST /workflows/:name beneath the mount path.
Workflow runsAuthorized HTTP operations on existing runs owned by that workflow beneath /runs/:runId.
Channel channelProvider-declared HTTP surfaces beneath /channels/:name/<suffix>.

route controls workflow invocation only. Export runs separately when HTTP clients should inspect runs, including runs created by ambient invoke(), schedules, or other non-HTTP callers:

import type { WorkflowRunsHandler } from '@bapX/runtime';
import { verifyRunToken } from '../auth.ts';

export const runs: WorkflowRunsHandler = async (c, next) => {
  const token = c.req.header('authorization');
  if (!(await verifyRunToken(token))) {
    return c.json({ error: 'Not found' }, 404);
  }

  await next();
};

runs receives an ordinary Hono context and may deny or call next(). It applies to GET, HEAD, ?meta, unsupported methods, and future run methods. Without it, existing runs return the same generic 404 as unknown or removed runs. A request reaches 405 for an unsupported method only after the run is exposed and authorized. These exports do not affect ambient invoke(), listRuns(), getRun(), or schedules. A temporary local bapX run process additionally exposes route-free resources and run reads through an existing authored bapX() mount; an absolute --server attachment uses only the server’s authored exposure.

An agent used only through application-owned dispatch(...) calls does not need a public transport export.

See Agents for creating and exposing continuing agent instances, and Workflows for exposing finite operations and inspecting their runs.

Next steps

  • Agents — create continuing agents and deliver direct or dispatched input.
  • Workflows — create finite operations and inspect workflow runs.
  • Channels — compose provider ingress with agent sessions.
  • CLI — run the application locally, create build output, and continue to deployment.
  • Observability — observe workflow runs and agent activity.