8.1 KiB
Copilot / AI Agent Instructions for ttscm-api
Purpose: make AI coding agents immediately productive in this repository by describing architecture, conventions, workflows, and helpful code pointers.
-- Big picture: This is a TypeScript API service (runs on Bun) using the Hono framework. The HTTP surface is implemented in src/api where small router files are mounted on a versioned router in src/api/server.ts (see the /v1 mount). Typical request flow is:
-
router(insrc/api/routers/*) →controller(insrc/controllers/*) →manager(insrc/managers/*) →module/generated/prismafor persistence and external integrations. -
Keep each layer focused: routers handle routing & middleware, controllers handle request validation and high-level orchestration, managers encapsulate domain/persistence logic, modules provide shared utilities (external API clients, helpers).
-
Runtime / tooling: The project runs on Bun. Dev command:
npm run dev(runsbun --watch src/index.ts). DB tooling uses Prisma; generated client lives undergenerated/prisma(do NOT edit generated files). Key scripts inpackage.json:dev— start the server with Bun in watch modedb:gen—prisma generatedb:push—prisma migrate dev --skip-generate
-
Data layer: Prisma schema is at
prisma/schema.prisma. The app imports the generated Prisma client fromgenerated/prisma/client.ts(orgenerated/prisma/browser.tsin browser contexts). Always run thedb:genscript after updatingschema.prisma. -
Routing & controllers: Example flow: a request hits a
routerinsrc/api/routers/*→ router delegates to acontrollerinsrc/controllers/*→ controller calls amanagerinsrc/managers/*for domain/persistence logic. Important concrete patterns:src/api/server.tsmountsv1and usesv1.route("/auth", require("./routers/authRouter").default)style requires; preserve this shape when adding routes.- Router files export a default Hono router object (CommonJS
module.exports/export defaultmixture is used across the codebase). - Controllers are single-export modules named like
CompanyController.tswith named methods per action (e.g.,fetch,update). Prefer small methods that call intomanagers/*. - Managers are thin domain layers (e.g.,
managers/companies.ts) that wrapgenerated/prismacalls and other I/O. Keep side effects here, keep controllers pure orchestration. - Use
src/modules/api-utils/apiResponse.tsfor every HTTP response shape (successful/created/error/internalError/zodError). - Use Zod schemas in controllers for request validation; server-level
app.onErrormapsZodErrortoapiResponse.zodError.
API layout & conventions (how to add a new endpoint)
- Add router: create
src/api/routers/<thing>Router.tsexporting a Hono router and mount it insrc/api/server.tsunder thev1router. - Add controller: create
src/controllers/<Thing>Controller.tsexporting functions for each action. Controllers should validate input with Zod, call managers, and returnapiResponse.*results. - Add manager: create
src/managers/<things>.tsfor persistence/domain logic. Use the generated Prisma client (generated/prisma/client.ts) here; do not import Prisma directly in controllers. - Add modules/types: if needed, add helpers to
src/modules/*and runtime types tosrc/types/*. - Middleware & auth: use
src/api/middleware/authorization.tsfor protecting routes; follow existing token/session patterns fromsrc/controllers/SessionController.tsandsrc/Errors/*. - Error handling: throw repository-specific errors from
src/Errors/*(includestatus,name,message, optionalcause) and letsrc/api/server.tsmap them viaapiResponse.error. - Naming: prefer plural manager filenames (
companies.ts) and singular controller names (CompanyController.ts) — follow existing files.
Examples & notable files
-
src/api/server.ts— mountsv1, registerscors, centralonErrorhandler andnotFoundresponse. -
src/api/routers/companyRouter.tsandsrc/controllers/CompanyController.ts— canonical example for adding company endpoints. -
src/api/user/@me/*— nested route example (use Hono sub-routers for subpaths). -
src/modules/cw-utils/*— external API integrations; keep interfaces stable and return domain objects consumed by managers. -
Validation & errors: Zod is used for input validation; Zod errors are handled centrally in
src/api/server.tsviaapiResponse.zodError. Application errors use custom error classes insrc/Errors/*(e.g.,AuthenticationError.ts,GenericError.ts). When creating errors, follow the shape used in existing errors (includestatus,name,message, and optionalcause). -
Response pattern: Use the
apiResponsehelpers insrc/modules/api-utils/apiResponse.tsfor formatting responses and status codes (successful, created, error, internalError, zodError). -
Auth & external integrations: Microsoft OAuth flow is under
src/api/auth/*andsrc/modules/fetchMicrosoftUser.ts. If touching authentication, follow existing redirect/refresh patterns insrc/api/authand preserve token-refresh semantics. -
ConnectWise integration: Utilities for ConnectWise interactions live in
src/modules/cw-utils/*(e.g.,fetchCompanyConfigurations.ts). These modules often call external APIs and return domain data; preserve the module contracts (input types and returned shapes) when refactoring. -
Generated files and CI:
generated/is a build artifact. Do not modify. When updating Prisma models, runnpm run db:genand commit changes togenerated/prismaonly if that's the established workflow. -
Files to inspect for context / examples:
src/api/server.ts— central error handling, router mounting, CORS and not-found handling.src/modules/api-utils/apiResponse.ts— response shaping used across controllers.src/controllers/CompanyController.ts— example controller calling managers.src/modules/cw-utils/fetchCompanyConfigurations.ts— example external integration utility.generated/prisma/client.ts— generated Prisma client imports; avoid editing.
-
Coding conventions & patterns specific to this repo:
- Prefer the existing layered architecture: routers → controllers → managers → modules.
- Use the
apiResponsehelpers for all HTTP responses. - Throw or propagate repository-specific custom errors (from
src/Errors/*) rather than returning bare objects. - Keep TypeScript types in
src/types/*and use Zod for runtime checks. - Avoid
elsestatements — prefer ternary operators, early returns, or other control flow patterns. Only useelseif there is absolutely no other way.
-
Local dev / quick checks:
- Start dev server:
npm run dev - Regenerate Prisma client:
npm run db:gen - Apply DB migrations locally:
npm run db:push
- Start dev server:
-
When editing generated or infra files: if you need to change
generated/prisma/*(rare), explain why in the PR and show commands used to regenerate. -
Documentation sync (IMPORTANT): Whenever you add, remove, or modify API routes or permission nodes, you must update all three of the following files to keep them in sync:
src/types/PermissionNodes.ts— the single source of truth for all permission node definitions, categories, descriptions, andusedInreferences.PERMISSIONS.md— human-readable documentation of all permission nodes; must strictly reflect the data inPermissionNodes.ts.API_ROUTES.md— comprehensive documentation of all API routes, including method, path, auth requirements, permissions, request/response examples. Always verify that new routes have their required permissions listed inPermissionNodes.ts, thatPERMISSIONS.mdtables match the TS file exactly, and thatAPI_ROUTES.mdincludes full documentation for every mounted route. Run through all three files at the end of any route or permission change to catch discrepancies.
If anything here is unclear or you'd like more examples (e.g., a walk-through editing a controller + manager + test run), tell me which area to expand and I'll iterate.