---
title: React to order events
description: Create an order, publish an event, and run a confirmation function.
pagination: false
sidebar:
  icon: radio
---

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

> **Before you begin**
>
> You'll need an [existing RELKIT application](/docs/learn/create-an-app) and Bun 1.3.10 or later.

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. ## 1. Install and bind the selected event runtime [#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:

    {/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

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

    ```sh
    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:

    ```ts title="src/platform/events.ts"
    /**
     * 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. ## 2. Add the event and its callers [#add-the-event-and-its-callers]

    ### Define the event

    ```ts title="src/services/orders/events/order-created.event.ts"
    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:

    ```ts title="src/services/orders/functions/price-order.function.ts"
    /** 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:

    ```ts title="src/services/orders/functions/create-order.function.ts"
    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

    ```ts title="src/services/orders/functions/order-confirmation.function.ts"
    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:

    ```ts title="src/services/orders/service.ts"
    /** 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`:

    ```ts title="src/routes/orders/route.ts"
    /** 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:

    ```sh
    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](https://github.com/rel-kit/relkit/blob/main/packages/testing/tests/compatibility/events.test.ts)

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

    ```sh
    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. ## 3. Add another reaction [#add-another-reaction]

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

    ```sh
    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](/docs/modules/operations/inspector), then open
    `orders.created` in its UI at `http://localhost:3210` to see both subscribers under **Consumer functions**.

## Related tasks

[Run a durable task](/docs/modules/jobs/quickstart) when follow-up work must survive a process
restart. For transactional database changes, follow [Register users in a
transaction](/guides/database/register-users-in-a-transaction).
