···11-import { Global, Module } from "@nestjs/common";
11+import { Module } from "@nestjs/common";
22+import { CursorService } from "./cursor.service";
33+import { PaginationService } from "./pagination.service";
2433-@Global()
45@Module({
55- providers: [],
66- exports: [],
66+ providers: [PaginationService, CursorService],
77+ exports: [PaginationService, CursorService],
78})
89export class BaseModule {}
+25
apps/server/src/modules/base/connection.types.ts
···11+export abstract class BaseEdge<T> {
22+ cursor: string;
33+ node: T;
44+55+ constructor(cursor: string, node: T) {
66+ this.cursor = cursor;
77+ this.node = node;
88+ }
99+1010+ /**
1111+ * Static factory method to create an edge from pagination result
1212+ */
1313+ static fromPaginationEdge<T, TEdge extends BaseEdge<T>>(
1414+ this: new (
1515+ cursor: string,
1616+ node: T,
1717+ ) => TEdge,
1818+ edge: { node: T; cursor: string },
1919+ ): TEdge {
2020+ // biome-ignore lint/complexity/noThisInStatic: this is intentional for the factory pattern
2121+ return new this(edge.cursor, edge.node);
2222+ }
2323+}
2424+2525+// Base classes for inheritance - concrete classes define their own GraphQL types
+18
apps/server/src/modules/base/cursor.service.ts
···11+import { Injectable } from "@nestjs/common";
22+33+@Injectable()
44+export class CursorService {
55+ /**
66+ * Encode an ID to a cursor
77+ */
88+ encode(id: string): string {
99+ return Buffer.from(id).toString("base64");
1010+ }
1111+1212+ /**
1313+ * Decode a cursor to get the ID
1414+ */
1515+ decode(cursor: string): string {
1616+ return Buffer.from(cursor, "base64").toString("utf-8");
1717+ }
1818+}
+18
apps/server/src/modules/base/not-found.util.ts
···11+import { NotFoundException } from "@nestjs/common";
22+33+/**
44+ * Utility function to throw a NotFoundException with a consistent message
55+ * @param entityName - The name of the entity that was not found
66+ * @param property - The property that was searched (e.g., "id", "email", "name")
77+ * @param value - The value that was searched for
88+ * @throws {NotFoundException}
99+ */
1010+export const notFound = (
1111+ entityName: string,
1212+ property: string,
1313+ value: string,
1414+): never => {
1515+ throw new NotFoundException(
1616+ `${entityName} with ${property} ${value} not found`,
1717+ );
1818+};