{"id":50695785,"url":"https://github.com/rijalghodi/express-drizzle-pg","last_synced_at":"2026-06-09T06:08:49.256Z","repository":{"id":330698222,"uuid":"1123611192","full_name":"rijalghodi/express-drizzle-pg","owner":"rijalghodi","description":"Express + Drizzle ORM + Postgres Boilerplate","archived":false,"fork":false,"pushed_at":"2026-04-01T06:48:21.000Z","size":262,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-01T09:24:36.582Z","etag":null,"topics":["backend","drizzle","express","pg"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rijalghodi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-27T08:34:40.000Z","updated_at":"2026-04-01T06:48:25.000Z","dependencies_parsed_at":"2025-12-29T02:03:15.069Z","dependency_job_id":null,"html_url":"https://github.com/rijalghodi/express-drizzle-pg","commit_stats":null,"previous_names":["rijalghodi/express-drizzle-pg"],"tags_count":0,"template":true,"template_full_name":null,"purl":"pkg:github/rijalghodi/express-drizzle-pg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rijalghodi%2Fexpress-drizzle-pg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rijalghodi%2Fexpress-drizzle-pg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rijalghodi%2Fexpress-drizzle-pg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rijalghodi%2Fexpress-drizzle-pg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rijalghodi","download_url":"https://codeload.github.com/rijalghodi/express-drizzle-pg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rijalghodi%2Fexpress-drizzle-pg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34093842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","drizzle","express","pg"],"created_at":"2026-06-09T06:08:44.490Z","updated_at":"2026-06-09T06:08:49.248Z","avatar_url":"https://github.com/rijalghodi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Express TypeScript PostgreSQL API\n\nA robust and production-ready RESTful API built with TypeScript, Express, PostgreSQL, and Drizzle ORM. Features include JWT authentication, Google OAuth, rate limiting, email verification, password reset, and a complete todo management system.\n\n## Features\n\n### Core Stack\n\n- **TypeScript**: Strongly-typed development for enhanced code quality and maintainability\n- **Express**: Fast, minimalist web framework for Node.js\n- **PostgreSQL**: Powerful, open-source relational database\n- **Drizzle ORM**: Type-safe, lightweight ORM for PostgreSQL\n- **Zod**: TypeScript-first schema validation for request validation\n\n### Authentication \u0026 Security\n\n- **JWT Authentication**: Secure token-based authentication\n- **Google OAuth 2.0**: Social authentication with Google\n- **Email Verification**: User email verification system with tokens\n- **Password Reset**: Secure forgot password and reset password flow\n- **Rate Limiting**: Global and endpoint-specific rate limiting\n  - Global: 100 requests per 10 minutes\n  - Auth endpoints: 1 request per 30 seconds (forgot password, request verification)\n- **bcrypt**: Password hashing for secure credential storage\n\n### Email System\n\n- **Nodemailer**: Email service for verification and password reset\n- **Email Templates**: Professional email templates for user communications\n\n### Architecture\n\n- **Modular Design**: Organized by Controllers, Services, Routes, Middlewares, and Validators\n- **Custom Response Handlers**: Standardized API response format\n- **Environment Configuration**: Centralized environment variable management\n- **Error Handling**: Comprehensive error handling with detailed messages\n\n## Tech Stack\n\n| Category       | Technology                    |\n| -------------- | ----------------------------- |\n| Runtime        | Node.js                       |\n| Language       | TypeScript                    |\n| Framework      | Express.js                    |\n| Database       | PostgreSQL                    |\n| ORM            | Drizzle ORM                   |\n| Validation     | Zod                           |\n| Authentication | JWT + Passport (Google OAuth) |\n| Security       | bcrypt, express-rate-limit    |\n| Email          | Nodemailer                    |\n| Dev Tools      | nodemon, tsx                  |\n\n## API Endpoints\n\n### Authentication Routes (`/auth`)\n\n| Method | Endpoint                     | Description                | Rate Limited | Auth Required |\n| ------ | ---------------------------- | -------------------------- | ------------ | ------------- |\n| POST   | `/auth/register`             | Register new user          | ✓ Global     | ✗             |\n| POST   | `/auth/login`                | Login with email/password  | ✓ Global     | ✗             |\n| GET    | `/auth/me`                   | Get current user profile   | ✓ Global     | ✓             |\n| POST   | `/auth/forgot-password`      | Request password reset     | ✓ 30s        | ✗             |\n| POST   | `/auth/reset-password`       | Reset password with token  | ✓ Global     | ✗             |\n| POST   | `/auth/request-verification` | Request email verification | ✓ 30s        | ✗             |\n| GET    | `/auth/verify-email/:token`  | Verify email with token    | ✓ Global     | ✗             |\n| GET    | `/auth/google`               | Initiate Google OAuth      | ✓ Global     | ✗             |\n| GET    | `/auth/google/callback`      | Google OAuth callback      | ✓ Global     | ✗             |\n\n### Todo Routes (`/todos`)\n\n| Method | Endpoint     | Description        | Auth Required |\n| ------ | ------------ | ------------------ | ------------- |\n| POST   | `/todos`     | Create new todo    | ✓             |\n| GET    | `/todos`     | Get all user todos | ✓             |\n| GET    | `/todos/:id` | Get todo by ID     | ✓             |\n| PUT    | `/todos/:id` | Update todo        | ✓             |\n| DELETE | `/todos/:id` | Delete todo        | ✓             |\n\n## Database Schema\n\n### Users Table\n\n```typescript\n{\n  id: uuid (primary key)\n  name: string | null\n  email: string (unique)\n  password: string | null (nullable for OAuth users)\n  googleId: string | null\n  image: string | null\n  isVerified: boolean (default: false)\n  createdAt: timestamp\n  updatedAt: timestamp\n}\n```\n\n### Todos Table\n\n```typescript\n{\n  id: uuid (primary key)\n  userId: uuid (foreign key → users.id)\n  title: string\n  description: string | null\n  status: boolean (default: false)\n  createdAt: timestamp\n  updatedAt: timestamp\n}\n```\n\n### Verification Tokens Table\n\n```typescript\n{\n  id: uuid (primary key)\n  userId: uuid (foreign key → users.id)\n  token: string (hashed)\n  type: 'email_verification' | 'password_reset'\n  expiresAt: timestamp\n  createdAt: timestamp\n}\n```\n\n## Prerequisites\n\nEnsure you have the following installed:\n\n- **Node.js** v18.x or higher\n- **pnpm** (or npm/yarn)\n- **PostgreSQL** v14.x or higher\n\n## Getting Started\n\n### 1. Clone the Repository\n\n```bash\ngit clone https://github.com/yourusername/express-drizzle-pg.git\ncd express-drizzle-pg\n```\n\n### 2. Install Dependencies\n\n```bash\npnpm install\n```\n\n### 3. Environment Configuration\n\nCreate a `.env` file in the root directory:\n\n```env\n# Server\nEXPRESS_PORT=8000\nNODE_ENV=development\n\n# Database\nDATABASE_URL=postgresql://username:password@localhost:5432/dbname\n\n# JWT\nJWT_SECRET=your-super-secret-jwt-key\nJWT_EXPIRATION=7d\n\n# Google OAuth\nGOOGLE_OAUTH_CLIENT_ID=your-google-client-id\nGOOGLE_OAUTH_CLIENT_SECRET=your-google-client-secret\nGOOGLE_OAUTH_CALLBACK_URL=http://localhost:8000/auth/google/callback\n\n# Email (Nodemailer)\nEMAIL_HOST=smtp.gmail.com\nEMAIL_PORT=587\nEMAIL_USER=your-email@gmail.com\nEMAIL_PASS=your-app-password\nEMAIL_FROM=noreply@yourapp.com\n```\n\n### 4. Database Setup\n\nGenerate migration files:\n\n```bash\npnpm db:generate\n```\n\nRun migrations:\n\n```bash\npnpm db:migrate\n```\n\n### 5. Start Development Server\n\n```bash\npnpm dev\n```\n\nThe server will start at `http://localhost:8000`\n\n## Available Scripts\n\n| Script             | Description                                |\n| ------------------ | ------------------------------------------ |\n| `pnpm dev`         | Start development server with hot reload   |\n| `pnpm build`       | Compile TypeScript to JavaScript           |\n| `pnpm start`       | Run compiled production build              |\n| `pnpm typecheck`   | Run TypeScript type checking               |\n| `pnpm lint`        | Run ESLint                                 |\n| `pnpm lint:fix`    | Fix ESLint errors and format with Prettier |\n| `pnpm db:generate` | Generate Drizzle migration files           |\n| `pnpm db:migrate`  | Run database migrations                    |\n\n## Project Structure\n\n```\nexpress-drizzle-pg/\n├── src/\n│   ├── config/          # Configuration files (env, database, OAuth)\n│   ├── controllers/     # Request handlers\n│   ├── db/             # Database schema and client\n│   ├── middlewares/    # Custom middleware (auth, rate limiting)\n│   ├── routes/         # API route definitions\n│   ├── services/       # Business logic layer\n│   ├── types/          # TypeScript type definitions\n│   ├── validators/     # Zod validation schemas\n│   └── index.ts        # Application entry point\n├── drizzle/            # Database migrations\n├── .env                # Environment variables\n├── package.json\n└── tsconfig.json\n```\n\n## Development Flow\n\nFollow these steps to add a new feature or entity to the project:\n\n### 1. Create Schema\n\nDefine your database tables in `src/db/schema.ts` using Drizzle ORM's `pgTable`.\n\n```typescript\nexport const newTable = pgTable(\"new_table_name\", {\n  id: uuid(\"id\")\n    .primaryKey()\n    .$defaultFn(() =\u003e randomUUID()),\n  name: varchar(\"name\", { length: 255 }).notNull(),\n  createdAt: timestamp(\"created_at\").notNull().defaultNow(),\n  // ... other columns\n});\n```\n\n### 2. Create Migration\n\nGenerate and apply migrations to keep your database in sync with your schema.\n\n```bash\n# Generate SQL migration files based on schema changes\npnpm db:generate\n\n# Apply the generated migrations to your PostgreSQL database\npnpm db:migrate\n```\n\n### 3. Create Service\n\nImplement business logic and database interactions in `src/services/`. Services should use the `db` instance from `src/config/drizzle.client.ts`.\n\n```typescript\nimport { db } from \"@/config/drizzle.client\";\nimport { newTable } from \"@/db/schema\";\n\nconst NewService = {\n  createItem: async (data: any) =\u003e {\n    const [item] = await db.insert(newTable).values(data).returning();\n    return item;\n  },\n  // ... other data access methods\n};\n\nexport default NewService;\n```\n\n### 4. Create Controller\n\nHandle incoming HTTP requests in `src/controllers/`. Controllers should validate input (using Zod) and call the appropriate service methods. Use the standardized `res.success()` and `res.error()` helpers.\n\n```typescript\nconst NewController = {\n  createItem: async (req: Request, res: Response) =\u003e {\n    try {\n      const item = await NewService.createItem(req.body);\n      res.success(item, 201);\n    } catch (error) {\n      res.error(\"Failed to create item\", 500);\n    }\n  },\n};\n\nexport default NewController;\n```\n\n### 5. Register Routes\n\nDefine the API endpoints in `src/routes/` and link them to your controller methods.\n\n```typescript\nimport { Router } from \"express\";\nimport NewController from \"@/controllers/new.controller\";\n\nconst router = Router();\nrouter.post(\"/\", NewController.createItem);\n\nexport default router;\n```\n\nDon't forget to mount your new router in `src/index.ts`.\n\n## Example Usage\n\n### Register a New User\n\n```bash\ncurl -X POST http://localhost:8000/auth/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\",\n    \"password\": \"SecurePass123!\"\n  }'\n```\n\n### Login\n\n```bash\ncurl -X POST http://localhost:8000/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"email\": \"john@example.com\",\n    \"password\": \"SecurePass123!\"\n  }'\n```\n\n### Get Current User\n\n```bash\ncurl -X GET http://localhost:8000/auth/me \\\n  -H \"Authorization: Bearer YOUR_JWT_TOKEN\"\n```\n\n### Create a Todo\n\n```bash\ncurl -X POST http://localhost:8000/todos \\\n  -H \"Authorization: Bearer YOUR_JWT_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Complete project\",\n    \"description\": \"Finish the API documentation\"\n  }'\n```\n\n## Rate Limiting\n\nThis API implements rate limiting to prevent abuse:\n\n- **Global Rate Limit**: 100 requests per 10 minutes per IP address\n- **Auth Endpoints** (`/forgot-password`, `/request-verification`): 1 request per 30 seconds per IP\n\nWhen rate limit is exceeded, you'll receive a `429 Too Many Requests` response.\n\n## Security Features\n\n✅ Password hashing with bcrypt  \n✅ JWT token-based authentication  \n✅ Rate limiting on sensitive endpoints  \n✅ Email verification for new accounts  \n✅ Secure password reset flow with expiring tokens  \n✅ Input validation with Zod  \n✅ SQL injection prevention via Drizzle ORM  \n✅ Environment variable protection\n\n## Contributing\n\nContributions are welcome! Please follow these steps:\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the ISC License.\n\n## Acknowledgements\n\nSpecial thanks to the creators and maintainers of:\n\n- [TypeScript](https://www.typescriptlang.org/) - Typed JavaScript\n- [Express](https://expressjs.com/) - Web framework\n- [PostgreSQL](https://www.postgresql.org/) - Database\n- [Drizzle ORM](https://orm.drizzle.team/) - Type-safe ORM\n- [Zod](https://zod.dev/) - Schema validation\n- [Passport](http://www.passportjs.org/) - Authentication middleware\n- [Nodemailer](https://nodemailer.com/) - Email service\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frijalghodi%2Fexpress-drizzle-pg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frijalghodi%2Fexpress-drizzle-pg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frijalghodi%2Fexpress-drizzle-pg/lists"}