UNPKG

@accounter/server

Version:
72 lines (54 loc) 3.21 kB
--- name: graphql-module description: When creating, modifying, or debugging GraphQL modules, resolvers, providers, or type definitions in the server package --- # GraphQL Module Patterns ## Module Anatomy Each module in `src/modules/<name>/` contains: - `typeDefs/<name>.graphql.ts` — schema using `gql` tag from `graphql-modules` - `resolvers/<name>.resolver.ts` — query, mutation, and field resolvers - `providers/<name>.provider.ts` — data access layer with `@Injectable()` - `helpers/` — module-specific utility functions - `types.ts` — re-exports generated types from `__generated__/` - `index.ts``createModule()` registration See `references/` for annotated examples of each file. ## Provider Injection - Providers use `@Injectable({ scope: Scope.Operation, global: true })` from `graphql-modules` - Constructor injection for dependencies: `constructor(private db: TenantAwareDBClient, ...)` - Database access via `TenantAwareDBClient` (tenant-scoped) — never raw Postgres clients - Admin context via `AdminContextProvider` when owner/tenant ID is needed ## SQL Queries (PgTyped — mandatory) - **Avoid inline SQL** with raw pg or tagged strings without a type parameter. - All SQL better use `sql<IQueryType>` from `@pgtyped/runtime`, defined as module-level constants **outside** the class. - The `IQueryType` interface is generated by `yarn generate` from the SQL — never hand-write it. - Call queries via `.run(params, this.db)` inside provider methods. - See `references/example-provider.ts` for the correct pattern. ## DataLoader for N+1 Prevention - Every provider that fetches by ID must expose a DataLoader (`new DataLoader(keys => this.batchFn(keys))`) - Batch function receives `readonly` array of keys, returns results in matching order - Prime the loader cache when fetching all records (e.g., `getAll` primes individual loaders) - Call `clearAll()` on loaders after mutations to invalidate cache ## Field Resolvers - Field resolvers on extended types resolve cross-module relationships - Access the parent's DB fields (snake_case) to load related data via providers - Return `null` explicitly when the foreign key is missing — don't throw ## Type Generation - Types come from `__generated__/types.ts` (graphql-codegen) and `__generated__/<name>.types.ts` (pgtyped) - Module resolver type: `<ModuleName>Module.Resolvers` from `./types.js` - Never hand-write GraphQL resolver types — always import generated ones - Run `yarn generate` after any schema or SQL change ## Gotchas - Always import with `.js` extension — even for `.ts` source files (ESM requirement) - Never access DB directly from resolvers — always go through providers - `__generated__/` is git-ignored — run `yarn generate` before working on a module for the first time - Provider classes must have `@Injectable()` decorator or DI fails silently at runtime - SQL queries use `@pgtyped/runtime`'s `sql` tagged template — types are generated from the SQL, not hand-written - Auth directives (`@requiresAuth`, `@requiresAnyRole`) go on schema fields, not in resolver code - `Scope.Operation` ensures one provider instance per GraphQL operation (request-scoped)