Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

React to order events

Create an order, publish an event, and run a confirmation function.

Create an order, publish its details, and run a confirmation function. This example logs the reaction without sending email or writing to a database.

Create the files shown below. If your app already has the Orders example, update those files instead. Run all commands from your app’s root.

1. Install and bind the selected event runtime

The manual path below uses the file-backed local event integration, which is not selected by the minimal app. Install its owner first; no Docker or model SDK is needed:

From this guide’s application/package root, install only its selected requirements:

bun add @relkit/local@0.7.12

Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.

Create src/platform/events.ts from this descriptor:

/**
 * Describes the explicit file-backed event profile used by the manual tutorial.
 * The app config imports this value; the selected runtime activates it after
 * checking, without adding a Docker dependency or enabling inspection.
 */
import { localEvent } from "@relkit/local";

export const event = localEvent();

Import event from @app/platform/events.js into the existing relkit.config.ts. Add it as the singular event option and merge event: "default" into defaults, preserving other profiles and settings. The event scaffold can install/configure this selected baseline automatically; manual source copying does not. With --no-install, complete bun install and bun run check first.

2. Add the event and its callers

Define the event

import { defineEvent } from "@relkit/app/events";
import { z } from "@relkit/app/schema";

const orderCreated = defineEvent({
  id: "orders.created",
  version: 1,
  input: z.object({
    orderId: z.string().min(1),
    sku: z.string().min(1),
    totalCents: z.number().int().nonnegative(),
  }),
});

export default orderCreated;

orders.created carries the order ID, product code, and total.

Publish after creating the order

Add a pricing function. For this example, each item costs 100 cents:

/** Calculates integer order totals at 100 cents per item for the create-order function. */
import { defineFunction } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";

const priceOrder = defineFunction({
  input: z.object({ quantity: z.number().int().positive() }),
  output: z.object({ totalCents: z.number().int().nonnegative() }),
  handler: ({ quantity }) => ({ totalCents: quantity * 100 }),
});

export default priceOrder;

Create the order and publish its details:

import { defineFunction } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";

const createOrder = defineFunction({
  input: z.object({
    orderId: z.string().min(1),
    sku: z.string().min(1),
    quantity: z.number().int().positive(),
  }),
  output: z.object({
    orderId: z.string(),
    sku: z.string(),
    totalCents: z.number().int().nonnegative(),
  }),
  publishes: ["orders.created"],
  handler: async (input, context) => {
    const order = { orderId: input.orderId, sku: input.sku, totalCents: input.quantity * 100 };
    await context.events["orders.created"].publish(order);
    return order;
  },
});

export default createOrder;

publishes allows this function to send orders.created. The handler returns the order without waiting for the confirmation.

Consume the event

import { defineEventFunction } from "@relkit/app/events";

const orderConfirmation = defineEventFunction({
  id: "orders.confirmation",
  event: "orders.created",
  handler: async (order, context) => {
    context.log.info("order.confirmation.requested", { orderId: order.orderId });
  },
});

export default orderConfirmation;

event: "orders.created" subscribes this function to the event. Its handler receives the order details.

Add an endpoint

Expose the order-creating function through an Orders service. If you already have this service, add these members alongside your existing ones:

/** Declares the orders service boundary and its public members. */
import { defineService } from "@relkit/app/services";
import createOrder from "@app/services/orders/functions/create-order.function.js";
import priceOrder from "@app/services/orders/functions/price-order.function.js";

export default defineService({
  functions: { createOrder, priceOrder },
});

Connect it to POST /orders:

/** Maps the HTTP endpoint to its owning service's public member. */
import { defineServiceRoutes } from "@relkit/app/routes";
import orders from "@app/services/orders/service.js";

export const { POST } = defineServiceRoutes(orders, {
  POST: { member: "createOrder", successStatus: 201 },
});

Check and test delivery

Configure a compatible event adapter under singular event, then check the graph:

bun run check
bun run typecheck

No environment mode silently substitutes an event provider. For a credential-free behavior test, use the deterministic event harness:

Event-harness regression test

Start the application with bun run dev. In a second terminal, create an order:

curl -i -X POST http://localhost:3000/orders \
  -H 'content-type: application/json' \
  -d '{"orderId":"order-100","sku":"book","quantity":2}'

Expect HTTP 201 and totalCents: 200. In the application terminal, look for order.confirmation.requested with orderId=order-100. The response confirms publication was accepted; the log separately confirms that the consumer ran.

3. Add another reaction

In a second terminal, open your app’s root directory and copy the consumer:

cp src/services/orders/functions/order-confirmation.function.ts src/services/orders/functions/order-analytics.function.ts

Change the copy’s id to orders.analytics and its log message to order.analytics.recorded. Keep event: "orders.created".

After configuring an event binding and starting the app, call the publishing route with orderId set to order-101. Look for both order.confirmation.requested and order.analytics.recorded with that ID. You added a reaction without changing the publishing function.

Optionally complete the separate Inspector setup, then open orders.created in its UI at http://localhost:3210 to see both subscribers under Consumer functions.

Run a durable task when follow-up work must survive a process restart. For transactional database changes, follow Register users in a transaction.