Aphex

Configuration

The complete reference for aphex.config.ts — schemas, adapters, auth, GraphQL, versioning, branding, and security.

The aphex.config.ts file is the central configuration for your Aphex project. It's created by createCMSConfig() and wires together your schemas, database, storage, authentication, and email.

aphex.config.ts
import { createCMSConfig } from '@aphexcms/cms-core/server';
import { schemaTypes } from '$lib/schemaTypes/index.js';
import { db } from '$lib/server/db/index.js';
import { authProvider } from '$lib/server/auth/index.js';
import { email } from '$lib/server/email/index.js';

export default createCMSConfig({
	schemaTypes,
	database: db,
	email,
	auth: {
		provider: authProvider,
		loginUrl: '/login'
	},
	graphql: {
		defaultPerspective: 'published',
		path: '/api/graphql'
	},
	customization: {
		branding: {
			title: 'My CMS'
		}
	}
});

Options

OptionTypeRequiredDefaultDescription
schemaTypesSchemaType[]YesYour content schemas (document and object types).
databaseDatabaseAdapterYesDatabase adapter instance.
storageStorageAdapter | nullNoLocal filesystemStorage adapter for file uploads.
emailEmailAdapter | nullNonullEmail adapter for sending emails.
cacheCacheAdapter | nullNonullCache adapter for published-perspective reads.
authobjectNoAuthentication configuration.
graphqlboolean | GraphQLConfigNotrueGraphQL API configuration.
versioningobjectNoDocument version history options.
api(app: Hono) => voidNoRegister custom HTTP routes / middleware on the built-in Hono app.
customizationobjectNoBranding and theme options.
logLevelstringNoauto'debug' in dev, 'warn' in prod.
securityobjectNoSecurity options (asset signing).

schemaTypes

An array of SchemaType objects defining your content model. Each schema is either a document (top-level collection) or an object (reusable nested structure).

import { schemaTypes } from '$lib/schemaTypes/index.js';

createCMSConfig({
	schemaTypes
	// ...
});

Schemas are registered in the database on first startup and re-synced when they change during development (via Vite HMR). See Schemas for the full schema reference.

database

A DatabaseAdapter instance that handles all content storage. This is the only required adapter — Aphex is database-agnostic through this interface.

import { db } from '$lib/server/db/index.js';

createCMSConfig({
	database: db
	// ...
});

The PostgreSQL adapter (@aphexcms/postgresql-adapter) is the built-in implementation. It uses Drizzle ORM and supports Row-Level Security for multi-tenant isolation.

storage

A StorageAdapter instance for file uploads and asset management. If not provided, Aphex creates a local filesystem adapter automatically:

// Default (no config needed):
// Files stored at ./storage/assets
// Served via /assets/{id}/{filename}

// S3-compatible storage:
import { storage } from '$lib/server/storage/index.js';

createCMSConfig({
	storage
	// ...
});

Available adapters:

  • Local filesystem (default) — stores files in ./storage/assets.
  • @aphexcms/storage-s3 — S3-compatible storage (AWS S3, Cloudflare R2, MinIO).

email

An EmailAdapter instance for sending transactional emails (password resets, invitations, email verification). If not provided, email features are disabled.

import { email } from '$lib/server/email/index.js';

createCMSConfig({
	email
	// ...
});

Available adapters:

  • @aphexcms/nodemailer-adapter — SMTP via Nodemailer. Includes a createMailpitAdapter() shorthand for local development.
  • @aphexcms/resend-adapterResend API for production.

In development, createMailpitAdapter() sends all emails to Mailpit on localhost:1025. The base template wires this up automatically — see Getting Started.

cache

An optional CacheAdapter that caches reads with perspective: 'published'. Entries are invalidated automatically when a document is published, unpublished, or deleted. Draft reads bypass the cache so the admin UI always sees the latest data.

import { InMemoryCacheAdapter } from '@aphexcms/cms-core/server';

createCMSConfig({
	cache: new InMemoryCacheAdapter({ maxSize: 5000 })
});

The base template exports a shared cacheAdapter from src/lib/server/cache/index.ts and passes the same instance to both the CMS config and Better Auth (for API-key lookup caching).

ValueBehavior
undefined or nullNo caching (default).
InMemoryCacheAdapterLRU in-memory cache, shipped with @aphexcms/cms-core/server.
Custom CacheAdapterImplement the CacheAdapter interface for Redis, Upstash, etc.

auth

Authentication configuration. If not provided, the CMS has no access control.

createCMSConfig({
	auth: {
		provider: authProvider,
		loginUrl: '/login'
	}
	// ...
});
OptionTypeRequiredDefaultDescription
auth.providerAuthProviderYesAuthentication provider instance.
auth.loginUrlstringNo'/login'Redirect URL for unauthenticated users.

The AuthProvider interface handles:

  • Session auth — browser sessions for the admin UI.
  • API key auth — programmatic access via x-api-key header.
  • User management — fetching users, changing names.
  • Password reset — request and confirm password resets.

