A modern backend API built with Bun, Elysia, and PostgreSQL following clean architecture principles.
- Runtime: Bun
- Framework: Elysia
- Language: TypeScript
- Database: PostgreSQL with Drizzle ORM
- Validation: TypeBox
- Error Handling: Result Pattern (neverthrow)
- Lint/Formatter: BiomeJS
- ✅ Health check endpoint with DB latency
- ✅ User CRUD with pagination and validation
- ✅ Product CRUD with image management and price updates
- ✅ Order management capturing product price snapshots
- ✅ Feature-based architecture
- ✅ Result pattern for error handling
- ✅ Type-safe database operations
- ✅ Input validation with TypeBox
- ✅ OpenAPI documentation at
/docs
src/
├── features/
│ ├── health/
│ ├── user/
│ ├── product/
│ └── order/
├── shared/
│ ├── config/
│ │ ├── database.ts # DB connection
│ │ └── schema.ts # Drizzle schema
│ └── errors/
│ └── domain-error.ts # Error classes
├── app.ts # App configuration
└── index.ts # Entry point
-
Install dependencies:
bun install
-
Start PostgreSQL with Docker:
docker-compose up -d
This will start a PostgreSQL 16 database on port 5432 with:
- Database:
mng_backend - User:
postgres - Password:
postgres
- Database:
-
Configure environment:
cp .env.example .env
The default
.env.exampleis configured for the Docker database:DATABASE_URL=postgres://postgres:postgres@localhost:5432/mng_backend PORT=3000 DB_SYNC=false -
Run migrations:
bun run db:push
-
Start development server:
bun run dev
- OpenAPI docs:
http://localhost:3000/docs - Root health:
GET /health
# Start database
docker-compose up -d
# Stop database
docker-compose down
# Stop and remove volumes (deletes all data)
docker-compose down -v
# View logs
docker-compose logs -f postgres
# Access PostgreSQL CLI
docker-compose exec postgres psql -U postgres -d mng_backendGET /health- Service and database status
GET /users- Paginated listGET /users/:id- Fetch onePOST /users- CreatePATCH /users/:id- UpdateDELETE /users/:id- Delete
GET /products- Paginated listGET /products/:id- Fetch onePOST /products- CreatePATCH /products/:id- UpdateDELETE /products/:id- DeletePOST /products/:id/images- Add imagesDELETE /products/:id/images- Remove a resolutionPATCH /products/:id/price- Update price
GET /orders- Paginated listGET /orders/:id- Fetch oneGET /orders/user/:userId- User orders with paginationPOST /orders- Create (captures product prices at purchase time)PATCH /orders/:id- Update statusDELETE /orders/:id- Delete (pending/cancelled only)
Create user:
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"cellphone": "+5511999999999"
}'Create order:
curl -X POST http://localhost:3000/orders \
-H "Content-Type: application/json" \
-d '{
"userId": "<user-uuid>",
"items": [
{ "productId": "<product-uuid>", "quantity": 2 }
]
}'bun run dev- Start development server with hot reloadbun run dev:sync- Start dev server and sync schemabun run start- Start production serverbun run start:sync- Start production server and sync schemabun run test- Run tests oncebun run test:watch- Run tests in watch modebun run db:generate- Generate migration filesbun run db:migrate- Apply migrationsbun run db:push- Push schema to databasebun run db:studio- Open Drizzle Studiobun run lint- Lint codebun run format- Format codebun run check- Lint and format code
This project follows the rules defined in AGENTS.md:
- Feature-Based Structure: Code organized by domain, not technical layers
- Result Pattern: No throwing errors in business logic
- Separation of Concerns: Clear boundaries between layers
- Type Safety: Full TypeScript with strict mode
- Validation: All inputs validated with TypeBox schemas
- Clean Code: BiomeJS enforced code standards
Drizzle migrations define tables for users, products, orders, and order items. Use bun run db:studio for an interactive view or inspect drizzle/ for the current snapshots.
The project uses:
- Path aliases:
@/maps tosrc/ - Strict TypeScript configuration
- BiomeJS for consistent code style
- Drizzle for type-safe database queries
MIT