A Sanity-inspired, database-agnostic CMS built with SvelteKit V2 (Svelte 5)
| Package | Description |
|---|---|
@aphexcms/cms-core |
Database-agnostic core engine with admin UI, API handlers, and built-in GraphQL |
@aphexcms/postgresql-adapter |
PostgreSQL implementation with Drizzle ORM |
@aphexcms/storage-s3 |
S3-compatible storage (R2, AWS S3, MinIO, etc.) |
@aphexcms/nodemailer-adapter |
Nodemailer/SMTP email adapter (with Mailpit helper for local dev) |
@aphexcms/resend-adapter |
Resend API email adapter for production |
@aphexcms/ui |
Shared shadcn-svelte component library |
@aphexcms/base |
Starter template scaffolded by create-aphex |
@aphexcms/studio |
Reference implementation app (drives the template) |
create-aphex |
Scaffolder invoked by pnpm create aphex / npm create aphex@latest |
π‘ Architecture deep-dive: See ARCHITECTURE.md for detailed design patterns and internals.
π‘ Adding UI components: Run
pnpm shadcn <component-name>to add shadcn-svelte components to@aphexcms/ui
create-aphex (Recommended)The fastest way to get started:
pnpm create aphex my-app
# or
npm create aphex my-app
# or
npx create-aphex my-app
This will:
.env file with all required environment variablesThen:
cd your-project-name
pnpm install
pnpm db:start # Start PostgreSQL via Docker
pnpm db:push # Push database schema
pnpm dev # Start development server
π Admin UI: http://localhost:5173/admin
If you want to contribute to Aphex or work with the monorepo:
# Clone and install
git clone https://github.com/IcelandicIcecream/aphex.git
cd aphex
pnpm install
# Configure environment
cd apps/studio
cp .env.example .env
# Default values work for local development
cd ../..
# Start database and migrate
pnpm db:start
pnpm db:migrate
# Start dev server
pnpm dev
π Admin UI: http://localhost:5173/admin
By default, uses local filesystem. For cloud storage:
pnpm add @aphexcms/storage-s3
// apps/studio/src/lib/server/storage/index.ts
import { s3Storage } from '@aphexcms/storage-s3';
export const storageAdapter = s3Storage({
bucket: env.R2_BUCKET,
endpoint: env.R2_ENDPOINT,
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
publicUrl: env.R2_PUBLIC_URL
}).adapter;
// aphex.config.ts
import { storageAdapter } from './src/lib/server/storage';
export default createCMSConfig({
storage: storageAdapter // Pass your adapter
});
Content models live in your app as TypeScript objects:
// apps/studio/src/lib/schemaTypes/page.ts
export const page: SchemaType = {
name: 'page',
type: 'document',
title: 'Page',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
validation: (Rule) => Rule.required().max(100)
},
{
name: 'slug',
type: 'slug',
title: 'URL Slug',
source: 'title', // Auto-generate from title
validation: (Rule) => Rule.required()
},
{
name: 'content',
type: 'array',
title: 'Content Blocks',
of: [{ type: 'textBlock' }, { type: 'imageBlock' }, { type: 'catalogBlock' }]
},
{
name: 'author',
type: 'reference',
title: 'Author',
to: [{ type: 'author' }] // Reference to other documents
}
]
};
Register schemas in your config:
// aphex.config.ts
import { page, author, textBlock } from './src/lib/schemaTypes';
export default createCMSConfig({
schemaTypes: [page, author, textBlock]
// ...
});
Available field types: string, text, number, boolean, slug, url, date, datetime, image, file, array, object, reference
The admin UI is a responsive 3-panel layout inspired by Sanity Studio:
# Just IDs (default)
GET /api/documents/123
# Resolve first-level references
GET /api/documents/123?depth=1
# Resolve nested references
GET /api/documents/123?depth=2
Circular reference protection prevents infinite loops. Max depth: 5.
GraphQL is built into cms-core and enabled by default. To customize:
export default createCMSConfig({
graphql: {
path: '/api/graphql',
enableGraphiQL: true
}
});
Visit /api/graphql for GraphiQL interface with auto-generated schema.
# Development
pnpm dev # Start all packages in watch mode
pnpm dev:studio # Start studio app only
pnpm dev:package # Start cms-core package only
pnpm dev:docs # Start dev server
# Building
pnpm build # Build all packages (Turborepo)
pnpm preview # Preview production build
# Database
pnpm db:start # Start PostgreSQL (Docker)
pnpm db:push # Push schema changes (dev)
pnpm db:generate # Generate migrations
pnpm db:migrate # Run migrations (prod)
pnpm db:studio # Open Drizzle Studio
# Code Quality
pnpm lint # Prettier + ESLint check
pnpm format # Format code with Prettier
pnpm check # Type-check all packages
# UI Components (shadcn-svelte β @aphexcms/ui)
pnpm shadcn button # Add button component
pnpm shadcn dialog # Add dialog component
# Components shared between cms-core & studio
Batteries included with Better Auth:
curl http://localhost:5173/api/documents?docType=page \
-H "x-api-key: your-api-key-here"
Generate keys from /admin/settings.
Bring your own auth: Implement the
AuthProviderinterface to use Auth.js, Lucia, or custom solutions.
pnpm formatpnpm check$state, $derived, $effect)DatabaseAdapter interface in a new package + AuthProviderStorageAdapter interfaceCMSPlugin interfaceSee ARCHITECTURE.md for detailed extension guides.
Include:
pnpm create aphex / npm create aphex@latest (published as create-aphex)release.yml + sync-template.yml + ChangesetsmaxVersions (GET /api/documents/{id}/versions)read/write scopes or a fine-grained capabilities allowlistInMemoryCacheAdapter for published reads + API-key lookupspreview: { select: { title, subtitle } } (with dot-paths) on document + object types; rendered in document list, array item rows, and reference pickersingleton: true expose a SingletonCollection<T> surface with get/update/getSingletonId and hide Create/Delete in adminapps/studiopnpm build works without any .env (server modules guarded with building flag); Dockerfile + Procfile ship in the template for Docker / buildpack deploysaphex() plugin bundles HMR + dayjs alias + SSR/optimizeDeps tuning so consumers don't copy boilerplatellms.txt, per-page markdown, and "Copy / Open in ChatGPT / Open in Claude" actionsbase (newsletter, marketing site, docs, portfolio)portable-textβstyle fieldTODO-image-transforms.md)InMemoryCacheAdapterDatabaseAdapter implementationsInspired by Sanity.io β’ Built with SvelteKit, Drizzle ORM, Better Auth, and shadcn-svelte