The built-in integration uses Better Auth with organization support. See the scaffolded src/lib/server/auth/ directory for the full implementation.

graphql

Controls the built-in GraphQL API. Can be true (defaults), false (disabled), or a config object.

// Enabled with defaults
createCMSConfig({ graphql: true });

// Disabled
createCMSConfig({ graphql: false });

// Custom options
createCMSConfig({
	graphql: {
		defaultPerspective: 'published',
		path: '/api/graphql',
		enableGraphiQL: true,
		defaultQuery: '{ allPost { id title } }'
	}
});
OptionTypeDefaultDescription
defaultPerspective'draft' | 'published''published'Default perspective when not specified in a query.
pathstring'/api/graphql'GraphQL endpoint path.
enableGraphiQLbooleantrueEnable the interactive GraphiQL IDE at the endpoint.
defaultQuerystringBuilt-in exampleDefault query shown in GraphiQL.

See GraphQL API for the full reference.

versioning

Controls document version history. Each draft save and publish creates an entry in the cms_document_versions table. See Version History for the full reference.

createCMSConfig({
	versioning: {
		maxVersions: 25 // default — set 0 to disable rolling cleanup
	}
});

Prop

Type

api

The escape hatch for registering custom HTTP routes and middleware. Aphex's HTTP API runs on Hono — the function you pass receives the same Hono app the built-in routes mount onto, before they mount.

aphex.config.ts
createCMSConfig({
	api: (app) => {
		// Add a brand-new endpoint
		app.post('/send-email', async (c) => {
			const { aphexCMS, auth } = c.var;
			// ...
			return c.json({ success: true });
		});

		// Wrap a built-in route with side effects (registration order
		// matters: register before built-ins to intercept them)
		app.use('/organizations/invitations', async (c, next) => {
			await next();
			if (c.res.status === 201) sendInviteEmail(/* ... */);
		});
	}
});

Hono is registration-order-strict and first-match-wins. Registering before built-ins lets you wrap them with app.use() or override them outright with app.METHOD(path, handler).

c.var exposes the same context the built-in routes use:

VarTypeDescription
c.var.aphexCMSCMSInstancesLocal API, services, adapters.
c.var.authAuth | nullResolved auth — session or API key.

customization

Branding and theme options for the admin UI.

createCMSConfig({
	customization: {
		branding: {
			title: 'My CMS',
			logo: '/images/logo.png',
			favicon: '/favicon.ico'
		},
		theme: {
			colors: { primary: '#3b82f6' },
			fonts: { sans: 'Inter, sans-serif' }
		}
	}
});

Branding

OptionTypeDefaultDescription
titlestring'Aphex CMS'Display title in the admin UI.
logostringURL to a logo image.
faviconstringURL to a favicon.

Theme

OptionTypeDefaultDescription
colorsRecord<string, string>Custom color values (e.g. { primary: '#3b82f6' }).
fontsRecord<string, string>Custom font families (e.g. { sans: 'Inter, sans-serif' }).

security

Security options for asset access control.

createCMSConfig({
	security: {
		assetSigningSecret: 'a-long-random-string-32-chars-minimum'
	}
});
OptionTypeDefaultDescription
assetSigningSecretstringSecret key for HMAC-signed asset URLs. Enables temporary, expiring URLs for multi-tenant asset access without exposing API keys. Should be 32+ characters.

How config is consumed

The createCMSHook() function in hooks.server.ts takes your config and creates singleton instances of all adapters:

src/hooks.server.ts
import { createCMSHook } from '@aphexcms/cms-core/server';
import config from '../aphex.config.ts';

const aphexHook = createCMSHook(config);

export const handle = sequence(authHook, aphexHook);

On the first request, the hook:

  1. Creates the storage adapter (or uses the default local filesystem).
  2. Initializes the AssetService with the storage adapter.
  3. Creates the CMSEngine and registers schemas in the database.
  4. Creates the LocalAPI (unified data layer).
  5. Calls your api(app) hook (if provided), then mounts the built-in Hono routes.
  6. Initializes GraphQL if enabled.

On every subsequent request, the hook injects these singletons into event.locals.aphexCMS, making them available in all route handlers and load functions.

What's available on event.locals.aphexCMS

event.locals.aphexCMS.localAPI; // LocalAPI instance
event.locals.aphexCMS.databaseAdapter; // DatabaseAdapter
event.locals.aphexCMS.assetService; // AssetService
event.locals.aphexCMS.storageAdapter; // StorageAdapter
event.locals.aphexCMS.emailAdapter; // EmailAdapter (or null)
event.locals.aphexCMS.cmsEngine; // CMSEngine
event.locals.aphexCMS.rolesService; // RolesService
event.locals.aphexCMS.auth; // AuthProvider (or undefined)
event.locals.aphexCMS.config; // Your resolved CMSConfig (includes `cache`, `versioning`, etc.)
event.locals.aphexCMS.graphqlSettings; // GraphQL endpoint info (or null)
Edit on GitHub

Last updated on