A modern, full-stack subscription management application built with Turborepo, Node.js/Express, Next.js, MongoDB, and Google Gemini AI.
Loopify is a sophisticated monorepo-based application that helps users manage their subscriptions efficiently. It features user authentication, subscription tracking, AI-powered responses, and automated email reminders.
- Turborepo ^2.6.1 - Monorepo management and task orchestration
- PNPM ^10.19.0 - Fast, disk-space-efficient package manager
- TypeScript 5.9+ - Full static type checking across the project
- Node.js ≥18 - JavaScript runtime
- Express ^5.1.0 - Web application framework
- MongoDB - NoSQL database
- Mongoose ^9.0.0 - MongoDB object modeling
- Next.js ^16.1.4 - React framework with SSR/SSG
- React ^19.2.0 - UI library
- Next.js Auth ^4.24.13 - Authentication solutions
- Google Gemini AI (@google/genai) - AI-powered responses
- Google Auth Library ^10.5.0 - OAuth2 authentication
- Nodemailer ^7.0.12 - Email sending service
- Tailwind CSS ^4.1.5 - Utility-first CSS framework
- Tailwind PostCSS ^4.1.5 - PostCSS plugin for Tailwind
- Custom Theme - Blue (#2a8af6), Purple (#a853ba), Red (#e92a67)
- Bcrypt ^6.0.0 - Password hashing
- JSON Web Tokens (JWT) ^9.0.2 - Secure token-based authentication
- Zod ^4.1.13 - TypeScript-first schema validation
- Nodemon ^3.1.11 - Auto-reload during development
- TSX ^4.21.0 - TypeScript execution for Node.js
- ESLint - Code linting
- Prettier - Code formatting
loopify/
├── apps/
│ ├── server/ # Express API backend
│ │ ├── src/
│ │ │ ├── index.ts # Main entry point
│ │ │ ├── database/
│ │ │ │ └── connection.ts # MongoDB connection
│ │ │ ├── controllers/
│ │ │ │ ├── login.controller.ts
│ │ │ │ ├── signup.controller.ts
│ │ │ │ ├── subscription.controller.ts
│ │ │ │ ├── response.controller.ts (AI)
│ │ │ │ └── mailer/ # Email controllers
│ │ │ ├── routes/
│ │ │ │ ├── login.routes.ts
│ │ │ │ ├── signup.routes.ts
│ │ │ │ ├── subscription.routes.ts
│ │ │ │ ├── response.routes.ts
│ │ │ │ ├── reminderMail.routes.ts
│ │ │ │ └── authentication/
│ │ │ ├── models/
│ │ │ │ ├── signupModel.ts # User schema
│ │ │ │ └── subscription.model.ts
│ │ │ ├── schema/
│ │ │ │ ├── signup.schema.ts
│ │ │ │ └── subscription.schemas.ts
│ │ │ ├── authentication/
│ │ │ │ ├── middleware.authentication.ts
│ │ │ │ └── verify-otp.authentication.ts
│ │ │ ├── integrations/
│ │ │ │ └── geminiClient.ts # Google Gemini AI
│ │ │ ├── interface/ # TypeScript interfaces
│ │ │ ├── utils/
│ │ │ │ ├── services/
│ │ │ │ │ ├── mailer.ts
│ │ │ │ │ └── reminderMailer.ts
│ │ │ │ ├── password/ # Bcrypt utilities
│ │ │ │ ├── token/ # JWT utilities
│ │ │ │ ├── signup/ # Signup logic
│ │ │ │ ├── login/ # Login logic
│ │ │ │ ├── otp/ # OTP generation
│ │ │ │ ├── zod/ # Validation schemas
│ │ │ │ ├── google/ # OAuth logic
│ │ │ │ └── subscription/ # Subscription logic
│ │ │ ├── types/ # TypeScript type definitions
│ │ │ └── middlewares/
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ └── web/ # Next.js frontend
│ ├── app/
│ │ ├── layout.tsx # Root layout (Geist font)
│ │ ├── page.tsx # Landing page
│ │ ├── [landing]/
│ │ │ └── [...]landingCards/
│ │ │ └── [...]landingPrice/
│ │ │ └── [...]landingSteps/
│ │ │ └── [...]landingSubscription/
│ │ ├── dashboard/ # User dashboard
│ │ ├── login/ # Login page
│ │ ├── signup/ # Signup page
│ │ ├── oauth2callback/ # OAuth callback
│ │ ├── api/ # API routes
│ │ │ ├── get/
│ │ │ ├── post/
│ │ │ ├── put/
│ │ │ └── delete/
│ │ └── globals.css # Global styles
│ ├── public/
│ ├── utils/
│ ├── package.json
│ ├── next.config.ts
│ ├── tsconfig.json
│ └── tailwind.config.ts
│
├── packages/ # Shared packages
│ ├── eslint-config/
│ │ ├── base.js # Base ESLint config
│ │ ├── next.js # Next.js ESLint rules
│ │ └── react-internal.js # React rules
│ ├── tailwind-config/
│ │ ├── shared-styles.css # Custom theme colors
│ │ └── postcss.config.js
│ ├── typescript-config/
│ │ ├── base.json # Base TypeScript config
│ │ ├── nextjs.json # Next.js config
│ │ └── react-library.json # React library config
│ └── ui/ # Shared React components
│ ├── src/
│ │ ├── card.tsx
│ │ ├── gradient.tsx
│ │ ├── styles.css
│ │ └── tailwindcss/
│
├── pnpm-workspace.yaml # PNPM workspace config
├── turbo.json # Turborepo config
├── package.json # Root package.json
└── README.md
✅ User Registration - Email-based signup with password hashing (Bcrypt)
✅ JWT Authentication - Secure token-based API access
✅ OTP Verification - Email OTP for account verification
✅ Google OAuth - Social login via Google
✅ Middleware Protection - All protected routes require valid JWT token
✅ Create Subscriptions - Track multiple subscriptions with detailed metadata
✅ Categories - Organize by Productivity, Education, Entertainment, Utility, Other
✅ Plan Types - Monthly, Yearly, Free, Trial options
✅ Auto-Renewal - Track auto-renewal status
✅ Payment Methods - Credit Card, Debit Card, PayPal, UPI support
✅ Billing Reminders - Automatic reminders 1, 3, 7, 14, or 30 days before renewal
✅ OTP Emails - Beautiful HTML formatted verification codes
✅ Reminder Emails - Customizable billing reminders
✅ Gmail SMTP - Secure email delivery via Gmail
✅ Google Gemini 2.5 Flash - AI-powered prompt responses
✅ Real-time Processing - Low-latency responses
✅ Full TypeScript - Every file is type-safe
✅ Zod Validation - Runtime schema validation
✅ Interface Definitions - Comprehensive TypeScript interfaces
{
_id: ObjectId;
email: string;
password: string (hashed with Bcrypt);
otp: string | null;
createdAt?: Date;
updatedAt?: Date;
}{
_id: ObjectId;
userId: ObjectId (ref: Users);
appName: string;
category: "Productivity" | "Education" | "Entertainment" | "Utility" | "Other";
planType: "Monthly" | "Yearly" | "Free" | "Trial";
amount: number;
currency: string (e.g., "USD", "INR");
paymentMethod: "Credit Card" | "Debit Card" | "PayPal" | "Upi" | "Other";
autoRenew: boolean;
startDate: Date;
nextBillingDate: Date;
reminderDaysBefore: 1 | 3 | 7 | 14 | 30;
createdAt?: Date;
updatedAt?: Date;
}POST /google - Google OAuth login
POST /otp-verification/:id - Verify OTP
POST /create-user - Create new user
GET /get-user/:userId - Get user info (Protected)
DELETE /delete-user/:userId - Delete user (Protected)
POST /login-user - User login with email/password
POST /create-subscription - Create new subscription (Protected)
GET /get-subscription/:id - Get subscription details (Protected)
PUT /update-subscription - Update subscription (Protected)
DELETE /delete-subscription - Delete subscription (Protected)
POST /one-day-reminder/:id - Send 1-day reminder (Protected)
POST /three-day-reminder/:id - Send 3-day reminder (Protected)
POST /seven-day-reminder/:id - Send 7-day reminder (Protected)
POST /fourteen-day-reminder/:id - Send 14-day reminder (Protected)
POST /thirty-day-reminder/:id - Send 30-day reminder (Protected)
POST /create-prompt-response - Get AI response via Gemini
# Database
MONGODB_URI=mongodb+srv://user:password@cluster.mongodb.net/database
# Authentication
JWT_SECRET=your-secret-key-here
# Email Service
USER_EMAIL=your-gmail@gmail.com
USER_PASS=your-app-specific-password
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# AI Integration
GEMINI_API_KEY=your-gemini-api-key
# Server Port
PORT=3000NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your-google-client-id- Node.js ≥18
- PNPM ≥10.19.0
- MongoDB Atlas account
- Gmail account (for email service)
- Google Cloud project (for OAuth & Gemini API)
-
Clone the repository
git clone https://github.com/yourusername/loopify.git cd loopify -
Install dependencies
pnpm install
-
Configure environment variables
# Copy .env template to .env cp apps/server/.env.example apps/server/.env cp apps/web/.env.example apps/web/.env.local # Fill in your credentials
-
Start development servers
# Run all apps in parallel pnpm dev # Server: http://localhost:3000 # Web: http://localhost:3001
# Build all packages and apps
pnpm build
# Start production server
pnpm startpnpm dev # Start all apps in development mode
pnpm build # Build all packages and apps
pnpm lint # Run ESLint across all packages
pnpm check-types # Type check all TypeScript files
pnpm badiya # Format code with Prettierpnpm --filter server dev # Start Express server in dev mode
pnpm --filter server build # Build TypeScript to dist/
pnpm --filter server start # Run production buildpnpm --filter web dev # Start Next.js dev server
pnpm --filter web build # Build Next.js for production
pnpm --filter web start # Start production server- Version: 4.1.5
- PostCSS: Enabled with autoprefixer
- Custom Colors:
--color-blue-1000: #2a8af6 (Primary) --color-purple-1000: #a853ba (Secondary) --color-red-1000: #e92a67 (Accent)
- Global styles in
apps/web/app/globals.css - Shared theme in
packages/tailwind-config/shared-styles.css - Component styles with Tailwind utilities
- Custom CSS prefixes for UI components (
ui-prefix)
- Primary Font: Geist (Google Font)
- Fallback: System fonts (Segoe UI, Tahoma, Geneva, Verdana)
- User signs up → password hashed with Bcrypt
- JWT token generated with user ID
- Token sent to client
- Client includes token in Authorization header:
Bearer {token} - Middleware verifies token on protected routes
- User registration → OTP generated (4 digits)
- Email sent via Nodemailer → Beautiful HTML template
- User submits OTP → Server validates
- Account activated → OTP cleared from DB
- User initiates Google login
- Redirect to Google authentication
- Google returns authorization code
- Server exchanges code for tokens
- User info retrieved and stored/updated
- Service: Gmail SMTP
- Authentication: App-specific password (recommended)
- Templates: HTML + Plain text fallback
- OTP Verification - Account setup
- Billing Reminders - 1, 3, 7, 14, 30 days before renewal
- Custom Emails - Extensible design
- Responsive HTML design
- Gradient headers
- Mobile-friendly
- Brand colors and styling
- Model: Gemini 2.5 Flash
- Purpose: Generate AI-powered responses to user prompts
- Endpoint:
POST /api/v1/response/create-prompt-response
POST http://localhost:3000/api/v1/response/create-prompt-response
Content-Type: application/json
{
"prompt": "How can I manage my subscriptions better?"
}{
"success": true,
"prompt": "How can I manage my subscriptions better?",
"aiResponse": "Here are some strategies..."
}- Zod Schemas - Runtime type checking
- Email Validation - Format verification
- Password Requirements - Strength checking
- OTP Expiration - Time-based validation
pnpm check-types # Full TypeScript compilation checkpnpm lint # ESLint with strict rules
pnpm badiya # Prettier auto-formatting✅ Shared Dependencies - Single node_modules installation
✅ Consistent TypeScript Configuration - Across all projects
✅ Unified Linting & Formatting - ESLint, Prettier configs
✅ Efficient Builds - Task orchestration & caching
✅ Workspace Dependencies - Easy local package imports
@repo/eslint-config- ESLint configurations@repo/tailwind-config- Tailwind CSS theme@repo/typescript-config- TypeScript configurations@repo/ui- Shared React components
- Try-catch blocks in all controllers
- Consistent error response format
- Detailed console logging
- HTTP status codes (400, 401, 404, 500)
{
"success": false,
"message": "Descriptive error message",
"error": "Optional error details"
}- CORS - Cross-origin requests enabled
- JSON Parser - Express built-in (5MB limit)
- Dotenv - Environment variable loading
- JWT Authentication - Token verification for protected routes
# Build TypeScript
pnpm --filter server build
# Output: apps/server/dist/
# Start: node ./dist/index.js# Build Next.js
pnpm --filter web build
# Output: apps/web/.next/
# Start: next start- TypeScript compilation ready
- Environment variables configurable
- Both apps containerizable
- Express: Lightweight API framework
- Mongoose: Minimal DB overhead
- Zod: ~15KB validation library
- Bcrypt: Native binding for fast hashing
- JWT: Stateless authentication
- ✅ Tree-shaking with native ES modules
- ✅ TypeScript strict mode for safety
- ✅ Minimal dependencies
- ✅ Tailwind CSS production optimization
-
Create Route
// routes/feature.routes.ts router.route('/endpoint').post(controller);
-
Create Controller
// controllers/feature.controller.ts export const controller = async (req: Request, res: Response) => { // Implementation };
-
Add Validation Schema
// schema/feature.schema.ts export const featureSchema = z.object({ field: z.string() });
-
Create Model (if needed)
// models/feature.model.ts const Feature = mongoose.model('Features', schema);
- Turborepo: https://turbo.build/
- Express: https://expressjs.com/
- Next.js: https://nextjs.org/
- Mongoose: https://mongoosejs.com/
- Tailwind CSS: https://tailwindcss.com/
- Zod: https://zod.dev/
- Google Gemini: https://ai.google.dev/
- Nodemailer: https://nodemailer.com/
ISC
- Create a feature branch
- Make your changes
- Test with
pnpm check-typesandpnpm lint - Submit a pull request
For support, email support@loopify.app or open an issue on GitHub.
Built with ❤️ using Turborepo, TypeScript, and modern web technologies