Observers
In Harpia, Observers provide a powerful mechanism to listen for and react to database events using Prisma’s extension capabilities. They allow you to trigger side effects automatically whenever a specific database operation occurs, such as sending a welcome email after a user is created or invalidating a cache when a record is updated.
This approach ensures that your core business logic (Services) remains strictly decoupled from secondary side effects.
Generating an Observer
Observers are defined within the app/observers/ directory. You can quickly generate a new observer using the Harpia CLI:
bun harpia generate observer user
Alternatively, using the shorthand:
bun g observer user
This command scaffolds a new observer file (user.observer.ts) tailored for your specified model.
Defining Triggers
Harpia’s Observer system uses a fluent registration API. An observer file imports the global Observer object and registers a callback for a specific model and operation via the Observer.model() method.
// app/observers/user.observer.ts
import { Observer } from "app/observers";
/**
* Observer for the User model.
*/
Observer.model("User", "create", async ({ data, model, operation }) => {
/**
* This callback runs every time a `create` operation is successfully called
* on the User model.
*
* Add your custom logic here, like logging, triggering side effects, etc.
*/
console.log(`A new ${model} was created!`);
// Example: Triggering an email service
// await mailService.sendWelcomeEmail(data.email);
});
The Callback Arguments
The asynchronous callback function receives a single context object containing three properties:
data: The exact arguments passed to the underlying Prisma operation (e.g.,data,where,select).model: A string representing the name of the model being operated on (e.g.,"User").operation: A string representing the executed Prisma operation (e.g.,"create").
Auto-Discovery Mechanism
You do not need to manually import your observer files anywhere in your application.
Under the hood, Harpia uses an ObserverManager (located in app/observers/index.ts) which recursively scans for any file ending in .observer.ts during the server boot phase (start/server.ts). This guarantees that your side-effect logic is automatically discovered, registered, and bound to the Prisma Client Extension invisibly.
Supported Operations
You can hook into any of the standard Prisma operations. The operation argument in Observer.model() strongly types these events, including but not limited to:
create,createMany,createManyAndReturnupdate,updateManydelete,deleteManyupsertfindUnique,findFirst,findMany
Simply register a new Observer.model() block for every distinct operation you wish to observe.