@flightdev/cms
v0.2.1
Published
Unified CMS adapters for Flight Framework - use any headless CMS
Maintainers
Readme
@flightdev/cms
Unified CMS adapters for Flight Framework. One API for Strapi, Contentful, Sanity, and more.
Philosophy
Flight doesn't impose - you choose your CMS. All adapters are optional, swap providers without changing your code.
Features
- Adapter pattern - Same API for any CMS
- Zero lock-in - Switch CMS providers without code changes
- React hooks - useCMSQuery, useCMSOne, mutations
- Vue composables - Reactive queries with auto-refetch
- Full CRUD - Read, create, update, delete
- i18n support - Locale-aware queries
- Preview mode - Draft content for editors
Installation
npm install @flightdev/cmsQuick Start
import { createCMS } from '@flightdev/cms';
import { strapi } from '@flightdev/cms/strapi';
const cms = createCMS(strapi({
url: process.env.STRAPI_URL,
token: process.env.STRAPI_TOKEN,
}));
// Query posts
const { data: posts, meta } = await cms.findMany('posts', {
limit: 10,
sort: { publishedAt: 'desc' },
populate: ['author', 'cover'],
});
// Get single post
const post = await cms.findOne('posts', {
where: { slug: 'hello-world' },
});Adapters
Strapi
import { strapi } from '@flightdev/cms/strapi';
const adapter = strapi({
url: 'http://localhost:1337',
token: 'your-api-token',
preview: true, // Enable draft mode
});Contentful
import { contentful } from '@flightdev/cms/contentful';
const adapter = contentful({
spaceId: 'your-space-id',
accessToken: 'your-access-token',
environment: 'master',
preview: true,
previewToken: 'preview-token',
});Sanity
import { sanity } from '@flightdev/cms/sanity';
const adapter = sanity({
projectId: 'your-project-id',
dataset: 'production',
token: 'your-token', // Optional for public datasets
useCdn: true,
});React Integration
import { CMSProvider, useCMSQuery, useCMSOne } from '@flightdev/cms/react';
// App
function App() {
return (
<CMSProvider cms={cms}>
<PostList />
</CMSProvider>
);
}
// Query many
function PostList() {
const { data: posts, loading, meta, refetch } = useCMSQuery('posts', {
limit: 10,
populate: ['author'],
});
if (loading) return <Skeleton />;
return (
<>
{posts.map(post => <PostCard key={post.id} post={post} />)}
<p>Total: {meta?.total}</p>
</>
);
}
// Query one
function PostPage({ slug }) {
const { data: post, loading, error } = useCMSOne('posts', {
where: { slug },
});
if (loading) return <Skeleton />;
if (!post) return <NotFound />;
return <Post post={post} />;
}Vue Integration
<script setup>
import { provideCMS, useCMSQuery } from '@flightdev/cms/vue';
// Provide CMS in root component
provideCMS(cms);
// Query posts
const { data: posts, loading, meta } = useCMSQuery('posts', {
limit: 10,
sort: { publishedAt: 'desc' },
});
</script>
<template>
<div v-if="loading">Loading...</div>
<PostGrid v-else :posts="posts" />
</template>API Reference
Query Options
interface FindManyOptions {
where?: Record<string, unknown>; // Filter conditions
populate?: string[]; // Relations to include
limit?: number; // Max results
offset?: number; // Skip results
page?: number; // Page number
pageSize?: number; // Items per page
sort?: Record<string, 'asc' | 'desc'>; // Sort order
fields?: string[]; // Select fields
locale?: string; // Content locale
preview?: boolean; // Draft mode
}CMS Methods
| Method | Description |
|--------|-------------|
| findOne(collection, options) | Get single entity |
| findMany(collection, options) | Get multiple with pagination |
| findById(collection, id, options) | Get by ID |
| create(collection, data) | Create entity |
| update(collection, id, data) | Update entity |
| delete(collection, id) | Delete entity |
License
MIT
