Skip to content

A simple, production-ready Elysia.js starter πŸš€ Ship faster with optional Better Auth, Scalar Docs, and Docker support.

License

Notifications You must be signed in to change notification settings

techfusionid/elysia-production-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

46 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Elysia Production Boilerplate

A production-ready ElysiaJS starter for your projects, featuring
Clean Architecture, Better Auth, Drizzle ORM, and OpenAPI documentation.

Elysia Production Template Thumbnail

Elysia Bun TypeScript Better Auth Drizzle ORM PostgreSQL Docker

CI OpenAPI

Features

What you get:

  • ⚑ High-performance, fully async APIs
  • πŸ” Built-in Auth & API Endpoint Protection (Better Auth)
  • βœ… Type-safe validation (TypeBox)
  • πŸ—„οΈ Database & Migrations (PostgreSQL + Drizzle ORM)
  • πŸ“– API Documentation via OpenAPI with Scalar UI
  • 🚦 Rate limiter for global & auth
  • 🧾 Structured logging using Pino
  • 🌐 Configurable CORS middleware for frontend integration
  • πŸ“§ Email Infrastructure via Resend + React Email (optional)
  • 🐳 One-command Docker Compose

Why use this starter?

Elysia.js is fast. It's currently one of the fastest frameworks in the Bun ecosystem, with benchmarks showing performance that can match Golang and Rust frameworks (based on TechEmpower Benchmarks).

The problem? Setting up Authentication, ORM, Docker, and logging from scratch for a production-ready app takes hours.

This boilerplate provides a simple, ready-to-use, production-grade foundation so you can focus on building features immediately with Elysia, without redoing repetitive configuration and setup.

Perfect if you want to:

  • Build a production-ready API on Elysia.js without reinventing the same setup
  • Start developing immediately with sensible defaults and clear structure
  • Keep full control over configuration while avoiding boilerplate fatigue
  • Use modern, type-safe tooling without framework lock-in

Quick Start

1. Use This Template (Recommended)

Click the green "Use this template" button at the top of this repo β€” or use the direct link:

πŸ‘‰ Create from template

Creates a clean repo without git history.

2. Clone the repository (Alternative)

If you prefer cloning manually:

git clone https://github.com/techfusionid/elysia-production-template.git
cd elysia-production-template

Setup environment:

cp .env.example .env

Important

Open .env and update BETTER_AUTH_SECRET and DATABASE_URL before running.

Install & run:

bun install
bun run dev

Or with Docker:

docker compose up

Your app is now running:

Project Structure

src/
β”œβ”€β”€ common/
β”‚   β”œβ”€β”€ config/       # Environment, auth, email settings
β”‚   β”œβ”€β”€ db/           # Database connection & schema
β”‚   β”œβ”€β”€ logger/       # Pino logger
β”‚   └── middleware/   # Auth guard, rate limiter, request logger
β”œβ”€β”€ modules/
β”‚   β”œβ”€β”€ auth/         # Better Auth integration
β”‚   β”œβ”€β”€ health/       # Health check endpoint
β”‚   └── posts/        # Example CRUD module
β”œβ”€β”€ app.ts            # App composition & middleware
└── index.ts          # Entry point

tests/                # Integration tests

πŸ“Œ Note The posts module and its API endpoints are provided as example CRUD implementations. You can safely modify or remove them if not needed.

Configuration

Key environment variables (see .env.example for full list):

