A production-ready Express.js API with TypeScript, Drizzle ORM, and Better Auth for email/password authentication.
- ✅ Express.js with TypeScript
- ✅ Drizzle ORM with PostgreSQL
- ✅ Better Auth (email/password)
- ✅ Cookie-based authentication
- ✅ CORS enabled
- ✅ Modular architecture
- ✅ Input validation with Zod
- ✅ Error handling middleware
- ✅ Environment variable validation
src/
├── modules/
│ ├── auth/ # Authentication module
│ ├── user/ # User profile module
│ └── index.ts # Module routes aggregator
├── shared/
│ ├── config/ # App configuration
│ └── middleware/ # Shared middleware
├── db/
│ ├── schema.ts # Aggregate schemas
│ ├── migrations/ # Database migrations
│ └── seed.ts # Seed script
├── app.ts # Express app setup
└── server.ts # Entry point
npm installcp .env.example .envEdit .env and fill in your values:
PORT=3000
NODE_ENV=development
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
BETTER_AUTH_SECRET=your-super-secret-key-min-32-chars-long
BETTER_AUTH_URL=http://localhost:3000
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000Generate a secure secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Make sure PostgreSQL is running and create a database:
createdb mydbOr using psql:
CREATE DATABASE mydb;# Generate migration files from schema
npm run db:generate
# Run migrations
npm run db:migrate
# Alternative: Push schema directly (dev only)
npm run db:pushnpm run devThe server will start at http://localhost:3000
| Command | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Build for production |
npm start |
Start production server |
npm run db:generate |
Generate migration files |
npm run db:migrate |
Run database migrations |
npm run db:push |
Push schema directly (dev only) |
npm run db:studio |
Open Drizzle Studio (database GUI) |
npm run db:seed |
Run seed script |
Better Auth automatically provides these endpoints:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/sign-up/email |
Register with email/password |
| POST | /api/auth/sign-in/email |
Login with email/password |
| POST | /api/auth/sign-out |
Logout |
| GET | /api/auth/session |
Get current session |
| GET | /api/auth/me |
Get current user (custom) |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/users/profile |
Get user profile | ✅ |
| POST | /api/users/profile |
Create profile | ✅ |
| PATCH | /api/users/profile |
Update profile | ✅ |
| DELETE | /api/users/profile |
Delete profile | ✅ |
curl -X POST http://localhost:3000/api/auth/sign-up/email \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecurePass123!",
"name": "John Doe"
}'curl -X POST http://localhost:3000/api/auth/sign-in/email \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{
"email": "user@example.com",
"password": "SecurePass123!"
}'curl -X GET http://localhost:3000/api/users/profile \
-b cookies.txtcurl -X POST http://localhost:3000/api/auth/sign-out \
-b cookies.txtnpm install better-auth @better-auth/react// lib/auth.ts
import { createAuthClient } from 'better-auth/react';
export const authClient = createAuthClient({
baseURL: 'http://localhost:3000',
});
export const { signIn, signUp, signOut, useSession } = authClient;// components/Login.tsx
import { signIn } from '@/lib/auth';
function Login() {
const handleLogin = async (e) => {
e.preventDefault();
await signIn.email({
email: 'user@example.com',
password: 'password123',
});
};
return <form onSubmit={handleLogin}>...</form>;
}- Create module folder:
src/modules/post/ - Add files:
post.schema.ts- Drizzle schemapost.repository.ts- Database queriespost.service.ts- Business logicpost.controller.ts- Request handlerspost.routes.ts- Route definitionspost.validation.ts- Zod schemas
- Export schema in
src/db/schema.ts - Register routes in
src/modules/index.ts
The project includes these tables:
user- Better Auth user tablesession- User sessionsaccount- OAuth/password accountsverification- Email verification tokensuser_profile- Extended user profiles
| Variable | Description | Required |
|---|---|---|
PORT |
Server port | No (default: 3000) |
NODE_ENV |
Environment | No (default: development) |
DATABASE_URL |
PostgreSQL connection string | Yes |
BETTER_AUTH_SECRET |
Auth secret (min 32 chars) | Yes |
BETTER_AUTH_URL |
Base URL of your app | Yes |
ALLOWED_ORIGINS |
CORS allowed origins (comma-separated) | No |
- Build the project:
npm run build-
Set environment variables in your hosting platform
-
Run migrations:
npm run db:migrate- Start the server:
npm startMIT