Backend
Use this skill when contributing to InsForge's backend package. This is for maintainers editing backend routes, services, providers, auth, database logic (including RLS-enforced surfaces like storage and realtime), schedules, or backend tests in the InsForge monorepo.From its SKILL.md
npx -y skills add InsForge/InsForge --skill backendAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
6.8 KB, as published. Nobody here has run it
InsForge Dev Backend
Use this skill for backend/ work in the InsForge repository.
Scope
backend/src/api/**backend/src/services/**backend/src/providers/**backend/src/infra/**backend/tests/**
Working Rules
-
Keep the route -> service -> provider/infra split intact.
- Routes handle auth, parsing, validation, and delegation.
- Services own business logic and orchestration.
- Providers and infra wrap external systems or lower-level integrations.
- Service layer code should be the only layer that interacts with the core PostgreSQL database.
- Do not put direct database access in routes.
- Do not bypass services when reading from or writing to Postgres.
-
Follow backend conventions.
- Use ESM-style
.jsimport specifiers in TypeScript source. - InsForge's core database is PostgreSQL.
- InsForge currently runs as a single-instance server, so be careful about introducing logic that assumes distributed coordination, cross-instance locking, or background worker separation.
- Reuse shared schemas from
@insforge/shared-schemaswhen contracts cross packages. - Use
safeParseplusAppErrorfor invalid input. - Return successful results through
successResponse. - Preserve existing auth middleware patterns such as
verifyAdmin,verifyUser, andverifyApiKey. - Never use the TypeScript
anytype. Prefer precise interfaces, schema-derived types,unknown, or constrained generics. - A new environment variable must be documented in the repository's single
.env.example. Every compose file reads that one file, so a variable missing from it is one self-hosters cannot discover — the S3 storage settings went undocumented that way for months. deploy/coolify/docker-compose.ymlanddeploy/dokploy/docker-compose.ymlcarry identical service definitions apart from two lines:INSFORGE_DEPLOYMENT_METHOD, which telemetry reads to tell the two platforms apart, and the buildcontext, which differs because Coolify builds with--project-directory <repo root>and Dokploy does not. Their header comments are per-platform by design. Change both, or one platform silently misses whatever you added.- For schema changes, write a new migration file instead of editing database structure manually.
- Put schema changes under
backend/src/infra/database/migrations/.
- Use ESM-style
-
Write idempotent migrations. Every SQL migration must be safe to re-run.
- Use
CREATE TABLE IF NOT EXISTS,CREATE INDEX IF NOT EXISTS,ADD COLUMN IF NOT EXISTS. - Never use bare
ALTER TABLE ... RENAME TO— it fails if the target name already exists. Wrap renames in aDOblock that checksinformation_schema.tablesfor both source and target. - Always
DROP TRIGGER IF EXISTSbeforeCREATE TRIGGER. - Guard data migrations and
DROP COLUMNbehindinformation_schema.columnschecks when the column may already be gone. - Use
ON CONFLICTorWHERE NOT EXISTSfor seedINSERTstatements.
- Use
-
Preserve existing behavior around mutation flows.
- Keep audit logging when surrounding routes already log state changes.
- Keep error handling flowing through shared middleware.
- Do not introduce a new response envelope unless the existing feature already uses one.
- For critical flows with multiple dependent database writes, use an explicit transactional process so the whole operation succeeds or fails together.
- Be especially careful with transactions around auth, secrets, billing-like usage updates, schema changes, and any flow that would leave the system inconsistent if partially applied.
-
Use Postgres Row Level Security, not app-side filters, for tables accessed via authenticated end-user routes (anything where
req.userreaches the service layer). RLS-enforced services such as storage, realtime, and payments should usewithUserContext. Tables accessed only by admin or service-internal paths (audit logs, billing aggregations) don't need RLS. Do not writeWHERE user_id = $1filters in services; let RLS evaluateauth.jwt() ->> 'sub'against the row.- Plumb identity through
withUserContext(pool, ctx, fn, settings?)fromservices/database/user-context.service.ts. It opens a transaction, setsSET LOCAL ROLEplus the canonicalrequest.jwt.claimsJSON GUC viaset_config, applies optional transaction-local settings such asrealtime.channel_name, runsfn, commits on success or rolls back on error, and resets role infinallyso policies see the calling user viaauth.jwt() ->> 'sub'. - Keep
UserContextuser-only and defined inapi/middlewares/auth.ts:{ id, role, email? }(idis always present at the API level). API keys and admin bypass flags do not belong insideUserContext. - Routes that issue out-of-band URLs (S3 presigned redirects, signed download links, anything the client redeems against a service that won't re-evaluate RLS) must do an explicit RLS-scoped existence check before handing the URL out — RLS does not fire when the client redeems the URL directly. See
StorageService.objectIsVisibleas the template. - Migrations that enable RLS on an existing populated table must auto-install a sensible default policy set so the upgrade does not silently break existing rows. See migration 036's
IF EXISTS (SELECT 1 FROM <table>) THEN <create policies> END IFpattern. - When adding a new RLS-enforced table: enable RLS,
GRANTtable-level CRUD toauthenticated, and write per-operation policies (SELECT, INSERT, UPDATE, DELETE). Public-bucket-style anonymous bypasses live at the route layer before calling the RLS helper, not in policies. - Normal raw SQL and custom migrations execute as
project_admin. It has service-key row visibility, but PostgreSQL grants and ownership still limit object access and DDL.
- Plumb identity through
-
Always write unit tests for new code.
- Every new feature, migration, service, or bug fix should have accompanying unit tests.
- For migrations, write tests that validate SQL structure and idempotency guards (see
tests/unit/redirect-url-whitelist-migration.test.tsfor the pattern). - For services, test business logic and error cases.
- For RLS-gated services, mock the pool/client and pin the SQL sequence (see
tests/unit/user-context.service.test.tsandtests/unit/storage-object-is-visible.test.ts). - Run the full test suite before submitting work:
cd backend && npm test.
Validation
cd backend && npm testcd backend && npm run build
For contract changes, also validate packages/shared-schemas/ and any affected dashboard consumers.