@mhbdev/unicms
v0.2.0
Published
Typed SDK for UniCMS public API
Maintainers
Readme
@mhbdev/unicms
Typed SDK for the UniCMS public API (/api/public/v1/*).
Install
pnpm add @mhbdev/unicmsCreate client
import { createUniCmsClient } from '@mhbdev/unicms'
const cms = createUniCmsClient({
baseUrl: 'https://cms.example.com',
tenant: { slug: 'acme' },
})Use domain-based tenant scope instead:
const cms = createUniCmsClient({
baseUrl: 'https://cms.example.com',
tenant: { domain: 'acme.example.com' },
})Optional auth headers (private tenants)
const cms = createUniCmsClient({
baseUrl: 'https://cms.example.com',
tenant: { slug: 'acme' },
headers: async () => ({
Authorization: `Bearer ${await getToken()}`,
}),
})API
await cms.getSiteSettings()
await cms.getPageBySlug('home')
await cms.getPageBySlug('pricing', { includeRawContent: true })
await cms.listPosts({ page: 1, limit: 10, q: 'cms', categorySlug: 'news', tagSlug: 'payload' })
await cms.getPostBySlug('hello-world')
await cms.getPostBySlug('hello-world', { includeRawContent: true })
await cms.listFaqs()
await cms.listFaqs({ includeRawContent: true })
await cms.listCategories()
await cms.listTags()
await cms.externalUsersLogin({
username: 'tenant-admin',
password: 'secret',
})Generic typed requests
Use request for advanced/custom endpoints with schema-validated responses:
import { z } from 'zod'
const response = await cms.request({
path: '/api/public/v1/posts',
query: { page: 1, limit: 5 },
schema: z.object({
data: z.object({
docs: z.array(z.object({ id: z.number(), slug: z.string() })),
}),
}),
})React / Next.js integration
Install React peer deps if needed:
pnpm add react react-domUse the React entrypoint:
'use client'
import { createUniCmsClient } from '@mhbdev/unicms'
import { UniCmsProvider, usePageBySlugQuery, useSiteSettingsQuery } from '@mhbdev/unicms/react'
const client = createUniCmsClient({
baseUrl: 'https://cms.example.com',
tenant: { slug: 'acme' },
})
function HomePage() {
const siteSettings = useSiteSettingsQuery()
const page = usePageBySlugQuery({ slug: 'home' })
if (siteSettings.isLoading || page.isLoading) return <p>Loading...</p>
if (siteSettings.error || page.error) return <p>Failed to load</p>
return <h1>{page.data?.title}</h1>
}
export default function App() {
return (
<UniCmsProvider
client={client}
defaultQueryOptions={{ staleTimeMs: 30_000 }}
namespace="acme-frontend"
>
<HomePage />
</UniCmsProvider>
)
}Available hooks:
useUniCmsClientuseUniCmsQueryuseUniCmsMutationuseUniCmsInvalidateuseSiteSettingsQueryusePageBySlugQueryusePostsQueryusePostBySlugQueryuseFaqsQueryuseCategoriesQueryuseTagsQueryuseExternalUsersLoginMutation
Next App Router SSR helpers
Use @mhbdev/unicms/next inside Server Components, Route Handlers, and Server Actions:
import { createNextAppRouterUniCmsClient } from '@mhbdev/unicms/next'
const cms = await createNextAppRouterUniCmsClient({
tenant: { slug: 'acme' },
// Optional. If omitted, it tries:
// UNICMS_BASE_URL -> NEXT_PUBLIC_SERVER_URL -> PAYLOAD_PUBLIC_SERVER_URL -> NEXT_PUBLIC_SITE_URL
baseUrl: 'https://cms.example.com',
})Server Component example
import { createNextAppRouterUniCmsClient } from '@mhbdev/unicms/next'
export default async function Page() {
const cms = await createNextAppRouterUniCmsClient({
tenant: { slug: 'acme' },
})
const settings = await cms.getSiteSettings()
return <h1>{settings.siteName}</h1>
}Route Handler example (request-scoped forwarding)
import { createNextAppRouterUniCmsClient } from '@mhbdev/unicms/next'
export async function GET(request: Request) {
const cms = await createNextAppRouterUniCmsClient({
request, // forwards selected request headers + cookie header by default
tenant: { domain: 'acme.example.com' },
})
const posts = await cms.listPosts({ page: 1, limit: 10 })
return Response.json(posts)
}Custom forwarding
const cms = await createNextAppRouterUniCmsClient({
tenant: { slug: 'acme' },
includeCookies: true,
requestHeaders: ['authorization', 'x-request-id'],
})Error handling
SDK methods throw UniCmsError:
import { UniCmsError } from '@mhbdev/unicms'
try {
await cms.getPageBySlug('missing-page')
} catch (error) {
if (error instanceof UniCmsError) {
console.error(error.code, error.status, error.message, error.details)
}
}Error codes:
BAD_REQUESTUNAUTHORIZEDFORBIDDENNOT_FOUNDVALIDATION_ERRORNETWORK_ERRORPARSE_ERRORUNKNOWN
Pagination
listPosts returns:
type Paginated<T> = {
docs: T[]
pagination: {
page: number
limit: number
totalDocs: number
totalPages: number
hasNextPage: boolean
hasPrevPage: boolean
nextPage: number | null
prevPage: number | null
}
}Defaults: page=1, limit=10, max limit=50.
Compatibility
- SDK targets UniCMS public API v1.
- Endpoints are expected at
/api/public/v1/*. - v1 returns published content only.
Release notes
0.1.x: Initial v1 contract-first release.
