Use when scaffolding, auditing, or validating MetaSaver data service packages. Covers feature-based structure, service/controller/routes pattern, middleware integration, and API endpoint setup. Requires @metasaver/core-service-utils, {project}-contracts, {project}-database packages. File types: .ts, package.json.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
TEMPLATES.mdtemplates/Dockerfile.templatetemplates/auth.ts.templatetemplates/env.ts.templatetemplates/error.ts.templatetemplates/eslint.config.js.templatetemplates/feature-controller.ts.templatetemplates/feature-index.ts.templatetemplates/feature-service.ts.templatetemplates/features-index.ts.templatetemplates/index.ts.templatetemplates/logging.ts.templatetemplates/package.json.templatetemplates/register.ts.templatetemplates/server.ts.templatetemplates/tsconfig.json.templateThis skill documents the complete file and folder organization for MetaSaver data service packages (e.g., rugby-crm-service). Data services are Express-based API packages that coordinate between database clients and HTTP handlers using a feature-based structure.
Data services provide:
Use when:
packages/services/{project}-service/
├── package.json # Package metadata and scripts
├── tsconfig.json # TypeScript configuration
├── README.md # Package documentation
├── .env.example # Environment variable template
├── eslint.config.js # ESLint configuration (flat config)
├── Dockerfile # Docker build configuration
└── dist/ # Build output (generated)
src/
├── index.ts # Service entry point
├── env.ts # Environment configuration
├── server.ts # Express app factory
├── routes/
│ └── register.ts # Route registration
├── middleware/
│ ├── auth.ts # JWT auth middleware
│ ├── error.ts # Error handling middleware
│ └── logging.ts # Request logging middleware
└── features/
└── {feature}/
├── index.ts # Feature barrel export
├── {feature}.service.ts # Service class (Prisma operations)
└── {feature}.controller.ts # Controller (validation + response)
Complete Example:
packages/services/rugby-crm-service/
├── package.json
├── tsconfig.json
├── .env.example
├── eslint.config.js
├── README.md
├── Dockerfile
├── src/
│ ├── index.ts
│ ├── env.ts
│ ├── server.ts
│ ├── routes/
│ │ └── register.ts
│ ├── middleware/
│ │ ├── auth.ts
│ │ ├── error.ts
│ │ └── logging.ts
│ └── features/
│ ├── users/
│ │ ├── index.ts
│ │ ├── users.service.ts
│ │ └── users.controller.ts
│ ├── teams/
│ │ ├── index.ts
│ │ ├── teams.service.ts
│ │ └── teams.controller.ts
│ └── index.ts
└── dist/ # Build output (generated)
| Package | Purpose | Key Exports |
|---|---|---|
@metasaver/core-service-utils | Service factory and utilities | createService, ApiError, asyncHandler |
{project}-contracts | Zod validation schemas and types | Validation schemas, types |
{project}-database | Prisma client and database types | prisma, Prisma types |
express | HTTP framework | Express Router, middleware |
jsonwebtoken | JWT token creation and verification | sign, verify |
Required Fields:
name: @metasaver/{project}-service (always scoped)version: 0.1.0 (start with patch version)type: "module" (ESM packages)main: "./dist/index.js"types: "./dist/index.d.ts"Required Scripts:
build: TypeScript compilation (tsc -b)clean: Remove build artifactsdev: Run with tsx for development (tsx watch src/index.ts)start: Production server startuplint, lint:fix, lint:tsc, prettier, prettier:fixtest:unit: Unit tests (stub or actual)See templates/package.json.template for complete template.
Rule: Export a single env object with all configuration, type-safe with defaults.
Variable Naming: {PROJECT_UPPER}_{SETTING_NAME} (e.g., RUGBY_CRM_SERVICE_PORT)
See templates/env.ts.template for implementation.
Rule: Extend base config with proper ESM settings.
See templates/tsconfig.json.template for configuration.
Key Points:
include for eager-loading relationsgetAll, getById, create, update, deleteSee templates/feature-service.ts.template for implementation.
Key Points:
asyncHandler wraps async functions to catch errors automaticallyApiError.notFound() for consistent error responses{ data: {...} } for consistent response formatSee templates/feature-controller.ts.template for implementation.
Key Points:
/api/v1 with versioningSee templates/register.ts.template for implementation.
Key Points:
See templates/server.ts.template and templates/index.ts.template.
Rule: Export service and routes via barrel export.
export { {Feature}Service } from "./{feature}.service.js";
export { router as {Feature}Routes } from "./{feature}.controller.js";
See templates/feature-index.ts.template.
Create Package Directory
mkdir -p packages/services/{project}-service/src/{features,middleware,routes}
Create Configuration Files (use templates)
package.json, tsconfig.json, .env.example, eslint.config.js, DockerfileCreate Core Service Files (use templates)
src/env.ts, src/server.ts, src/index.tsCreate Middleware Files (use templates)
src/middleware/auth.ts, src/middleware/error.ts, src/middleware/logging.tsCreate Route Registration (use template)
src/routes/register.tsCreate Features (repeat per feature)
src/features/{feature}/index.tssrc/features/{feature}/{feature}.service.tssrc/features/{feature}/{feature}.controller.tsCreate Feature Index (aggregates all features)
src/features/index.tsTest Build and Run
pnpm --filter @metasaver/{project}-service build
pnpm --filter @metasaver/{project}-service dev
mkdir -p src/features/{feature}src/routes/register.tssrc/features/index.tspackages/services/{project}-service/src/features, src/middleware, src/routesdist/ (git-ignored)@metasaver/{project}-service (scoped)0.1.0 (semantic versioning)"module" (ESM)tsconfig.json extends @metasaver/core-typescript-config/basemodule: "ESNext")rootDir: "./src" and outDir: "./dist"src/env.ts exports single env object.env.example exists (committed)src/server.ts exports createServer() functionsrc/index.ts creates server and listens on env.PORTsrc/middleware/auth.ts exists (JWT verification)src/middleware/error.ts exists (error response formatting)src/middleware/logging.ts exists (request logging)src/routes/register.ts centralizes all route mounting/health check available without authentication/api/v1 versioning prefixindex.ts exports service and routesasyncHandler wrapperasyncHandler{ data: {...} }pnpm build succeeds without errorspnpm lint:tscViolation: Express handlers not wrapped with asyncHandler
// WRONG
router.get("/:id", async (req, res) => { ... });
// RIGHT
router.get("/:id", asyncHandler(async (req, res) => { ... }));
Violation: Service class with HTTP concerns
// WRONG - service knows about HTTP
async getUser(req: Request): Promise<Response> { ... }
// RIGHT - service is pure data layer
async getById(id: string): Promise<User | null> { ... }
Violation: Custom validation instead of Zod
// WRONG
if (!req.body.name || typeof req.body.name !== "string") { ... }
// RIGHT
const validated = CreateUserSchema.parse(req.body);
Violation: Direct environment variable access throughout code
// WRONG - scattered env access
const port = process.env.PORT;
// RIGHT - centralized env object
import { env } from "./env.js";