Interact in Service

When an order comes in, you need to inform the staff so that they can make ice cream, and when the ice cream is made, you need to notify the customer. Also, if the ice cream melts, you need to notify the customer again if they don't pick it up for a long time.
To achieve this, you need to add a service that sends periodic warnings if a served order is not finished, and a service that sends an alert message to the customer to remind them to pick up the order.

Declare Adapter

First, let's start by connecting an external API or module like an alert. You can connect an api that sends messages or emails, but in this tutorial, we'll create an api that simply prints to the console and connect it.
Modules like service, signal, document should not be directly connected to external systems, but rather created as adapters that are injected. First, let's create an adapter in the /nest folder as follows.
apps/koyo/nest/alarmApi.ts
1import { Logger } from "@akanjs/common";
2
3export class AlarmApi {
4  readonly #logger = new Logger("AlarmApi");
5  constructor(readonly name: string) {}
6
7  warn(message: string) {
8    this.#logger.warn(`${this.name}: ${message}`);
9  }
10}
Then, export the module in the /nest/index.ts file.
apps/koyo/nest/index.ts
1export * from "./alarmApi";

Use External API

Now, in order to inject the adapter for the alarm into the server, you need to declare and register the adapter to be injected in the option.ts file.
apps/koyo/lib/option.ts
1import { AlarmApi } from "@koyo/nest";
2
3// ...existing code...
4
5export const registerGlobalModule = (options: ModulesOptions) => {
6  return useGlobals({
7    uses: {
8      alarmApi: new AlarmApi(options.appName),
9    },
10    useAsyncs: {},
11  });
12};
Now, you can inject the alarmApi in the icecreamOrderService and use it.
apps/koyo/lib/icecreamOrder/icecreamOrder.service.ts
1import { serve } from "@akanjs/service";
2import { AlarmApi } from "@koyo/nest";
3
4import * as db from "../db";
5
6export class IcecreamOrderService extends serve(db.icecreamOrder, ({ use }) => ({
7  alarmApi: use<AlarmApi>(),
8})) {
9  // ...existing code...
10}

Query in Document

Now, let's add a service function that sends a warning message if a served order is not finished. For easy testing, let's send a warning message after 15 seconds if the order is served.
apps/koyo/lib/icecreamOrder/icecreamOrder.service.ts
1import { dayjs } from "@akanjs/base";
2// ...existing code...
3
4export class IcecreamOrderService extends serve(db.icecreamOrder) {
5  // ...existing code...
6
7  async warnIcecreamMeltingAll() {
8    const servedIcecreamOrders = await this.icecreamOrderModel.listByStatuses(["served"]);
9    for (const icecreamOrder of servedIcecreamOrders) {
10      if (icecreamOrder.createdAt.isAfter(dayjs().subtract(15, "seconds"))) continue;
11      this.alarmApi.warn(`IcecreamOrder ${icecreamOrder.id} is melting 😱`);
12    }
13  }
14}
However, how can we query only the served icecream orders? To do this, let's create a list query function by utilizing the feature of creating a query based on the given conditions in the icecreamOrder.document file.
Now, let's apply the query to the document.
apps/koyo/lib/icecreamOrder/icecreamOrder.document.ts
1// ...existing code...
2
3export class IcecreamOrderFilter extends from(cnst.IcecreamOrder, (filter) => ({
4  query: {
5    byStatuses: filter()
6      .opt("statuses", [cnst.IcecreamOrderStatus])
7      .query((statuses) => (statuses?.length ? { status: { $in: statuses } } : {})),
8  },
9  sort: {},
10})) {}
11
12// ...existing code...
By declaring the byStatuses query in IcecreamOrderFilter, you can use list, find, count, sample functions according to the given query conditions. For example, you can use listByStatuses, countByStatuses functions.

Use Interval

Now, let's use interval to execute the service every 15 seconds. The interval trigger is a trigger that works internally, and can be declared in IcecreamOrderInternal.
apps/koyo/lib/icecreamOrder/icecreamOrder.signal.ts
1
2// ...existing code...
3
4export class IcecreamOrderInternal extends internal(srv.icecreamOrder, ({ interval }) => ({
5  warnIcecreamMeltingAll: interval(10000).exec(async function () {
6    await this.icecreamOrderService.warnIcecreamMeltingAll();
7  }),
8})) {}
9
10// ...existing code...
Now, you can see the warning message printed to the console if the order is served for more than 15 seconds.
[AlarmApi] 44136 - 11/06/2025, 22:19:29 PM    WARN  myapp: IcecreamOrder 690c9f5d83050b6bb34b93bc is melting 😱 +331830ms
Released under the MIT License
Official Akan.js Consulting onAkansoft
Copyright © 2025 Akan.js. All rights reserved.
System managed bybassman