Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Bun + Elysia + React Monorepo Boilerplate

A modern, type-safe full-stack boilerplate using Bun, Elysia, React Router, and Better Auth.

πŸš€ Features

Backend (Bun + Elysia)

  • ⚑ Bun Runtime - Ultra-fast JavaScript runtime
  • 🦊 Elysia - Ergonomic web framework with end-to-end type safety
  • πŸ” Better Auth - Modern authentication with session management, API keys, and OAuth
  • πŸ—„οΈ MikroORM - Type-safe ORM with SQLite support
  • πŸ’‰ @cryxto/ioc-n-di - Lightweight DI container with TypeScript decorators
  • πŸ“ OpenAPI - Auto-generated API documentation
  • 🎯 Access Control - Role-based permissions system
  • πŸ”‘ API Key Management - Built-in API key authentication

Frontend (React + Vite)

  • βš›οΈ React 19 - Latest React with React Router v7
  • 🎨 Tailwind CSS - Utility-first CSS framework
  • πŸ” Better Auth Client - Seamless authentication integration
  • πŸ“Š TanStack Query - Powerful data fetching and caching
  • 🎭 Shadcn UI - Beautiful, accessible component library
  • πŸ“± Responsive Design - Mobile-first approach

Monorepo

  • πŸ“¦ Bun Workspaces - Efficient monorepo management
  • πŸ”— Shared Contracts - Type-safe API contracts between frontend and backend
  • πŸ› οΈ TypeScript - Full type safety across the stack

πŸ“ Project Structure

.
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ backend/              # Elysia API server
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ main/         # Application entry point
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ index.ts      # Barrel exports
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ types.ts      # Type definitions
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ factories.ts  # App & server factories
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ providers.ts  # DI providers
β”‚   β”‚   β”‚   β”‚   └── decorator.ts  # DI decorators
β”‚   β”‚   β”‚   β”œβ”€β”€ modules/      # Feature modules
β”‚   β”‚   β”‚   β”œβ”€β”€ entities/     # Database entities
β”‚   β”‚   β”‚   β”œβ”€β”€ infrastructures/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ better-auth/  # Auth configuration
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ config/       # Environment config
β”‚   β”‚   β”‚   β”‚   └── orm/          # Database setup
β”‚   β”‚   β”‚   └── common/       # Shared utilities
β”‚   β”‚   └── public/           # Static assets
β”‚   β”‚
β”‚   └── frontend/             # React application
β”‚       β”œβ”€β”€ app/
β”‚       β”‚   β”œβ”€β”€ components/   # React components
β”‚       β”‚   β”œβ”€β”€ routes/       # React Router pages
β”‚       β”‚   β”œβ”€β”€ lib/
β”‚       β”‚   β”‚   β”œβ”€β”€ auth.ts       # Better Auth client
β”‚       β”‚   β”‚   β”œβ”€β”€ api/          # API client functions
β”‚       β”‚   β”‚   └── hooks/        # Custom React hooks
β”‚       β”‚   └── root.tsx      # App entry point
β”‚       └── public/           # Static files
β”‚
└── packages/
    └── contracts/            # Shared TypeScript types
        └── src/
            └── index.ts      # Exported types

πŸ› οΈ Setup

Prerequisites

  • Bun >= 1.0.0
  • Node.js >= 18 (for frontend tooling)

Installation

  1. Clone the repository:
git clone https://github.com/Cryxto/monorepo-elysia-rr
cd monorepo-elysia-rr
  1. Install dependencies:
bun install
  1. Set up environment variables:

Backend (apps/backend/.env):

DATABASE_URL=file:./dev.db
BETTER_AUTH_SECRET=your-secret-key-min-32-chars
BETTER_AUTH_URL=http://localhost:3000
FRONTEND_URL=http://localhost:5173
NODE_ENV=development

Frontend (apps/frontend/.env):

VITE_API_URL=http://localhost:3000
  1. Initialize the database:
bun mikro-rom schema:update --run --safe

πŸš€ Development

Start All Services

From the root directory:

# Start both backend and frontend
bun dev

Or start individually:

Backend:

cd apps/backend
bun dev

Server runs at: http://localhost:3000

Frontend:

cd apps/frontend
bun dev

App runs at: http://localhost:5173

API Documentation

OpenAPI documentation available at:

πŸ—οΈ Architecture Patterns

Backend

Dependency Injection

The backend uses @cryxto/ioc-n-di - a lightweight dependency injection container with TypeScript decorators.

Key Principle: We import directly from @cryxto/ioc-n-di throughout the codebase - NO wrapper files or custom aliases. This keeps the architecture clean and transparent.

Container Setup (src/container.ts):

import 'reflect-metadata';
import { Container } from '@cryxto/ioc-n-di';

// Create and export singleton container instance
export const container = Container.createOrGet();

Container Initialization (src/index.ts):

import { container } from '@container';
import { Provider } from '@cryxto/ioc-n-di';

// Bootstrap all providers
await container.bootstrap(providers);

// Get instances
const service = container.getInstanceOrThrow(MyService);

Usage in Services:

import { Injectable } from '@cryxto/ioc-n-di';
import { InjectServer } from '@main/decorator';
import type { ApiServer } from '@main';

@Injectable()
export class MyController {
  constructor(
    @InjectServer() private server: ApiServer,
    private myService: MyService, // Auto-injected by type
  ) {}
}

Custom Decorators (src/main/decorator.ts):

// Custom decorators for common injections
import { Inject } from '@cryxto/ioc-n-di';
import { API_SERVER } from '../common/tokens';

export const InjectServer = () => Inject(API_SERVER);

