1. Choose a schema and publication policy
Use the database keys and property names above to reproduce the example. These
are application choices, not fixed NotCMS behavior.
list() returns all synced
pages; filtering happens on the server in this example. This is presentation
filtering, not an API access-control mechanism: credentials can read drafts, so
keep them on the server and keep private material out of any publicly exposed DB.
Dates with a time use their timezone; date-only values use UTC midnight here.
For a local publication timezone, adapt the date comparison explicitly.
languageuses the exact valuesjaanden; null/empty belongs to Japanese. Unsupported language values are excluded. An absent translation produces an empty list or 404, without silently showing Japanese on the English route.- Missing/invalid dates and future dates are excluded. Blogs also need the checkbox. Releases do not require a version or product value.
- Lists sort by publication date descending; IDs break ties. The input is not mutated. The same policy is checked for detail access, including a second check after the latest detail properties arrive.
- URLs are
/{locale}/blog/{encoded ID}or/{locale}/releases/{encoded ID}. There is no slug requirement or version-based key: missing versions and the same version in different products stay distinct. If your app chooses slugs, define uniqueness, fallback, and redirect policy first; SDKget()accepts a page ID, not a slug. Do not put unvalidated slugs directly into a path.
2. Generate types and copy the tested reader
Connect both databases in Dashboard and complete a sync. Then run in your Next.js App Router project:src/notcms/schema.ts, including schema and nc.
Copy content.ts to src/notcms/content.ts next to
that generated file. Its types derive from schema.blog.properties and
schema.releases.properties; update them if your keys differ. See the
sample schema for reference, but do not copy its fake IDs
into a live app. Run your application’s typecheck after each schema pull.
Wrap the reader on the server. Keep NOTCMS_SECRET_KEY and NOTCMS_WORKSPACE_ID
in .env.local and your deployment secrets, never NEXT_PUBLIC_* or client code.
The framework-independent reader is covered by SDK tests using fake responses.
3. List and generate URLs (Next.js 16 App Router)
These routes assume the standard@/* alias to src/* and the default cache
configuration. Awaiting connection() makes the page depend on a real request.
The example labels are placeholders: use your application’s translation messages.
content.listBlog(locale) and contentUrl("blog", locale, post.id)
in app/[locale]/blog/page.tsx. Empty results are a successful query with no
published records in that language. Do not use posts ?? [] on failed requests.
4. Fetch detail with the same publication rules
content.getBlog(id, locale) in
app/[locale]/blog/[id]/page.tsx. Params are already decoded by Next.js;
do not decode them a second time. The example shows Markdown as escaped plain
text. Add your chosen Markdown renderer and sanitize any HTML before display.
Failure, caching and policy changes
The reader returns null only after a successful query establishes that the page is absent or excluded by the policy. Network, 401/403, 429 and upstream 5xx errors are thrown; an error during detail retrieval also propagates, even if the list succeeded. Next.js can display these through anerror.tsx boundary instead
of recording a successful empty list or 404. Do not catch these errors and return
[] or call notFound().
This recipe uses request-time rendering without an application cache. The API
can still have a short cache window. If adding ISR or build-time generation,
fail the build/revalidation on CMS errors, define how long stale published data
may remain visible, and schedule updates for future publication dates. Static
sites need a rebuild after new publication times. Invalidate list and detail
caches together when withdrawing or changing language. Display filtering alone
cannot guarantee immediate removal from already-built or cached public pages.
Change selectBlogPosts, selectReleases, matchesLanguage, and contentUrl
for your own schema and policy. A version/product field is display data in this
recipe, never the route identity. To preview drafts, build a separate authenticated
server route rather than relaxing public list/detail checks.
References checked on 2026-10-11: dynamic route params,
connection,
error handling.