Reference › Config
Docs/Reference/Config

Config reference

Every option of easy-cms.config.ts, grouped by where it goes. For explanations and examples, follow the links to the guide. A test checks this page against the types in @easy-cms/core, so every option is listed here.

import { defineConfig } from '@easy-cms/core'
 
export default defineConfig({ secret, db, collections, /* … */ })

Top level

OptionTypeDefault
secretstring—Required, 32+ characters. Signs sessions and preview tokens.
dbDatabaseAdapter—Required. sqlite() or postgres(). Databases
serverURLstring—Public origin (https://example.com); makes media URLs absolute.
corsstring[] | '*'[]Origins whose browser code may call the API. Security
cronSecretstringCRON_SECRETBearer secret for GET <api>/jobs/run. Scheduling
webhooksWebhookConfig[][]See webhooks.
eventsstring[][]Events of the app or its plugins besides content changes (<area>.<what>, e.g. order.paid), sent with cms.emit() to webhooks that list them. Webhooks
jobsJobConfig[][]Work run with the scheduled jobs, e.g. a plugin's clean-up. See jobs.
localizationLocalizationConfig—See localization.
routesRoutesConfigSee routes.
adminAdminConfigSee admin.
uploadUploadConfigSee upload.
authAuthConfigSee auth.
collectionsCollectionConfig[][]See collections.
globalsGlobalConfig[][]See globals.
endpointsEndpoint[][]See endpoints.
commandsCliCommand[][]easy-cms <name> commands, e.g. from plugins: { name, description, help?, run({ cms, args, log }) }. CLI
onRequest({ headers, url, user, cms }) => { context?, user? }—Runs on each API request once its user is known: returns the request's context (e.g. its tenant) and may change the user (their role there, scoped). Usually set by a plugin. Multi-tenant
apiKeysbooleanfalseAPI keys under Settings, for scripts and other apps. API keys
emailEmailAdapter—Sends email for plugins, e.g. smtp() or consoleEmail(). Email
backupsBackupsConfigby hand onlyDatabase backups on a schedule. See backups.
auditboolean | AuditConfigoffThe audit log: who changed what, sign-ins, admin actions. See audit.
pluginsPlugin[][](config) => config, run in order before validation; definePlugin(fn, { name, version }) names one for the dashboard. Plugins
fieldTypesFieldTypeDefinition[][]Field types from packages, e.g. color from @easy-cms/fields. Custom field types

routes

OptionTypeDefault
apistring/api/cmsWhere the REST API is served.

admin

OptionTypeDefault
pathstring/adminWhere the admin is served.
locale'en' | 'th'enDefault admin language before a user picks one.
brandAdminBrand{}See brand.
siteUrlstring/ (Nuxt, Next.js)The public site for "View site": a path or an http(s) URL.
menustring[]config orderCollection slugs in menu order within their groups; unlisted ones follow.
navNavGroup[]—The menu's groups: { id, label, icon?, order?, children? }, children one level deep. Built in: content, settings (site, people, system). The admin
commands{ label, to, icon?, keywords? }[]—More entries of the command palette (⌘K) that open a page of the admin.
modulesstring[][]Admin modules with Web Components: package exports or paths. Admin components
pagesAdminPage[][]Pages of their own at <admin>/p/<path>, e.g. from plugins. See pages.
dashboardDashboardWidget[][]Panels on the dashboard after the built-in ones. See dashboard.
switcher{ cookie, label, options }—A choice at the top of the menu for the whole admin, e.g. the tenant, kept in a cookie. options: a path under the API returning { options: [{ value, label }], all? }. Usually set by a plugin.

brand

OptionType
namestringShown in the menu, login page and browser tab. Default "Easy CMS".
logostringA path on your site or an https:// URL.
colorstringMain color as #rrggbb; shades are derived.

pages

OptionType
pathstringRequired. Lowercase letters, digits and -, unique: the page is at <admin>/p/<path>.
componentAdminComponentRequired. The page's body; the admin draws the header.
labelstring | { en, th }Required. Title in the header, the menu and the browser tab.
iconAdminIconMenu icon. Default file-text.
group'content' | 'settings' | false | stringIts menu group: content, settings, a group's id (admin.nav) or a label; false: not listed. Default content.
access({ user }) => booleanWho may open it, checked on the server. Default: every logged-in user.

dashboard

OptionType
componentAdminComponentRequired. The panel's content.
width'half' | 'full'half (default): the side column; full: below both columns. One column on phones.
labelLabelIts name in Settings → Roles (auth.rbac). Default: the component's tag.
access({ user }) => booleanWho sees it, checked on the server. Default: every logged-in user.

Pages and dashboard panels shows how to write them.

backups

OptionTypeDefault
every'day' | 'week'—Back up automatically. Without it, admins back up by hand.
atstring03:00When, as HH:MM in the server's time zone.
keepnumber7Finished backups to keep; older ones are deleted.
dirstringbackupsFolder for the default local storage. Never served publicly.
storageStorageAdapterlocal diskE.g. s3Storage() with a private bucket.
sqlite(options) => DatabaseAdapter—Postgres only: sqlite from @easy-cms/db-sqlite, which writes the backup file. Backups

audit

OptionTypeDefault
keepnumber365Days to keep entries; older ones are deleted. 0: keep them all.
valuesbooleantrueKeep values before and after a change; false: only which fields changed.
failedLoginsnumber20Failed sign-ins within an hour that the dashboard warns about. Audit log
scope(context) => string | null—The part of the site an entry belongs to, e.g. its tenant (usually set by a plugin). Admins of a part (scoped) see its entries; others the chosen part's, or all.

auth

OptionTypeDefault
rolesstring[]['admin', 'editor']Must include admin. With rbac, the roles that always exist. Users & auth
rbacbooleanfalseRoles and their permissions from the admin (Settings → Roles), on top of access rules. Roles
providersAuthProvider[][]Signing in to the admin with outside accounts, e.g. [google()] from @easy-cms/auth-oauth. Single sign-on
allowSignUp{ domains, role? }—With providers: people from these email domains get an account on their first sign-in, with role (not admin).
passwordbooleantruefalse: only admins sign in with a password; everyone else uses providers.
setupCodestringEASY_CMS_SETUP_CODEThe code the first admin must enter on /admin, so nobody else claims a fresh site. Without one, no code is asked. One-click deploy
tokenExpirationnumber604800 (7 days)Session lifetime in seconds.
maxLoginAttemptsnumber5Failed logins allowed per email (and IP) within lockWindow.
lockWindownumber900 (15 minutes)In seconds.
trustedOriginsstring[][]Other origins that may send cookie-authenticated requests.
resetPasswordExpirationnumber3600Seconds a "forgot password" link works. Links need email, and serverURL in production. Forgotten passwords
inviteExpirationnumber604800Seconds an invitation link works.
emails{ resetPassword?, invite?, passwordChanged? }—Functions ({ user, url, locale, expiresAt }) => { subject, text, html? } for your own email text.
membersMembersConfig—People who sign in on the site, not the admin, e.g. customers. See members.

members

OptionType
rolesstring[]Their roles, also in roles. Members never get into the admin, and isLoggedIn (every collection's default) doesn't count them. Site members
signupMembersSignupVisitors create their own account: POST <api>/users/signup.
pages{ verifyEmail?, resetPassword? }The site's pages that open members' email links with ?token=: paths on admin.siteUrl (else serverURL) or URLs. Default: the admin's pages.
emails{ verifyEmail? }Your own text for the email that confirms an address.

signup

OptionTypeDefault
rolestring—The role new accounts get: one of members.roles.
verifyEmailbooleantrueNew accounts confirm their email with a link before signing in.
turnstile{ siteKey, secretKey }—Cloudflare Turnstile, checked on every sign-up.

upload

OptionTypeDefault
dirstringuploadsFolder for the default local storage, from the project root.
maxFileSizenumber10485760 (10 MB)In bytes.
mimeTypesstring[]['image/*', 'application/pdf']Allowed types, detected from file contents: MIME types, type/*, or documents, office, archives. See File types.
storageStorageAdapterlocal diskE.g. s3Storage() from @easy-cms/storage-s3. Uploads
imageSizesImageSize[][]Resized copies (needs sharp). See image sizes.
fromURLUploadFromURLConfigoffUploads from links. See fromURL.
privateStorageStorageAdapterstorage without public URLsWhere files in private folders go. See Private folders.
foldersbooleanfalseFolders in the media library, and with auth.rbac who can use each. See Folders.

fromURL

OptionType
allowedHostsstring[]Required. images.example.com, *.example.com (subdomains) or * (any public host).
allowPrivatebooleanAlso private network addresses (localhost, 10.x…). Default false. Read first

Image sizes

OptionTypeDefault
namestring—Key in media.sizes, e.g. thumbnail.
widthnumber—Width in pixels.
heightnumber—Optional height.
fit'cover' | 'contain' | 'inside'coverHow to fit when both width and height are set.

localization

OptionTypeDefault
localesstring[]—Content locales, e.g. ['th', 'en']. Localization
defaultLocalestringthe first localeUsed when no locale is given; its values fill empty ones.
fallbackbooleantrueReads return the default locale's value when a value is empty.

webhooks

OptionTypeDefault
urlstring—Where to POST events. Webhooks
eventsstring[]allcreate, update, delete, publish, unpublish, draft.
collectionsstring[]allCollections to send events for; [] for none.
globalsstring[]allGlobals to send events for; [] for none.
secretstring—Signs bodies: x-easy-cms-signature: sha256=<hex>.
headersRecord<string, string>—Extra request headers.

events also takes the config's own events (e.g. order.paid): those are sent only to webhooks that list them.

jobs

OptionType
namestringUnique, lowercase with - and :, e.g. shop:carts.
everynumberAt most this often, in seconds; when each last ran is kept in the database. Default: every run.
run({ cms, now }) => unknownThe work. A failure is logged; the other jobs still run.

They run with the scheduled jobs: every minute in a server, or each time a cron calls GET <api>/jobs/run.

collections

OptionTypeDefault
slugstring—Required. URL and table name: lowercase letters, digits, -, _.
fieldsField[]—Required. See the field reference.
labels{ singular?, plural? }from the slugEach a string or { en, th }.
iconAdminIconfile-textMenu icon. Branding
useAsTitlestring—Top-level field shown as the document title.
editIn'page' | 'drawer'pagedrawer edits in a panel over the list (without drafts, versions or preview).
draftsbooleanfalseAdds status (draft | published). Drafts
versionsboolean | VersionsConfigfalseKeep a version of every save. See versions.
schedulebooleanfalsePublish and unpublish at a set time (needs drafts).
preview({ doc, locale }) => string | null—The page showing a document, for live preview.
access{ read?, create?, update?, delete? }logged inAccess control
hooksCollectionHooks—See hooks.
adminCollectionAdmin—See admin components.

versions

OptionTypeDefault
maxnumber50Versions kept per document; older ones are deleted.

hooks

Each is a list of functions. Hooks

HookArgumentsReturn
beforeValidatedata, operation, originalDoc?new data, or nothing
beforeChangedata, operation, originalDoc?new data, or nothing
afterChangedoc, operation, previousDoc?—
beforeDeleteid—
afterDeleteid, doc—
afterReaddocnew doc, or nothing

Every hook also gets user, cms and slug.

Admin components

OptionType
sidebarSidebarPanel[]Panels in the edit page's side column; { tag, props, position: 'top' } puts one above the rest. Admin components
groupstring | LabelIts menu group: a group's id (admin.nav, e.g. shop.catalog), settings (Settings › Site), or a label that makes a group of that name. Default: Content. The admin
layoutLayoutNode[]Tabs, sections that fold and rows of the edit page, by field name. The admin
badge{ where, tone?, label? }A number beside its menu item: the documents matching where that the user may read, e.g. orders to send.
countbooleanThe number of documents beside its menu item. Default true.
empty{ description?, link? }What the list says before it has documents, and a link ({ label, href }).
list{ tree?, sort? }The list page: tree names a relationship to the same collection to show a tree (top-level documents first, children open below); sort is the default order, e.g. 'title'.
ownerFieldstringWith auth.rbac: a relationship field to users naming the owner, e.g. 'author', for roles given "own documents only". Default: who created it (createdBy). Roles
confirmDelete{ typeTitle?, impact? }Deleting asks more: typeTitle has the user type the title; impact is a path under the API, called with ?id=, returning { message } about what goes too. No deleting from a selection.

globals

OptionTypeDefault
slugstring—Required.
fieldsField[]—Required.
labelstring | { en, th }from the slug
iconAdminIconsettingsMenu icon.
draftsbooleanfalse
versionsboolean | VersionsConfigfalse
schedulebooleanfalseNeeds drafts.
preview({ doc, locale }) => string | null—
access{ read?, update? }logged in
scope({ context, user }) => string | null | undefined—One value per scope, e.g. per tenant: a string keeps a value of its own, undefined the shared one, null none (reads give it empty, changes are refused).
hooksGlobalHooks—See below.
adminContainerAdmin—{ sidebar }, as for collections.

Global hooks

HookArgumentsReturn
beforeChangedata, operation, originalDoc?new data, or nothing
afterChangedoc, operation, previousDoc?—
afterReaddocnew doc, or nothing

endpoints

OptionType
pathstringUnder routes.api, e.g. /seo/generate or /stats/:collection.
method'get' | 'post' | 'put' | 'patch' | 'delete'
handler(request: EndpointRequest) => unknownReturns a Response, or a value sent as JSON. Endpoints
rootbooleanServe the path from the site's root (standalone server only), e.g. /robots.txt. Default false.

EndpointRequest

PropertyType
requestRequestThe Web request.
urlURLIts URL.
paramsRecord<string, string>Values of :name segments.
userAuthUser | nullThe logged-in user.
contextRequestContextThe request's context (onRequest); pass it to the Local API with user.
ipstring | undefinedThe client's IP address, when the adapter knows it.
cmsEasyCMSThe Local API.
json() => Promise<object>The JSON body (an object, at most 1 MB).