@structcms/api
v0.1.2
Published
Storage, domain API, and delivery API for StructCMS
Maintainers
Readme
@structcms/api
Storage, domain API, delivery API, and content export for StructCMS. Provides Supabase-agnostic adapter interfaces, handler functions for content CRUD, media management, and JSON export.
For architectural context, see ARCHITECTURE.md (Layer 3: Storage, Layer 4: Domain API, Layer 5: Delivery API).
Installation
npm install @structcms/apiQuick Start
import { createStorageAdapter, createMediaAdapter } from '@structcms/api';
import { createClient } from '@supabase/supabase-js';
import { createNextPagesRoute } from '@structcms/api/next';
// 1. Create Supabase client
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SECRET_KEY!
);
// 2. Create adapters
const storageAdapter = createStorageAdapter({ client: supabase });
const mediaAdapter = createMediaAdapter({ client: supabase });
// 3. Create Next.js API route handlers
export const { GET, POST } = createNextPagesRoute({
storageAdapter,
mediaAdapter
});See examples/test-app/app/api/cms/ for complete route handler implementations.
File Structure
packages/api/
├── src/
│ ├── index.ts # Public exports
│ ├── storage/
│ │ ├── types.ts # StorageAdapter interface, Page, Navigation types
│ │ ├── supabase-adapter.ts # Supabase implementation of StorageAdapter
│ │ ├── handlers.ts # Page/Navigation CRUD handlers
│ │ └── *.test.ts
│ ├── delivery/
│ │ ├── types.ts # PageResponse, NavigationResponse types
│ │ ├── handlers.ts # Delivery handlers (list, get by slug)
│ │ └── *.test.ts
│ ├── media/
│ │ ├── types.ts # MediaAdapter interface, MediaFile types
│ │ ├── supabase-adapter.ts # Supabase implementation of MediaAdapter
│ │ ├── handlers.ts # Upload, list, delete handlers
│ │ ├── resolve.ts # Media reference resolution
│ │ └── *.test.ts
│ ├── export/
│ │ ├── types.ts # Export response types
│ │ ├── handlers.ts # Export handlers (page, site, navigation)
│ │ └── *.test.ts
│ ├── utils/
│ │ ├── slug.ts # Slug generation and uniqueness
│ │ └── sanitize.ts # HTML sanitization (XSS protection)
│ └── types/
│ └── database.types.ts # Generated Supabase types
├── package.json
├── tsconfig.json
└── tsup.config.tsKey Concepts
Handler Functions
This package exports handler functions, not complete route handlers. This keeps the package framework-agnostic and allows adapter injection. Host projects create thin route handlers that inject their adapters.
Adapter Interfaces
StorageAdapter— Interface for page and navigation persistenceMediaAdapter— Interface for media file operations
Both have Supabase implementations but the interfaces are Supabase-agnostic for future portability.
HTML Sanitization
Rich text content is sanitized on write using sanitize-html to prevent XSS attacks.
API Reference
Adapter Factories
createStorageAdapter({ client })— Create a Supabase storage adaptercreateMediaAdapter({ client, bucketName? })— Create a Supabase media adapter
Storage Handlers
handleCreatePage(adapter, input)— Create a page with auto-generated slughandleUpdatePage(adapter, input)— Update an existing pagehandleDeletePage(adapter, id)— Delete a page by IDhandleCreateNavigation(adapter, input)— Create a navigationhandleUpdateNavigation(adapter, input)— Update a navigationhandleDeleteNavigation(adapter, id)— Delete a navigation
Delivery Handlers
handleListPages(adapter, options?)— List pages with optional filteringhandleGetPageBySlug(adapter, slug)— Get a single page by slughandleGetNavigation(adapter, name)— Get a navigation by name
Media Handlers
handleUploadMedia(adapter, input)— Upload a media file (validates MIME type)handleGetMedia(adapter, id)— Get media by IDhandleListMedia(adapter, filter?)— List media fileshandleDeleteMedia(adapter, id)— Delete a media fileresolveMediaReferences(adapter, sections)— Resolve media IDs to URLs in section data
Allowed MIME types: image/jpeg, image/png, image/gif, image/webp, image/svg+xml
Export Handlers
handleExportPage(adapter, mediaAdapter, slug)— Export a single page with resolved mediahandleExportAllPages(adapter, mediaAdapter)— Export all pageshandleExportNavigations(adapter)— Export all navigationshandleExportSite(adapter, mediaAdapter)— Full site export (pages + navigations + media)
Next.js Preset Factories
createNextPagesRoute({ storageAdapter, mediaAdapter })— List and create pagescreateNextPageBySlugRoute({ storageAdapter, mediaAdapter })— Get page by slugcreateNextPageByIdRoute({ storageAdapter })— Update/delete page by IDcreateNextMediaRoute({ mediaAdapter })— List and upload mediacreateNextMediaByIdRoute({ mediaAdapter })— Delete media by IDcreateNextNavigationRoute({ storageAdapter })— List and create navigationcreateNextNavigationByIdRoute({ storageAdapter })— Update/delete navigation by ID
Error Classes
StorageError— Thrown by storage adapter on database errorsStorageValidationError— Thrown on validation failures (empty title, duplicate slug). Hascodeproperty.MediaError— Thrown by media adapter on storage/database errorsMediaValidationError— Thrown on validation failures (invalid MIME type). Hascodeproperty.
Constants
ALLOWED_MIME_TYPES— Readonly array of allowed MIME types
Utilities
generateSlug(title)— Generate a URL-safe slug from a titleensureUniqueSlug(slug, existingSlugs)— Ensure slug uniqueness by appending a suffix if needed
Dependencies
| Package | Purpose |
|---------|---------|
| @supabase/supabase-js | Database and storage client |
| sanitize-html | Server-side HTML sanitization for XSS protection |
Database
Migrations live in supabase/migrations/ at the monorepo root. See ARCHITECTURE.md for the database schema.
Development
# Run tests (watch mode)
pnpm test
# Run tests once
pnpm test run
# Build
pnpm build
# Type check
pnpm typecheck
# Regenerate Supabase types
pnpm gen:types