Raypx

Internal Packages

Overview of the 11 monorepo packages and their responsibilities.

Raypx is organized as a pnpm workspace monorepo with 11 internal packages. Each package has a single responsibility and exports a clean public API. All packages follow the @raypx/{name} naming convention.

Package Overview

Infrastructure

PackageDescription
@raypx/envEnvironment variable validation using Zod. Every package imports from here to get typed, validated config values.
@raypx/loggerStructured logging with Pino. Provides a shared logger instance with consistent formatting and log levels.
@raypx/databaseDrizzle ORM setup with PostgreSQL. Contains schema definitions, migrations, and the database client factory.

Feature

PackageDescription
@raypx/authBetter Auth server configuration. Wraps Better Auth with plugins (OAuth, admin, email), a Drizzle adapter, and typed client exports.
@raypx/rpcoRPC router definitions and context. Contains all API procedures, input/output types, and the request context factory.
@raypx/storageFile storage abstraction. Implements a driver interface with local disk and S3 backends.
@raypx/emailTransactional email service. Integrates Resend with React Email templates for verification, password reset, and notification emails.
@raypx/otpOne-time password service. Generates, stores, and verifies OTPs with configurable expiry and rate limits.
@raypx/billingPayment and subscription management. Integrates Polar for SaaS billing with plan management and webhook handling.

Presentation

PackageDescription
@raypx/i18nInternationalization with use-intl. Provides locale detection, message loading, server-side locale context, and locale-prefixed routing.
@raypx/uiShared UI components built on shadcn/ui. Contains button, card, input, dialog, and other primitives used across the application.

Package Details

@raypx/env

Validates environment variables at application startup. Each package defines its required and optional variables in a Zod schema:

// packages/env/src/env.ts
import { z } from "zod/v4"

export const envSchema = z.object({
  APP_KEY: z.string().min(1),
  DATABASE_URL: z.string().url(),
  AUTH_SECRET: z.string().min(32),
})

export type Env = z.infer<typeof envSchema>

The @raypx/auth package extends this with its own variables:

// packages/auth/src/env.ts
export const authEnvSchema = z.object({
  AUTH_URL: z.string().url(),
  AUTH_GOOGLE_SECRET: z.string().optional(),
  AUTH_GITHUB_ID: z.string().optional(),
  AUTH_GITHUB_SECRET: z.string().optional(),
  ADMIN_EMAILS: z.string().optional(),
})

@raypx/database

Contains Drizzle schema definitions organized by domain. The client is created lazily to avoid importing Node.js modules at the top level:

packages/database/src/
  schema/       # Drizzle table definitions
  migrate.ts    # Migration runner
  health.ts     # Health check query
  client.ts     # Database client factory

@raypx/auth

Wraps Better Auth with all plugins pre-configured. Exports both the server instance and a typed client for use in React components:

// packages/auth/src/index.ts
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { admin } from "better-auth/plugins"

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  plugins: [admin()],
  emailAndPassword: { enabled: true },
  socialProviders: { google: { ... }, github: { ... } },
})

@raypx/rpc

Defines all oRPC procedures organized into routers. The context factory creates a per-request context object with the current user session:

// packages/rpc/src/context.ts
export async function createContext({ req }: { req: Request }) {
  const session = await auth.api.getSession({ headers: req.headers })
  return { session, user: session?.user ?? null }
}

@raypx/i18n

Manages internationalization with two locales (en-US, zh-CN). Provides server-side middleware for locale detection from URL paths and cookies, and client-side hooks for locale switching:

// packages/i18n/src/request.ts
export function handleLocaleMiddleware(request: Request) {
  const path = new URL(request.url).pathname
  const locale = detectLocale(path) // extract from /en-US/ or /zh-CN/ prefix
  return { locale, redirect: needsRedirect(path, locale), setCookie: ... }
}

@raypx/ui

Shared React components built on Radix primitives and Tailwind CSS. Managed with the shadcn CLI — new components are added with pnpm bump-ui and live in packages/ui/src/components/.

Adding a New Package

To add a new internal package:

  1. Create the package directory under packages/:
mkdir -p packages/my-package/src
  1. Create a package.json with the @raypx/ scope:
{
  "name": "@raypx/my-package",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "exports": { ".": "./src/index.ts" }
}
  1. Add environment variables to packages/env/src/env.ts if needed.

  2. Import the package in the root application:

pnpm add @raypx/my-package@workspace:*
  1. Turborepo will automatically detect the new workspace. No changes to turbo.json are needed unless you want to define custom tasks.

Next Steps

On this page