Package Exports
- @remediator/core
Readme
@remediator/core
โก CQRS-style mediator pattern for frontend apps (Remix, React Router 7, Vite/Webpack support)
Minimal, convention-based mediator designed for frontend frameworks like Remix and React Router 7. Automatically registers your *.mediator.ts
files for clean, scalable architecture.
๐ง Install
npm install @remediator/core
๐ Quick Start
1. Create a Query or Command
// /app/user/GetUserQuery.mediator.ts
export class GetUserQuery {
constructor(public id?: string) {}
}
export class GetUserQueryHandler {
async handle(query: GetUserQuery) {
// fetch user data here
return { id: query.id, name: "Jeremy" };
}
}
โ File name must end in
.mediator.ts
โ Class names must follow this convention:
XQuery
+XQueryHandler
XCommand
+XCommandHandler
Every Query
or Command
must have a corresponding Handler
class in the same file.
2. Auto-Register Your Files
In Remix (e.g. server.entry.ts
or root loader):
import { registerAll } from "@remediator/core";
registerAll();
In React Router 7:
import { registerAll } from "@remediator/core";
export function loader() {
registerAll(); // Safe to call multiple times (runs once)
}
3. Send a Command or Query
โ Option 1: Classic
await reMediator.send(new GetUserQuery("u1"), request);
โ Option 2: Plain Object (auto mapped)
await reMediator.send<GetUserQuery>({ id: "u1" }, request);
โ Option 3: Decorator + Named Usage
@RemediatorRequest("GetUserQuery")
export class GetUserQuery {
constructor(public id?: string) {}
}
await reMediator.send("GetUserQuery", { id: "u1" }, request);
๐ก Using Middleware
Middleware lets you inject behavior (auth, logging, etc.) into the send pipeline.
import type { Pipeline } from "@remediator/core";
import { getSession } from "./session.server";
import { UnauthorizedError } from "~/shared/lib/errors.server";
const publicRoutes = ["/login", "/register", "/forgot-password"];
export const authMiddleware: Pipeline = async (req, context, next) => {
const cookie = context.rawRequest.headers.get("Cookie");
const session = await getSession(cookie);
const userId = session.get("userId");
if (publicRoutes.includes(context.rawRequest.url)) {
return next();
}
if (!userId) {
throw new UnauthorizedError();
}
context.userId = userId;
return next();
};
Register it:
reMediator.use(authMiddleware);
๐งช Usage Examples in Remix
โ Loader Example
const user = await reMediator.send(new GetUserQuery(), request);
const cycles = await reMediator.send(new GetCyclesForUserQuery(), request);
const projects = await reMediator.send(new GetProjectsForUserQuery(), request);
โ Action Example
const formData = await request.formData();
const data = Object.fromEntries(formData);
const result = await reMediator.send(
new ProjectUpsertCommand(data.id, data.name),
request
);
๐ง send()
Arguments
reMediator.send(request: IRequest | object | string, rawRequest: Request, middleware?: Array<Pipeline | string>)
- Pass a class instance, plain object, or registered name (with
@RemediatorRequest
) - Optional middleware can be
[]
,["name"]
, or[func]
โ Summary of Conventions
Type | File Format | Class Format |
---|---|---|
Command | *.mediator.ts |
XCommand + XCommandHandler |
Query | *.mediator.ts |
XQuery + XQueryHandler |
Middleware | *.mediator.ts |
Ends with Middleware function name |
๐ฆ Project Structure Example
/app
/auth
AuthMiddleware.mediator.ts
/GetUser
GetUserQuery.mediator.ts
GetUserQueryHandler.mediator.ts
/Project
UpsertProjectCommand.mediator.ts
UpsertProjectCommandHandler.mediator.ts
๐ฏ The Purpose of Mediator (@remediator/core
)
- โ Promotes thin loaders/actions
- โ Enables clean separation of concerns
- โ Centralizes cross-cutting concerns (via middleware)
- โ Supports reusable, testable request/response structures
๐ค Why Not Just Use a Class?
const result = await new GetUserQuery().fetch();
But that:
- Couples logic to routes
- Makes testing harder
- Scales poorly with cross-cutting logic
- Encourages inconsistent patterns
โ Mediator FTW
await reMediator.send(new GetUserQuery(), request);
- Feels declarative and clean
- Consistent across commands/queries
- Easily extended via middleware
โจ Credits
Created by @remediator โ solving CQRS needs for modern frontend frameworks.