Configuration API
Companions:
- Configuration — which application files own these objects and which runtime imports each one.
- Collections API — the collection tuple and the server/client presentation registries configured here.
- Core composition — initialization order, validation, adapters, and package boundaries.
- Admin-config registration — the dual admin registration path used by the TanStack Start host.
This is the exact application-facing configuration surface from @byline/core and the request-bound client getter surface from @byline/client/server. Use it when wiring byline/server.config.ts, byline/admin.config.ts, or server-side application reads.
BaseConfig
BaseConfig contains the configuration shared by the server and admin. The server and admin configs extend it independently.
interface BaseConfig { i18n: I18nConfig collections: readonly CollectionDefinition[] routes?: RoutesConfigInput}Property | Required | Description |
| Yes | Admin-interface locales, content locales, display labels, and admin translation bundles. |
| Yes | Canonical readonly collection tuple. The server and admin must receive the same schema definitions. |
| No | Partial admin, API, and sign-in mount paths. |
i18n
interface I18nConfig { admin: { defaultLocale: string locales: string[] localeDefinitions?: ReadonlyArray<{ code: string; nativeName: string }> } content: { defaultLocale: string locales: string[] localeDefinitions?: ReadonlyArray<{ code: string; nativeName: string }> } translations?: TranslationBundleShape}Property | Required | Description |
| Yes | Fallback locale for the admin interface. It must be present in |
| Yes | Permitted admin-interface locale codes. Every declared code requires at least one registered translation namespace. |
| No | Display labels used by the admin language switcher. Missing codes fall back through |
| Yes | Source locale assigned to newly created documents and the default locale for reads that do not specify one. Existing documents retain their own |
| Yes | Content locales the installation can author and serve. |
| No | Host-authored display labels for public content-language affordances. Byline stores and exposes them but does not render them itself. |
| Conditional | Locale → namespace → key → ICU message string. Required at boot when |
The complete behavioral model is in Internationalization.
RoutesConfigInput
interface RoutesConfigInput { admin?: string api?: string signIn?: string}Property | Default | Description |
|
| Root of the Byline admin route tree. |
|
| Root reserved for Byline API-facing routes. |
|
| Sign-in route outside both the admin and API trees. |
resolveRoutes(input?) normalizes repeated and missing slashes, rejects query/hash/encoded/unsafe paths, rejects . and .. segments, requires non-overlapping route trees, and returns a frozen RoutesConfig with all three keys.
import { resolveRoutes } from '@byline/core'
export const routes = resolveRoutes({ admin: '/cms', api: '/api', signIn: '/cms-sign-in',})Changing this object does not rename the physical host route files. Routing and API documents that coordination boundary.
createSignInRoute(path, options?)
The TanStack Start host adapter exposes the sign-in route factory from @byline/host-tanstack-start/routes:
interface CreateSignInRouteOptions { homeUrl?: string}
function createSignInRoute( path: string, options?: CreateSignInRouteOptions): RouteOption | Default | Description |
|
| Client-safe destination for the sign-in form's Home link. Use the default for an integrated same-origin host. Pass an absolute host-owned URL when navigation must target another origin. |
The route preserves and validates the callbackUrl search parameter used to return a newly authenticated editor to the requested admin page. homeUrl is independent of that redirect and is not part of BaseConfig, AdminConfig, or ServerConfig. An absolute value changes only the Home destination; it does not transfer Byline's host-only session or preview cookies across origins.
AdminConfig
AdminConfig extends BaseConfig with admin presentation and browser-side adapter slots. It may contain React component references and must remain outside public route bundles.
interface AdminConfig extends BaseConfig { admin?: CollectionAdminConfig[] collectionGroups?: CollectionGroupDefinition[] blockAdmin?: BlockAdminConfig[] slugifier?: SlugifierFn fields?: { richText?: { editor: RichTextEditorComponent } }}Property | Default | Description |
|
| Collection presentation configs registered by |
|
| Ordered registry of dashboard collection groups, each |
|
| Site-wide block presentation configs registered by |
|
| Client copy used by the admin path widget for live previews. Register the same pure synchronous function on |
| None | React editor component used for rich-text fields unless a per-field admin config overrides it. Rich-text fields in the admin require a registered editor. |
import { type AdminConfig, defineAdminConfig } from '@byline/core'
export const config: AdminConfig = { i18n, routes, collections, admin: [DocsAdmin, NewsAdmin, PagesAdmin], collectionGroups: [{ name: 'media', label: 'Media' }], blockAdmin: [QuoteBlockAdmin, PhotoBlockAdmin], fields: { richText: { editor: LexicalRichTextAi } },}
defineAdminConfig(config)defineAdminConfig(config)
function defineAdminConfig(config: AdminConfig): ResolvedAdminConfigValidates collection, collection-admin, and block-admin registrations; resolves routes; registers the admin singleton in the current module graph; and returns the resolved object.
getAdminConfig()
function getAdminConfig(): ResolvedAdminConfigReturns the registered admin config. During server-side rendering, if only a server config is available, it returns a compatible fallback containing shared i18n, routes, collections, and the server slugifier, plus admin: []. It throws when neither config exists.
ServerConfig
ServerConfig<TAdminStore> extends BaseConfig with server-only implementations. initBylineCore() is the normal registration entry point.
interface ServerConfig<TAdminStore = unknown> extends BaseConfig { db: IDbAdapter hooks?: ServerHooksConfig storage?: IStorageProvider slugifier?: SlugifierFn uploads?: { filenameSlugifier?: FilenameSlugifierFn } sessionProvider?: SessionProvider adminStore?: TAdminStore fields?: { richText?: { populate?: RichTextPopulateFn embed?: RichTextEmbedFn toMarkdown?: RichTextToMarkdownFn toText?: RichTextToTextFn } } search?: SearchProvider scheduledPublication?: { enabled: boolean } recurringTasks?: readonly RecurringTaskDefinition[]}Property | Requirement or default | Description |
| Required | Database adapter implementing typed storage, transactions, audit operations, collection bootstrap, and tree operations when used. |
| Optional | Server-only collection and upload hook registry attached after configuration validation. |
| Conditional | Installation-wide upload storage fallback. Required when an upload field has no |
|
| Authoritative document-path slugifier. Must be pure, synchronous, and identical to the client copy when customized. |
|
| Server-only transformation for the human-readable base name of every uploaded file before hooks and provider key composition. |
| Optional in the type | Authentication session implementation. Admin sign-in, refresh, verification, and revocation require one. |
| Optional | Adapter-built admin repositories surfaced on |
| Conditional | Write-time rich-text relation embedder. Required when a rich-text field effectively enables |
| Conditional | Read-time rich-text relation refresher. Required when a rich-text field effectively enables |
| Optional | Synchronous one-way serializer used by markdown document export, |
| Conditional | Plain-text extractor required when a collection search body includes a rich-text field. |
| Conditional | Search provider required when any collection declares |
| Disabled | Set |
|
| Definitions created with |
The host owns execution lifetime. A long-running server may call startBylineScheduler(core) from @byline/core/scheduler; an externally orchestrated installation may call runDueTasks(core) instead. Importing server configuration from a seed, migration, or maintenance script therefore never acquires a lease or keeps that process alive. Scheduled publication remains optional convenience: ordinary CollectionHandle.changeStatus() publication neither requires nor consults a schedule row.
ServerHooksConfig
interface ServerHooksConfig { collections?: Record<string, CollectionHooks | CollectionHooksLoader> uploads?: Record<string, UploadHooks | UploadHooksLoader>}Property | Key | Description |
| Collection path, such as | Hooks merged onto that collection after initialization validates the registry. |
|
| Field-scoped upload hooks. Array indexes are omitted; a block type segment follows a blocks field. |
export const serverHooks: ServerHooksConfig = { collections: { docs: () => import('./docs/hooks.js'), }, uploads: { 'media.image': () => import('./media/upload-hooks.js'), },}Use this registry when the hook module imports Node-only packages, secrets, storage SDKs, or server clients. A loader attached directly to an isomorphic schema remains reachable from the browser graph.
initBylineCore(config, pinoLogger?)
async function initBylineCore<TAdminStore = unknown>( config: ServerConfig<TAdminStore>, pinoLogger?: pino.Logger): Promise<BylineCore<TAdminStore>>This is the recommended server entry point. It:
- resolves and validates configuration;
- validates rich-text, search, translations, tree and scheduler capabilities, recurring tasks, and collection definitions;
- composes the configured services and logger;
- reconciles collection records and schema versions;
- ensures counter sequences and backfills legacy source locales where supported;
- registers collection abilities; and
- commits the server config, logger, and core singletons only after initialization succeeds.
defineServerConfig(config)
function defineServerConfig<TAdminStore = unknown>( config: ServerConfig<TAdminStore>): ResolvedServerConfig<TAdminStore>Validates, resolves routes, attaches configured hooks, and registers only the server-config singleton. It does not compose a BylineCore, reconcile collections, or register the logger and abilities. Application bootstraps should normally use initBylineCore().
getServerConfig()
function getServerConfig(): ResolvedServerConfigReturns the registered server config. It throws in a browser or before server configuration has completed.
BylineCore
initBylineCore() returns and globally registers the composed runtime.
Property or method | Type | Description |
|
| Resolved server configuration. |
|
| Canonical configured collection tuple. |
|
| Configured database adapter. |
|
| Installation-wide storage provider. |
|
| Structured runtime logger. |
|
| Boot-reconciled collection IDs, versions, and fingerprints by path. |
|
| Throwing cached lookup for one registered collection. |
|
| Collection and application ability registry. |
|
| Adds an application or plugin ability. |
|
| Returns every registered ability. |
|
| Groups registered abilities for admin presentation. |
|
| Configured session provider. |
|
| Configured admin-store aggregate. |
|
| Frozen, validated recurring-task definitions. Empty when none are registered; storing them on core does not start a scheduler. |
getBylineCore<TAdminStore>()
function getBylineCore<TAdminStore = unknown>(): BylineCore<TAdminStore>Returns the core registered by initBylineCore(). It throws in the browser or before initialization completes.
Configuration lookup helpers
Function | Return | Behavior |
|
| Reads the current server config, or admin config when no server config exists, and finds a schema by path. |
|
| Finds one browser-safe collection admin config by slug. Returns |
|
| Returns a sorted copy using configured content-locale order, with unknown codes alphabetized at the end. It never filters codes. |
Server client getters
The @byline/client/server subpath is server-only. Its browser export condition throws.
Function | Request authority | Use |
| Anonymous published reader | Feeds, sitemaps, third-party endpoints, and public reads where preview must never apply. |
| Anonymous by default; admin actor when a preview cookie and valid admin session both resolve | Public pages that support editorial preview. The call still needs |
| Authenticated admin actor resolved from the current request | Admin server functions and request-bound admin reads/writes. |
| Stable super-admin system actor | Seeds, migrations, maintenance jobs, and background lifecycle work outside an HTTP request. |
| — | Resolves |
import { getViewerBylineClient, isPreviewActive } from '@byline/client/server'
const preview = await isPreviewActive()const page = await getViewerBylineClient().collection('pages').findByPath('about', { status: preview ? 'any' : 'published',})All getters cache the client instance but resolve request authority per operation. Generated Register augmentation types every getter with the application's collection paths and field shapes.