Claude-skill-registry creating-elysia-domains
Creates new domain modules in the Nexus Elysia API with proper typing for Eden Treaty consumers (Dashboard, The Machine)
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/creating-elysia-domains" ~/.claude/skills/majiayu000-claude-skill-registry-creating-elysia-domains && rm -rf "$T"
manifest:
skills/data/creating-elysia-domains/SKILL.mdsource content
Creating Elysia Domains
This skill guides creation of new feature domains in the Nexus API following established patterns for type-safe API development.
Capabilities
- Create domain directory structure
- Define Drizzle database schemas
- Create Elysia
types for API contractst.* - Implement repository and service layers
- Build typed route handlers
- Enable Eden Treaty type inference for consumers
Domain Structure
Create in
apps/nexus/src/domains/<domain-name>/:
domains/<domain-name>/ ├── schema.ts # Drizzle table definitions ├── types.ts # Elysia t.* types for API contracts ├── service.ts # Business logic ├── repository.ts # Database queries ├── routes.ts # Elysia route handlers └── index.ts # Re-exports
File Templates
schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; export const items = sqliteTable("items", { id: text("id").primaryKey(), name: text("name").notNull(), status: text("status", { enum: ["active", "inactive"] }).notNull().default("active"), createdAt: integer("created_at", { mode: "timestamp" }).notNull(), updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), }); export type Item = typeof items.$inferSelect; export type NewItem = typeof items.$inferInsert;
types.ts
import { t } from "elysia"; export const ItemParams = t.Object({ id: t.String(), }); export const CreateItemBody = t.Object({ name: t.String({ minLength: 1 }), status: t.Optional(t.Union([t.Literal("active"), t.Literal("inactive")])), }); export const UpdateItemBody = t.Object({ name: t.Optional(t.String({ minLength: 1 })), status: t.Optional(t.Union([t.Literal("active"), t.Literal("inactive")])), }); export const ItemResponse = t.Object({ id: t.String(), name: t.String(), status: t.Union([t.Literal("active"), t.Literal("inactive")]), createdAt: t.String(), updatedAt: t.String(), }); export const ItemListResponse = t.Array(ItemResponse);
repository.ts
import { eq } from "drizzle-orm"; import { db } from "../../infra/database"; import { items, type Item, type NewItem } from "./schema"; export const itemRepository = { async findAll(): Promise<Item[]> { return db.select().from(items); }, async findById(id: string): Promise<Item | undefined> { const results = await db.select().from(items).where(eq(items.id, id)); return results[0]; }, async create(data: NewItem): Promise<Item> { const results = await db.insert(items).values(data).returning(); return results[0]; }, async update(id: string, data: Partial<NewItem>): Promise<Item | undefined> { const results = await db .update(items) .set({ ...data, updatedAt: new Date() }) .where(eq(items.id, id)) .returning(); return results[0]; }, async delete(id: string): Promise<boolean> { const results = await db.delete(items).where(eq(items.id, id)).returning(); return results.length > 0; }, };
service.ts
import { randomUUID } from "crypto"; import { itemRepository } from "./repository"; import type { Item } from "./schema"; export const itemService = { async list(): Promise<Item[]> { return itemRepository.findAll(); }, async get(id: string): Promise<Item | null> { return (await itemRepository.findById(id)) ?? null; }, async create(data: { name: string; status?: "active" | "inactive" }): Promise<Item> { const now = new Date(); return itemRepository.create({ id: randomUUID(), name: data.name, status: data.status ?? "active", createdAt: now, updatedAt: now, }); }, async update(id: string, data: Partial<{ name: string; status: "active" | "inactive" }>): Promise<Item | null> { return (await itemRepository.update(id, data)) ?? null; }, async delete(id: string): Promise<boolean> { return itemRepository.delete(id); }, };
routes.ts
import { Elysia } from "elysia"; import { itemService } from "./service"; import { ItemParams, CreateItemBody, UpdateItemBody, ItemResponse, ItemListResponse, } from "./types"; const formatItem = (item: any) => ({ ...item, createdAt: item.createdAt.toISOString(), updatedAt: item.updatedAt.toISOString(), }); export const itemRoutes = new Elysia({ prefix: "/items" }) .get("/", async () => { const items = await itemService.list(); return items.map(formatItem); }, { response: ItemListResponse, detail: { tags: ["Items"], summary: "List all items" }, }) .get("/:id", async ({ params, error }) => { const item = await itemService.get(params.id); if (!item) return error(404, { message: "Item not found" }); return formatItem(item); }, { params: ItemParams, response: ItemResponse, }) .post("/", async ({ body }) => { const item = await itemService.create(body); return formatItem(item); }, { body: CreateItemBody, response: ItemResponse, }) .patch("/:id", async ({ params, body, error }) => { const item = await itemService.update(params.id, body); if (!item) return error(404, { message: "Item not found" }); return formatItem(item); }, { params: ItemParams, body: UpdateItemBody, response: ItemResponse, }) .delete("/:id", async ({ params, error }) => { const deleted = await itemService.delete(params.id); if (!deleted) return error(404, { message: "Item not found" }); return { success: true }; }, { params: ItemParams, });
Registration
Add to
apps/nexus/src/app.ts:
import { itemRoutes } from "./domains/items/routes"; const app = new Elysia() .use(itemRoutes)
Eden Treaty Usage
Dashboard (React)
import { treaty } from "@elysiajs/eden"; import type { App } from "@nexus/app"; const api = treaty<App>(import.meta.env.VITE_API_URL); const { data: items } = await api.items.get(); const { data: item } = await api.items({ id: "123" }).get(); const { data: newItem } = await api.items.post({ name: "Test" });
The Machine (Discord Bot)
import { treaty } from "@elysiajs/eden"; import type { App } from "@nexus/app"; const api = treaty<App>(process.env.API_URL!); const items = await api.items.get();
Critical Rules
- Never duplicate types - Dashboard and The Machine import from Nexus via Eden Treaty
- Use
types - Enables Eden type inferencet.* - Export App type - Required for consumers
- Format dates as ISO strings - JSON serialization compatibility
Protected Routes
import { autheliaMiddleware } from "../../middleware/authelia"; export const protectedRoutes = new Elysia({ prefix: "/admin" }) .use(autheliaMiddleware) .get("/dashboard", async ({ user }) => { return { message: `Hello ${user.name}` }; });
Commands
bun run db:push # Push schema changes bun run typecheck:api # Verify types bun run dev:api # Start dev server