Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ Include any relevant background context.

Select all that apply:

* [ ] Bug fix (non-breaking change which fixes an issue)
* [ ] New feature (non-breaking change which adds functionality)
* [ ] Breaking change (fix or feature that could cause existing functionality to not work as expected)
* [ ] Refactor / Chore (code cleanup, dependencies, tooling, etc.)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that could cause existing functionality to not work as expected)
- [ ] Refactor / Chore (code cleanup, dependencies, tooling, etc.)

---

Expand All @@ -39,10 +39,10 @@ If none, write: `N/A`.

## Checklist

* [ ] My code follows the project’s code style.
* [ ] I have run all linter.
* [ ] I have checked that my changes do not introduce breaking changes.
* [ ] I have commented my code where needed, especially complex logic.
- [ ] My code follows the project’s code style.
- [ ] I have run all linter.
- [ ] I have checked that my changes do not introduce breaking changes.
- [ ] I have commented my code where needed, especially complex logic.

---

Expand All @@ -57,8 +57,8 @@ If none, write: `N/A`.

Add any extra details for reviewers:

* Deployment notes
* New dependencies or migrations
* Performance considerations
* Known limitations or follow-ups
If none, write: `N/A`.
- Deployment notes
- New dependencies or migrations
- Performance considerations
- Known limitations or follow-ups
If none, write: `N/A`.
2 changes: 0 additions & 2 deletions .github/scripts/workflow-lint-pr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@ cd packages/ui
npx eslint src/
```



**Note:** The workflow dynamically discovers all workspaces from `pnpm-workspace.yaml`, so you don't need to manually maintain a list. Any new workspace you add will automatically be included in linting.

## Recommended Testing Flow
Expand Down
61 changes: 32 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

A **modern full-stack TypeScript monorepo** using:

* **Next.js** (Web)
* **NestJS** (API)
* **Expo** (Mobile)
* **tRPC** (End-to-end type-safe API calls)
* **PostgreSQL** or **MongoDB** (Database - your choice!)
* **Prisma** or **Mongoose** (ORM/ODM - based on your DB choice)
* **Better Auth** (Authentication with OAuth, Email OTP)
* **SonarQube** (Code quality)
* **Rollbar** (Error tracking)
* **Turborepo** (Build orchestration)
- **Next.js** (Web)
- **NestJS** (API)
- **Expo** (Mobile)
- **tRPC** (End-to-end type-safe API calls)
- **PostgreSQL** or **MongoDB** (Database - your choice!)
- **Prisma** or **Mongoose** (ORM/ODM - based on your DB choice)
- **Better Auth** (Authentication with OAuth, Email OTP)
- **SonarQube** (Code quality)
- **Rollbar** (Error tracking)
- **Turborepo** (Build orchestration)

This repository is structured for scalability, developer experience, and seamless cross-platform sharing of logic and types.

Expand All @@ -34,7 +34,8 @@ Make sure the following are installed **before** setup:
## 🏗️ Setup Guide

### 0. Prerequisites
Before proceeding with the setup, ensure you have all the required prerequisites installed.

Before proceeding with the setup, ensure you have all the required prerequisites installed.
Missing any of these may cause the setup to fail or not work as expected.

### 1. Clone the Repository
Expand All @@ -49,7 +50,9 @@ Once the repository is cloned, you can run an **automated setup script** from th
```bash
npm run setup
```

or

```bash
node setup.js
```
Expand Down Expand Up @@ -183,28 +186,28 @@ This command runs all apps (API, Web, and Mobile) concurrently using Turborepo.

## 🧩 Tech Stack Highlights

* ⚡ **Turborepo** – Monorepo management
* 💬 **tRPC** – End-to-end type-safe API communication
* 🧠 **Zod** – Runtime validation & schema definition
* 🗄️ **Database Options** – Choose between:
* **PostgreSQL + Prisma** – Relational database with type-safe ORM
* **MongoDB + Mongoose** – NoSQL database with ODM
* 🔐 **Better Auth** – Modern authentication framework with:
* Email OTP authentication
* Google OAuth
* Expo mobile support
* Email verification
* 💻 **Next.js** – Web frontend
* 📱 **Expo (React Native)** – Mobile app
* 🧱 **NestJS** – Backend API
* 🧩 **Shared Packages** – Centralized types & logic
- ⚡ **Turborepo** – Monorepo management
- 💬 **tRPC** – End-to-end type-safe API communication
- 🧠 **Zod** – Runtime validation & schema definition
- 🗄️ **Database Options** – Choose between:
- **PostgreSQL + Prisma** – Relational database with type-safe ORM
- **MongoDB + Mongoose** – NoSQL database with ODM
- 🔐 **Better Auth** – Modern authentication framework with:
- Email OTP authentication
- Google OAuth
- Expo mobile support
- Email verification
- 💻 **Next.js** – Web frontend
- 📱 **Expo (React Native)** – Mobile app
- 🧱 **NestJS** – Backend API
- 🧩 **Shared Packages** – Centralized types & logic

---

## 🧑‍💻 Development Notes

* Keep Docker running while developing backend/API.
* Use `pnpm` consistently — **do not use npm or yarn**.
* Better Auth requires proper environment variables for OAuth providers.
- Keep Docker running while developing backend/API.
- Use `pnpm` consistently — **do not use npm or yarn**.
- Better Auth requires proper environment variables for OAuth providers.

---
3 changes: 2 additions & 1 deletion apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ MONGODB_CONTAINER_NAME=app-mongodb
MONGODB_PORT=27017
MONGODB_USER=admin
MONGODB_PASSWORD=admin123
DATABASE_URL_MONGODB=mongodb://admin:admin123@localhost:27017/appdb?authSource=admin
MONGODB_REPLICA_SET_KEY=your_key_here
DATABASE_URL_MONGODB=mongodb://admin:admin123@localhost:27017/appdb?authSource=admin&replicaSet=rs0&directConnection=true

# Mongo Express
MONGO_EXPRESS_CONTAINER_NAME=app-mongo-express
Expand Down
23 changes: 13 additions & 10 deletions apps/api/docker-compose.mongo.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
name: app-mongo-db
services:
mongodb:
image: mongodb/mongodb-community-server:latest
image: bitnami/mongodb:latest
container_name: ${MONGODB_CONTAINER_NAME}
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USER}
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
MONGODB_REPLICA_SET_MODE: primary
MONGODB_REPLICA_SET_NAME: rs0
MONGODB_REPLICA_SET_KEY: ${MONGODB_REPLICA_SET_KEY}
MONGODB_ROOT_USER: ${MONGODB_USER}
MONGODB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
MONGODB_ADVERTISED_HOSTNAME: mongodb
ports:
- "${MONGODB_PORT}:27017"
volumes:
- mongodb_data:/data/db
- mongodb_data:/bitnami/mongodb
healthcheck:
test: mongosh -u ${MONGODB_USER} -p ${MONGODB_PASSWORD} --authenticationDatabase admin --quiet --eval "db.runCommand('ping').ok"
test: >
bash -c "mongosh -u ${MONGODB_USER} -p ${MONGODB_PASSWORD} --authenticationDatabase admin --quiet --eval 'rs.status().ok' | grep 1"
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
retries: 30
start_period: 10s

mongo-express:
image: mongo-express:latest
container_name: ${MONGO_EXPRESS_CONTAINER_NAME}
restart: unless-stopped
environment:
ME_CONFIG_MONGODB_SERVER: mongodb
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGODB_USER}
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGODB_PASSWORD}
ME_CONFIG_MONGODB_URL: "mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@mongodb:27017/?authSource=admin&replicaSet=rs0"
ME_CONFIG_BASICAUTH_USERNAME: ${MONGO_EXPRESS_USER}
ME_CONFIG_BASICAUTH_PASSWORD: ${MONGO_EXPRESS_PASSWORD}
ports:
Expand Down
7 changes: 7 additions & 0 deletions apps/api/eslint-rules/plugin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { requireTransaction } from './require-transaction.mjs';

export const plugin = {
rules: {
'require-transactional': requireTransaction,
},
};
38 changes: 38 additions & 0 deletions apps/api/eslint-rules/require-transaction.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const requireTransaction = {
meta: {
type: 'problem',
docs: {
description:
'Warn if a service class is missing @AutoTransaction decorator',
recommended: 'warn',
},
messages: {
missingClassAutoTransaction:
"Service class '{{name}}' is missing @AutoTransaction decorator.",
},
schema: [],
},
create(context) {
return {
ClassDeclaration(node) {
const className = node.id?.name || '';
if (!className.endsWith('Service')) return;

const decorators = node.decorators || [];
const hasTransactional = decorators.some(
(d) =>
d.expression?.callee?.name === 'AutoTransaction' ||
d.expression?.name === 'AutoTransaction',
);

if (!hasTransactional) {
context.report({
node,
messageId: 'missingClassAutoTransaction',
data: { name: className },
});
}
},
};
},
};
17 changes: 12 additions & 5 deletions apps/api/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @ts-check
import { config as baseConfig } from "@repo/eslint-config/base";
import tseslint from "typescript-eslint";
import { config as baseConfig } from '@repo/eslint-config/base';
import tseslint from 'typescript-eslint';
import { plugin as customPlugin } from './eslint-rules/plugin.mjs';

export default tseslint.config(
...baseConfig,
{
ignores: ["eslint.config.mjs", "src/generated/**"],
ignores: ['eslint.config.mjs', 'src/generated/**', 'eslint-rules/**'],
},
{
languageOptions: {
Expand All @@ -14,5 +15,11 @@ export default tseslint.config(
tsconfigRootDir: import.meta.dirname,
},
},
}
);
plugins: {
custom: customPlugin,
},
rules: {
'custom/require-transactional': 'warn',
},
},
);
10 changes: 8 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@
"test:e2e": "jest --config ./test/jest-e2e.json",
"db:seed:mongo": "dotenvx run -- ts-node -r tsconfig-paths/register scripts/seed/seed-mongoose.ts",
"db:seed:prisma": "cd ../../packages/prisma-db && pnpm run db:seed",
"db:seed:all": "pnpm run db:seed:prisma && pnpm run db:seed:mongo"
"db:seed:all": "pnpm run db:seed:prisma && pnpm run db:seed:mongo",
"generate:repo:prisma": "ts-node -r tsconfig-paths/register scripts/code-generation/prisma/repositories/generate-repository.ts",
"generate:repo:mongo": "ts-node -r tsconfig-paths/register scripts/code-generation/mongoose/repositories/generate-repository.ts"
},
"dependencies": {
"@andeanwide/nestjs-rollbar": "^1.0.0",
"@better-auth/expo": "1.3.34",
"@nestjs-cls/transactional": "^3.1.1",
"@nestjs-cls/transactional-adapter-mongoose": "^1.1.25",
"@nestjs-cls/transactional-adapter-prisma": "^1.3.1",
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1",
Expand All @@ -35,20 +40,21 @@
"@thallesp/nestjs-better-auth": "^2.1.0",
"better-auth": "1.3.34",
"mongoose": "^9.0.0",
"nestjs-cls": "^6.1.0",
"nestjs-trpc": "^1.6.1",
"reflect-metadata": "^0.2.2",
"resend": "^6.4.2",
"rxjs": "^7.8.1",
"zod": "3.25.5"
},
"devDependencies": {
"@repo/db-seeder": "workspace:*",
"@better-auth/cli": "1.3.34",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@repo/db-seeder": "workspace:*",
"@repo/eslint-config": "workspace:*",
"@types/express": "^5.0.0",
"@types/jest": "^30.0.0",
Expand Down
Loading