A production-ready NestJS backend application for capturing and reviewing human decisions, featuring projects, member invites, and a structured decision log.
- RESTful API for projects, invites, and decisions
- TypeScript with strict typing throughout
- Modular Architecture with clear separation of concerns
- Input Validation with comprehensive error handling
- Consistent API Response Format across all endpoints
- Environment-based Configuration for dev/prod environments
- PostgreSQL + Prisma with clean data access patterns
- Auth with email verification and password reset
src/
auth/ # Auth module (sign-up, sign-in, verification, reset)
decisions/ # Decision logging module
invites/ # Project invite module
lib/ # Shared auth setup
pipes/ # Validation pipes
prisma/ # Prisma module/service
projects/ # Project module
types/ # Shared types
utils/ # RabbitMQ + email helpers
app.module.ts # Root Nest module
main.ts # Application entry point
redis.ts # Redis client
The application follows NestJS module boundaries to keep concerns isolated:
- Controllers: Handle HTTP requests/responses only
- Services: Contain business logic and validation
- Modules: Wire dependencies and encapsulate features
- DTOs + Zod: Validate request input at the edge
This design keeps each module focused and testable.
Prisma is used for type-safe access to PostgreSQL. It provides:
- Strong typing from the schema
- Consistent data access patterns
- Easy migrations and schema evolution
Errors are surfaced through NestJS exceptions which:
- Normalize API error responses
- Avoid leaking internal details
- Make controller logic consistent
Zod schemas power the validation pipe to:
- Validate request bodies and params
- Provide clear errors on invalid input
- Keep validation logic close to DTOs
Environment-based configuration keeps secrets and environment settings separate from code using .env.
Base path: /api/v1
POST /auth/sign-up/emailGET /auth/verify-emailPOST /auth/sign-in/emailPOST /auth/sign-outPOST /auth/resend-verify-emailPOST /auth/request-password-resetPOST /auth/reset-password/:tokenPOST /auth/change-passwordGET /auth/get-session
POST /projectsGET /projects(paginated)GET /projects/mine(paginated)GET /projects/:idPUT /projects/:idDELETE /projects/:id
POST /decisionsGET /decisions(requiresprojectId, paginated)GET /decisions/:idPUT /decisions/:idDELETE /decisions/:id
POST /invitesGET /invites/me(paginated)GET /invites/:projectIdPOST /invites/:inviteId/acceptPOST /invites/:inviteId/rejectDELETE /invites/:inviteId
- Node.js
- pnpm
- PostgreSQL
- Redis
- RabbitMQ
-
Install dependencies
pnpm install --frozen-lockfile
-
Set up environment variables
cp .env.example .env
Edit
.envwith your configuration. -
Run Prisma migrations and generate client
pnpm prisma migrate dev pnpm prisma generate
-
Start the application
# Development pnpm run start:dev # Production pnpm run build pnpm run start:prod
All API responses follow a consistent format:
{
"success": true,
"message": "...",
"data": { ... },
"error": null
}{
"success": false,
"message": "...",
"data": null,
"error": { ... }
}interface Project {
id: string;
name: string;
adminId: string;
description?: string;
isPublic: boolean;
}interface Decision {
id: string;
projectId: string;
actorType: 'admin' | 'member';
actorId: string;
action: string;
reason: string;
context?: Record<string, unknown>;
outcome: string;
createdAt: Date;
}interface ProjectInvite {
id: string;
projectId: string;
invitedBy: string;
target: string;
status: 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'REVOKED';
createdAt: Date;
expiresAt: Date;
respondedAt?: Date;
}Email verification and password reset emails are queued through RabbitMQ (sendVerificationEmail queue) and delivered via Resend. Redis is used for rate limiting and email send throttling.
Contributions, issues, and feature requests are welcome! If you want to contribute to Find Decisions, please follow the guidelines outlined in the contributing.md file.
MIT License