Key Principles:

  • βœ… Import directly from @cryxto/ioc-n-di - no wrappers
  • Container instance created once in src/container.ts using Container.createOrGet()
  • Import container via @container alias throughout the app
  • Tokens defined in common/tokens.ts to avoid circular dependencies
  • Custom decorators live in main/decorator.ts
  • Services auto-injected by type (no explicit token needed)

Module Structure

Each module follows a consistent pattern:

modules/
  └── feature/
      β”œβ”€β”€ feature.controller.ts    # Route handlers
      β”œβ”€β”€ feature.service.ts       # Business logic
      β”œβ”€β”€ feature.repository.ts    # Data access
      └── index.ts                 # Module exports

Authentication & Authorization

Session-based Auth:

// Protected route
route.get('/profile',
  async ({ user }) => ({ user }),
  { auth: true }
);

Permission Checks:

beforeHandle: async ({ user }) => {
  const hasPermission = await auth.instance.api.userHasPermission({
    body: {
      userId: user.id,
      permission: { resource: ['action'] }
    }
  });
  if (!hasPermission.success) {
    throw new ForbiddenError('Insufficient permissions');
  }
}

API Key Auth:

// Create API key
const { data } = await authClient.apiKey.create({
  name: 'my-api-key',
  expiresIn: 86400, // 24 hours in seconds
});

// Use in requests
fetch('/api/resource', {
  headers: {
    'Authorization': `Bearer ${apiKey}`
  }
});

Frontend

Authentication

Better Auth client is configured in app/lib/auth.ts:

import { authClient, useSession } from '@/lib/auth';

// In components
function Profile() {
  const { data: session } = useSession();

  return <div>Welcome, {session?.user.name}</div>;
}

// Sign in
await authClient.signIn.email({
  email: 'user@example.com',
  password: 'password',
});

API Integration

import { useQuery } from '@tanstack/react-query';

function MyComponent() {
  const { data, isLoading } = useQuery({
    queryKey: ['myData'],
    queryFn: async () => {
      const res = await fetch('/api/my-data', {
        credentials: 'include', // Include cookies
      });
      return res.json();
    },
  });
}

Protected Routes

// In routes
import { ProtectedRoute } from '@/components/protected-route';

export default function Dashboard() {
  return (
    <ProtectedRoute>
      <DashboardContent />
    </ProtectedRoute>
  );
}

πŸ”§ Available Scripts

Root

  • bun dev - Start all services
  • bun install - Install all dependencies

Backend

  • bun dev - Start dev server with watch mode
  • bun run migration:create - Create new migration
  • bun run migration:up - Run migrations
  • bun run migration:down - Rollback migrations
  • bun run seed - Seed database with sample data

Frontend

  • bun dev - Start dev server
  • bun run build - Build for production
  • bun run preview - Preview production build
  • bun run lint - Run ESLint

πŸ“ Creating New Features

Backend

  1. Create a new module:
cd apps/backend/src/modules
mkdir my-feature
  1. Create service:
// my-feature/my-feature.service.ts
import { Injectable } from '@di';

@Injectable()
export class MyFeatureService {
  async doSomething() {
    // Your logic here
  }
}
  1. Create controller:
// my-feature/my-feature.controller.ts
import { Injectable } from '@di';
import { BaseController } from '@common/base.controller';
import { InjectServer } from '@main/decorator';
import type { ApiServer } from '@main';

@Injectable()
export class MyFeatureController extends BaseController {
  constructor(
    @InjectServer() server: ApiServer,
    private myFeatureService: MyFeatureService,
  ) {
    super(server);
  }

  protected applyRoutes(): void {
    this.server.group('/my-feature', (route) =>
      route.get('/', () => this.myFeatureService.doSomething())
    );
  }
}
  1. Register in DI container:
// src/index.ts
import { MyFeatureController } from '@modules/my-feature/my-feature.controller';
import { MyFeatureService } from '@modules/my-feature/my-feature.service';

// Add to providers array
providers: [
  // ...
  MyFeatureService,
  MyFeatureController,
]

Frontend

  1. Create component:
// app/components/my-component.tsx
export function MyComponent() {
  return <div>My Component</div>;
}
  1. Create route:
// app/routes/my-route.tsx
export default function MyRoute() {
  return <MyComponent />;
}
  1. Add to navigation (if needed):
// Update app/components/nav-main.tsx or similar

πŸ” Authentication & Authorization

Roles

  • admin: Full access to all resources
  • regular: Limited access (configurable per resource)

Permissions

Defined per resource with actions:

  • create, read, update, delete
  • share, revoke, invite
  • approve, disapprove, reserve

OAuth Providers

Configure in apps/backend/src/infrastructures/better-auth/auth.ts:

  • Google
  • GitHub
  • Discord
  • Microsoft

πŸ—„οΈ Database

Migrations

# Create migration
bun run migration:create

# Run migrations
bun run migration:up

# Rollback
bun run migration:down

Entities

Define entities in apps/backend/src/entities/:

import { Entity, Property } from '@mikro-orm/core';
import { BaseEntity } from './base.entity';

@Entity()
export class MyEntity extends BaseEntity {
  @Property()
  name!: string;
}

🚒 Deployment

See DEPLOYMENT.md for detailed deployment instructions.

Quick Deploy to Fly.io:

./deploy.sh

This uses a hybrid build strategy:

  • Frontend built locally (avoids React 19 + Bun compatibility issues)
  • Backend deployed with pre-built assets
  • Zero-downtime deployments

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

πŸ“„ License

MIT

πŸ™ Acknowledgments

πŸ“ž Support

For issues and questions:

  • Open an issue on GitHub
  • Check the documentation links above

Happy coding! πŸŽ‰

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages