# CA Grow Ops Manager — Backend **Version**: 0.1.0 **Stack**: TypeScript, Node.js, Express/Fastify, PostgreSQL, Prisma --- ## Overview The backend is a **feature-first, domain-driven** Node.js application that provides typed HTTP/JSON APIs for all platform features. It uses Prisma ORM for type-safe database access and follows strict security and testing practices. --- ## Tech Stack - **Language**: TypeScript 5.x - **Runtime**: Node.js 20.x LTS - **Framework**: Express or Fastify (to be decided during implementation) - **Database**: PostgreSQL 15.x - **ORM**: Prisma 5.x - **Authentication**: JWT (access + refresh tokens) - **Testing**: Jest + Supertest - **Validation**: Zod or Joi - **Logging**: Winston or Pino - **Email**: SendGrid or Mailgun --- ## Project Structure ``` backend/ ├── src/ │ ├── modules/ # Feature-first modules │ │ ├── cultivation-ops/ # Tasks, batches, rooms │ │ │ ├── tasks/ │ │ │ │ ├── tasks.controller.ts │ │ │ │ ├── tasks.service.ts │ │ │ │ ├── tasks.schema.ts │ │ │ │ └── tasks.test.ts │ │ │ ├── batches/ │ │ │ └── rooms/ │ │ ├── compliance/ # Documents, audit packets │ │ ├── labor/ # Timeclock, hours, reports │ │ ├── inventory/ # Materials, lots, transactions │ │ ├── integrations/ # METRC, environmental, hardware │ │ ├── communications/ # Comments, announcements, notifications │ │ └── auth/ # Authentication, authorization │ ├── shared/ # Shared utilities │ │ ├── middleware/ # Auth, validation, error handling │ │ ├── utils/ # Helpers, formatters │ │ └── types/ # Shared TypeScript types │ ├── prisma/ # Prisma schema and migrations │ │ ├── schema.prisma │ │ └── migrations/ │ ├── config/ # Configuration │ │ ├── database.ts │ │ ├── auth.ts │ │ └── email.ts │ ├── app.ts # Express/Fastify app setup │ └── server.ts # Server entry point ├── tests/ # Integration tests │ ├── setup.ts │ └── integration/ ├── .env.example # Environment variables template ├── package.json ├── tsconfig.json └── README.md ``` --- ## Domain Modules Each module follows this structure: ``` module-name/ ├── entity.controller.ts # HTTP route handlers ├── entity.service.ts # Business logic ├── entity.schema.ts # Validation schemas (Zod/Joi) ├── entity.test.ts # Unit tests └── index.ts # Module exports ``` ### Example: Tasks Module ```typescript // tasks.controller.ts export const getTasks = async (req, res) => { const tasks = await tasksService.findAll(req.query); res.json(tasks); }; // tasks.service.ts export const findAll = async (filters) => { return prisma.task.findMany({ where: filters }); }; // tasks.schema.ts export const createTaskSchema = z.object({ name: z.string().min(1), dueDate: z.string().datetime(), assigneeId: z.string().optional(), }); ``` --- ## API Design ### RESTful Conventions - `GET /api/tasks` - List tasks - `GET /api/tasks/:id` - Get task detail - `POST /api/tasks` - Create task - `PATCH /api/tasks/:id` - Update task - `DELETE /api/tasks/:id` - Delete task ### Response Format ```json { "success": true, "data": { ... }, "meta": { "page": 1, "pageSize": 20, "total": 100 } } ``` ### Error Format ```json { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid input", "details": [ { "field": "dueDate", "message": "Required" } ] } } ``` --- ## Authentication & Authorization ### JWT-Based Auth - **Access Token**: 15-minute expiry, stored in httpOnly cookie - **Refresh Token**: 7-day expiry, stored in localStorage - **Token Payload**: `{ userId, roleId, iat, exp }` ### RBAC (Role-Based Access Control) Roles: - `Owner`: Full access - `Compliance Manager`: Full compliance, read-only operations - `Head Grower`: Full cultivation ops, read-only compliance - `Staff`: Limited access to tasks and timeclock - `Accountant`: Read-only reports Middleware: ```typescript export const requireRole = (roles: string[]) => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Forbidden' }); } next(); }; }; ``` --- ## Database (Prisma) ### Schema Example ```prisma model Task { id String @id @default(cuid()) name String description String? status TaskStatus @default(PENDING) dueDate DateTime assigneeId String? assignee User? @relation(fields: [assigneeId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum TaskStatus { PENDING IN_PROGRESS COMPLETE OVERDUE } ``` ### Migrations ```bash # Create migration npx prisma migrate dev --name add_tasks_table # Apply migrations npx prisma migrate deploy # Generate Prisma Client npx prisma generate ``` --- ## Testing ### Unit Tests (Jest) ```typescript describe('TasksService', () => { it('should create a task', async () => { const task = await tasksService.create({ name: 'Water plants', dueDate: new Date(), }); expect(task.name).toBe('Water plants'); }); }); ``` ### Integration Tests (Supertest) ```typescript describe('POST /api/tasks', () => { it('should create a task', async () => { const res = await request(app) .post('/api/tasks') .send({ name: 'Water plants', dueDate: '2025-12-10' }) .expect(201); expect(res.body.data.name).toBe('Water plants'); }); }); ``` --- ## Environment Variables ```bash # Database DATABASE_URL=postgresql://user:password@localhost:5432/ca_grow_ops # Auth JWT_SECRET=your-secret-key JWT_ACCESS_EXPIRY=15m JWT_REFRESH_EXPIRY=7d # Email EMAIL_SERVICE=sendgrid EMAIL_API_KEY=your-api-key EMAIL_FROM=noreply@example.com # Integrations METRC_API_KEY=your-metrc-key METRC_API_URL=https://api-ca.metrc.com # Server PORT=3000 NODE_ENV=development ``` --- ## Development Workflow ### Setup ```bash # Install dependencies npm install # Set up environment variables cp .env.example .env # Run database migrations npx prisma migrate dev # Seed database (optional) npx prisma db seed # Start dev server npm run dev ``` ### Scripts ```json { "scripts": { "dev": "tsx watch src/server.ts", "build": "tsc", "start": "node dist/server.js", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", "lint": "eslint src --ext .ts", "format": "prettier --write src", "migrate": "prisma migrate dev", "migrate:deploy": "prisma migrate deploy", "prisma:generate": "prisma generate" } } ``` --- ## Security Best Practices - **Input Validation**: Validate all inputs with Zod/Joi - **SQL Injection**: Use Prisma parameterized queries - **XSS**: Sanitize user inputs; use Content Security Policy - **CSRF**: Use CSRF tokens for state-changing operations - **Rate Limiting**: Limit API requests per user/IP - **Audit Logging**: Log all critical operations --- ## Deployment ### Docker ```dockerfile FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npx prisma generate RUN npm run build CMD ["npm", "start"] ``` ### Docker Compose ```yaml version: '3.8' services: backend: build: . ports: - "3000:3000" environment: DATABASE_URL: postgresql://user:password@db:5432/ca_grow_ops depends_on: - db db: image: postgres:15 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: ca_grow_ops volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data: ``` --- ## Next Steps 1. **Choose Framework**: Decide between Express and Fastify 2. **Set Up Project**: Initialize Node.js project with TypeScript 3. **Configure Prisma**: Set up Prisma schema and migrations 4. **Implement Auth**: Build authentication and RBAC 5. **Build Modules**: Implement feature modules per spec 6. **Write Tests**: Unit and integration tests for all modules 7. **Deploy**: Set up CI/CD and deploy to staging --- ## Resources - [Prisma Documentation](https://www.prisma.io/docs) - [Express Documentation](https://expressjs.com) - [Fastify Documentation](https://www.fastify.io) - [Jest Documentation](https://jestjs.io) - [TypeScript Documentation](https://www.typescriptlang.org/docs)