Variable Description Required
NODE_ENV Runtime environment (development, production, test) No (default: development)
HOST Server bind address No (default: 0.0.0.0)
PORT Server port No (default: 3000)
DATABASE_URL PostgreSQL connection string Yes
BETTER_AUTH_SECRET Auth secret key (generate: openssl rand -base64 32) Yes
BETTER_AUTH_URL Base URL for auth callbacks Yes
ENABLE_AUTH Enable/disable auth module No (default: true)
REQUIRE_EMAIL_VERIFICATION Require email verification before login No (default: false)
ENABLE_RATE_LIMITER Enable/disable rate limiting No (default: true)
LOG_LEVEL Log level: fatal, error, warn, info, debug, trace No (default: info)
CORS_ORIGIN Allowed origins (comma-separated) No (default: http://localhost:3000,http://localhost:5173)

NODE_ENV is used to adjust logging visual, testing, and runtime behavior.

Logging

Logging behavior is automatically adjusted based on NODE_ENV:

  • NODE_ENV=development: human-readable logs for easier debugging
  • NODE_ENV=production: structured JSON logs, optimized for log aggregation and monitoring

Log verbosity can be controlled using the LOG_LEVEL environment variable.

Commands

Development:

bun run dev          # Start dev server with hot reload

🐳 Local Development with Docker PostgreSQL

For local development, it's recommended to run PostgreSQL via Docker while keeping the API running locally with Bun.

docker compose up -d postgres

Production:

bun run build        # Build for production
bun run start        # Start production server

Docker Compose

Run API + PostgreSQL fully inside Docker:

docker compose up
docker compose up --build
docker compose down

View compose logs:

docker compose logs -f
docker compose logs -f api
docker compose logs -f postgres

Database & Migration (Drizzle):

bun run db:generate  # Generate Drizzle migrations
bun run db:migrate   # Run migrations
bun run db:studio    # Open Drizzle Studio (visual database browser)

Testing:

bun run test         # Run integration tests

Note

Tests run against your local database. Make sure PostgreSQL is running before testing.

Linting:

bun run lint         # Run Biome linter
bun run format       # Format code

API Endpoints

Below are the main routes exposed by the template. See /docs for full request/response schemas.

Auth routes (via Better Auth):

Method Endpoint Description
POST /api/auth/sign-up/email Register
POST /api/auth/sign-in/email Login
POST /api/auth/sign-out Logout
GET /api/auth/get-session Get current session
POST /api/auth/request-password-reset Request password reset
POST /api/auth/reset-password Reset password

Posts routes (example CRUD – safe to remove):

Method Endpoint Description Auth
GET /api/posts List all posts No
GET /api/posts/:id Get single post No
POST /api/posts Create post Yes
PUT /api/posts/:id Update post Owner
DELETE /api/posts/:id Delete post Owner

Health:

Method Endpoint Description
GET /health Health check with DB status

Full API documentation available at /docs when running.


Adding New Modules

The posts module is included as a reference CRUD implementation. Feel free to remove or replace it with your own modules.

To add a new module:

  1. Create folder: src/modules/your-module/
  2. Add index.ts (routes), service.ts (business logic), and optionally schema.ts
  3. Register in src/app.ts:
import { yourModule } from "@modules/your-module";
app.use(yourModule);

Customization

This template is designed to be configurable without hiding behavior. Below are common customization points.

Disabling Authentication

To disable authentication, set ENABLE_AUTH=false in .env. All auth routes and middleware will be automatically excluded.

If you want to completely remove built-in auth from your codebase:

  1. Delete the src/modules/auth/ folder
  2. Remove the src/common/config/auth.ts file
  3. (Optional) Clean up any route guards that use auth: true (e.g., in posts module)

πŸ’‘ For most use cases, just setting ENABLE_AUTH=false is sufficient and safe.

Email Verification

By default, email verification is disabled. To enable, set in .env:

REQUIRE_EMAIL_VERIFICATION=true

When enabled, users must verify their email before they can log in.

Tip

In development, emails are logged to console (no provider needed). To test with real emails, configure Resend (see below).

Email Setup (Resend)

To send real emails for verification and password reset:

  1. Create an account at resend.com
  2. Get your API key from the dashboard
  3. Add to .env:
    RESEND_API_KEY=re_xxxxxxxxxxxxx
    EMAIL_FROM=onboarding@resend.dev

Note

Sandbox mode: onboarding@resend.dev can only send to your Resend account email. For production, verify your domain. See Resend documentation for details.

Password Reset

Password reset works out of the box. Reset links are logged to console in development, or sent via email when Resend is configured.

Rate Limiting

Rate limiting is enabled by default. To disable (e.g., for testing):

ENABLE_RATE_LIMITER=false

Adjust rate limits via environment variables:

# Global rate limit
RATE_LIMIT_WINDOW_MS=60000    # 60 seconds
RATE_LIMIT_MAX=100            # 100 requests per window

# Auth-specific rate limit (stricter)
AUTH_RATE_LIMIT_WINDOW_MS=60000
AUTH_RATE_LIMIT_MAX=10

Protected vs Public Routes

Use the auth route option to mark endpoints as protected:

// Public route - anyone can access
.get("/", async () => { ... })

// Protected route - requires login
.post("/", async ({ user }) => { ... }, { auth: true })

Validation & Schemas

Uses drizzle-typebox to generate validation schemas from Drizzle ORM:

// Auto-generated from Drizzle + override validation
export const createPostSchema = createInsertSchema(posts, {
  title: t.String({ minLength: 5, maxLength: 50 }),
  content: t.String({ minLength: 10 }),
});

// Use in routes (omit auto-generated fields)
body: t.Omit(createPostSchema, ['id', 'authorId', 'createdAt', 'updatedAt'])

When you add fields in Drizzle, they auto-include in validation. Only validation rules need manual override.

Docs


Custom Authorization

For additional checks beyond login (e.g., ownership, roles, permissions), add logic in the route handler. The posts module shows an ownership check example:

.put("/:id", async ({ params, user, set }) => {
  const isOwner = await service.isPostOwner(params.id, user.id);

  if (!isOwner) {
    set.status = 403;
    return { error: "Forbidden", message: "Not allowed" };
  }

  // proceed...
}, { auth: true })

Deployment

This template is container-ready and works well with most Docker-based platforms.

Contributing

Thanks for your interest in contributing to Elysia Production Template! Contributions of all kinds are welcome: bug fixes, improvements, documentation, and examples.

This project aims to stay simple, production-focused, and easy to extend, so we appreciate well-scoped and thoughtful contributions.


How to contribute

  1. Fork this repository
  2. Create a new branch from main
  3. Make your changes
  4. Run linting and tests using bun test
  5. Open a pull request with clear PR description of the changes
git checkout -b my-feature

FAQ

Why Elysia.js?

Elysia is currently the fastest Bun framework with excellent TypeScript support, end-to-end type safety, and a clean plugin architecture. Perfect for building production APIs without sacrificing developer experience.

Can I use a different database?

The template uses PostgreSQL with Drizzle ORM. Drizzle also supports MySQL and SQLite. Update the connection config and adjust schema types as needed.

Is this production-ready?

Yes. Includes rate limiting, structured logging, error handling, health checks, and Docker support. For production, ensure you:

  • Use strong secrets
  • Set up database backups
  • Ensure HTTPS is handled by a reverse proxy or your hosting platform (nginx, Caddy, or managed TLS)

License

MIT