Modalin Backend is the API and persistence layer for Modalin, a product pre-order platform that helps Indonesian textile artisans begin production from confirmed demand and sufficient production capital.
The service owns the system's trusted operations:
- Authentication, sessions, roles, and onboarding
- Artisan-group profiles and validated profile, banner, and campaign image uploads
- Campaign, product, cost, target, capacity, and timeline persistence
- Public campaign discovery and artisan-owned campaign management
- Customer pre-orders and order history
- Midtrans Snap checkout creation, payment callbacks, and payment recovery state
- Production milestone and actual-expense updates with persisted evidence and receipt metadata
- Confirmation email delivery after a successful payment settlement
Modalin represents product purchases, not investments, lending, equity, or profit-sharing products.
| Area | Technology |
|---|---|
| Runtime | Node.js, TypeScript, TSX |
| HTTP API | Express 5 |
| Authentication | Better Auth with PostgreSQL persistence |
| Database | PostgreSQL and Prisma 7 |
| Validation | Zod |
| Payments | Midtrans Snap |
| Object Storage | AWS S3 or an S3-compatible service |
| Nodemailer over SMTP | |
| Security | Helmet, credentialed CORS, route authorization, and request validation |
| Tests | Node test runner with TSX |
| Base path | Access | Responsibility |
|---|---|---|
/api/health |
Public | Service and database health |
/api/auth/* |
Public/session | Better Auth endpoints |
/api/images |
Public | Stored public campaign and profile images |
/api/profile |
Authenticated | Profile, onboarding, avatar, and artisan-group banner operations |
/api/campaigns |
Mixed | Public campaign reads and artisan campaign management |
/api/finance |
Public | Campaign feasibility and financial calculations |
/api/orders |
Mixed | Order creation, customer history, payment continuation, and Midtrans notifications |
/api/dashboard |
Artisan | Campaign performance and order summaries |
/api/production |
Artisan | Milestone and expense updates |
/api/extractions |
Artisan | Structured extraction draft operations |
Every browser-provided value is validated at the API boundary. Authorization decisions remain in the backend even when the frontend hides an action.
Create .env from .env.example and replace every example secret.
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection URL |
BETTER_AUTH_SECRET |
Better Auth secret with at least 32 characters |
BETTER_AUTH_URL |
Public backend URL used by Better Auth |
FRONTEND_URL |
Public frontend URL and default allowed origin |
| Variable | Required when | Description |
|---|---|---|
PORT |
Optional | HTTP port; defaults to 4000 |
NODE_ENV |
Optional | development, test, or production |
CORS_ORIGIN |
Optional | Comma-separated allowed origins; defaults to FRONTEND_URL |
GOOGLE_CLIENT_ID |
Google login; production startup | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Google login; production startup | Google OAuth client secret |
MIDTRANS_SERVER_KEY |
Payment checkout and callbacks | Private Midtrans server key |
MIDTRANS_CLIENT_KEY |
Optional | Reserved configuration value; the backend currently uses the server key |
STORAGE_ENDPOINT |
S3-compatible storage | Optional custom endpoint; omit for AWS S3 |
STORAGE_REGION |
Image storage; production startup | Storage region |
STORAGE_BUCKET |
Image storage; production startup | Public image bucket |
STORAGE_ACCESS_KEY_ID |
Image storage; production startup | Storage access key |
STORAGE_SECRET_ACCESS_KEY |
Image storage; production startup | Storage secret key |
STORAGE_FORCE_PATH_STYLE |
Optional | Set true for providers that require path-style URLs |
SMTP_HOST |
Email; production startup | SMTP host |
SMTP_PORT |
Optional | SMTP port; defaults to 587 |
SMTP_SECURE |
Optional | Use a secure SMTP connection; defaults to false |
SMTP_USER |
Email; production startup | SMTP username |
SMTP_PASSWORD |
Email; production startup | SMTP password |
MAIL_FROM |
Email; production startup | Sender name and address |
Production startup fails when Google OAuth, object storage, or SMTP configuration is incomplete. Midtrans configuration is checked when a payment flow is used.
- Node.js and npm
- PostgreSQL
- Provider credentials for the flows you intend to run
npm ciGenerate the Prisma client and apply development migrations:
npm run prisma:generate
npm run prisma:migrateSet SHOWCASE_PASSWORD to an 8β128 character password, then seed the development showcase account:
npm run seedThe seed is safe to rerun and creates tenun.wanita@example.com with owned campaigns, orders, production milestones, assignments, and cost data.
For an existing production database, apply committed migrations without creating a new migration:
npm run prisma:deploynpm run devThe default API URL is http://localhost:4000.
npm run build
npm startnpm run typecheck
npm run buildFocused test files use Node's test runner through TSX:
npm exec tsx -- --test src/modules/order/order.service.test.tssrc/
|-- config/ # Environment, auth, Prisma, object storage, and mail clients
|-- generated/ # Generated Prisma client
|-- middlewares/ # Request logging and shared middleware
|-- modules/ # Domain routes, controllers, services, repositories, and validation
| |-- auth/
| |-- campaign/
| |-- dashboard/
| |-- extraction/
| |-- finance/
| |-- health/
| |-- image/
| |-- order/
| |-- payment/
| `-- production/
|-- utils/ # Error and HTTP response utilities
|-- app.ts # Express application and route mounts
`-- server.ts # Database connection and process lifecycle
prisma/
|-- migrations/ # Forward database migrations
|-- schema.prisma # PostgreSQL data model
|-- seed.ts # Controlled development seed
`-- seed-images.ts # Seed image metadata
- Apply all committed Prisma migrations before starting a newly deployed backend.
- Keep
BETTER_AUTH_SECRET,MIDTRANS_SERVER_KEY, storage secrets, SMTP credentials, and database credentials server-side. - Midtrans notifications update persisted payment state; the redirect page is not payment confirmation.
- A payment state marked for reconciliation must be resolved instead of creating a second provider transaction.
- Public image URLs are backed by configured object storage, not local process memory.
- SMTP delivery is part of successful payment communication and must be configured in production.