# Configuration
Notion CMS ships with sensible defaults but can be tailored via `NotionCMSConfig` and the type generation CLI. This guide explains how to customise file handling, debug logging, and generate database metadata.
## Configuration overview
```
typescript
import { NotionCMS } from "@mikemajara/notion-cms"
const notion = new NotionCMS(process.env.NOTION_TOKEN!, {
files: {
strategy: "local",
storage: {
path: "./public/notion-files"
}
},
debug: {
enabled: true,
level: "info"
}
})
```
`mergeConfig` combines user input with default values:
- `files.strategy` defaults to `"direct"`; other options are `"local"` and `"remote"`.
- `files.storage` contains shared fields (path, endpoint, credentials) used by the strategies.
- `debug` toggles scoped logging (see `debugging-and-troubleshooting.md`).
## File strategies
| Strategy | Behaviour | When to use |
| --- | --- | --- |
| `direct` (default) | Returns Notion-hosted URLs untouched. | Minimal setup, no caching needed. |
| `local` | Downloads assets once, stores them using `LocalStorage`, serves stable public URLs. | Static site exports, longer-lived URLs. |
| `remote` | Uploads files to S3-compatible storage via `S3Storage`. | CDN-backed delivery, multi-region setups. |
### Local strategy
- Requires a writable `storage.path` (defaults to `./public/assets/notion-files`).
- Uses `generateFileId()` to keep filenames stable between runs.
- Falls back to the original Notion URL if download fails.
### Remote strategy
```
typescript
const notion = new NotionCMS(token, {
files: {
strategy: "remote",
storage: {
endpoint: process.env.S3_ENDPOINT!,
bucket: "notion-cms",
accessKey: process.env.S3_ACCESS_KEY!,
secretKey: process.env.S3_SECRET_KEY!,
region: "us-east-1",
path: "uploads/notion/"
}
}
})
```
- `endpoint`, `accessKey`, and `secretKey` are required.
- `bucket` defaults to `"default-bucket"`, but you should override it.
- Optional `path` prefixes all stored keys (e.g., `uploads/notion/page-id.png`).
- Errors are logged via `debug.error`; the original URL remains available.
### Checking cache state
```
typescript
const cached = notion["databaseService"] // internal
console.log(notion instanceof Object) // placeholder to discourage direct access
```
Public API:
```
typescript
import { FileManager } from "@mikemajara/notion-cms"
// Via dependency injection in converters
const isCacheEnabled = fileManager.isCacheEnabled()
```
## Debug logging
`debug.configure` is called in the `NotionCMS` constructor. Settings:
- `enabled`: toggles logging entirely.
- `level`: `"error" | "warn" | "info" | "debug"`. Higher verbosity → more logs.
Logs include scoped helpers (`debug.query`, `debug.log`, `debug.error`) used across services.
## Type generation CLI
Generating database metadata improves DX and runtime validation.
1. Ensure the generator script is installed (comes with the package).
2. Create a script in your app:
```
json
{
"scripts": {
"notion:generate": "ts-node packages/notion-cms/src/generator.ts"
}
}
```
1. Run the generator with required env vars (database IDs, output path). Each project typically maintains a `notion-types-*.ts` file checked into source control.
2. Import the generated file early in your app (e.g., `_app.tsx`, entry server file) so `registerDatabase` runs before queries.
Generated files export types and call `registerDatabase("databaseKey", { id, fields })`. Fields are mapped to `DatabaseFieldMetadata`, powering autocomplete:
```
typescript
registerDatabase("blog", {
id: "xxxxxxxx",
fields: {
Title: { type: "title" },
Tags: { type: "multi_select", options: ["Docs", "Product"] },
PublishedAt: { type: "date" }
}
})
```
If you cannot run the generator (e.g., during prototyping), you can manually call `registerDatabase` with the same shape. Minimal metadata still unlocks QueryBuilder validation.