Integration of Cockpit CMS with Frontend via API
Cockpit provides a REST API for all content operations. Authorization via API token in query parameter or header.
Basic Client
// lib/cockpit.ts
class CockpitClient {
private baseUrl: string;
private token: string;
constructor(url: string, token: string) {
this.baseUrl = url.replace(/\/$/, '');
this.token = token;
}
private async request(path: string, options: RequestInit = {}) {
const res = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Cockpit-Token': this.token,
...options.headers,
},
next: { revalidate: 3600 }, // Next.js ISR
});
if (!res.ok) throw new Error(`Cockpit API error: ${res.status}`);
return res.json();
}
// Collection entries
async getCollection(name: string, params: CollectionParams = {}) {
const body = {
limit: params.limit || 100,
skip: params.skip || 0,
sort: params.sort || { _created: -1 },
filter: params.filter || {},
populate: params.populate || 1,
fields: params.fields,
};
return this.request(`/api/collections/get/${name}`, {
method: 'POST',
body: JSON.stringify(body),
});
}
// Single entry by ID
async getCollectionItem(collection: string, id: string) {
return this.request(`/api/collections/get/${collection}`, {
method: 'POST',
body: JSON.stringify({ filter: { _id: id }, limit: 1 }),
});
}
// Singleton
async getSingleton(name: string) {
return this.request(`/api/singletons/get/${name}`);
}
// Image with transformation
getImageUrl(path: string, options: ImageOptions = {}) {
const params = new URLSearchParams({
src: path,
w: String(options.width || 800),
h: String(options.height || 600),
m: options.mode || 'thumbnail',
q: String(options.quality || 80),
o: '1',
});
return `${this.baseUrl}/api/cockpit/image?${params}&token=${this.token}`;
}
}
export const cockpit = new CockpitClient(
process.env.COCKPIT_URL!,
process.env.COCKPIT_API_TOKEN!
);
Next.js: Static Pages
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const { entries } = await cockpit.getCollection('posts', {
filter: { published: true },
fields: { slug: 1 },
});
return entries.map((post: any) => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
const { entries } = await cockpit.getCollection('posts', {
filter: { slug: params.slug, published: true },
limit: 1,
populate: 2,
});
if (!entries.length) notFound();
const post = entries[0];
return (
<article>
<h1>{post.title}</h1>
{post.image && (
<img
src={cockpit.getImageUrl(post.image.path, { width: 1200, height: 630 })}
alt={post.title}
/>
)}
<div dangerouslySetInnerHTML={{ __html: post.description }} />
</article>
);
}
Search Implementation
Cockpit REST API does not support full-text search natively. Implement via regex filter:
async function searchPosts(query: string) {
const { entries } = await cockpit.getCollection('posts', {
filter: {
published: true,
$or: [
{ title: { $regex: query, $options: 'i' } },
{ description: { $regex: query, $options: 'i' } },
],
},
limit: 20,
});
return entries;
}
For full-featured search — index in Algolia via webhook on changes.
GraphQL API
Cockpit also provides a GraphQL endpoint at /api/graphql:
query {
posts: collectionGet(collection: "posts", limit: 10, sort: {_created: -1}) {
entries {
_id
title
slug
image
}
total
}
homepage: singletonGet(singleton: "homepage") {
hero_title
hero_subtitle
hero_image
}
}
Integration with Next.js (2–3 collections, singleton settings, images via CDN) takes 2–4 days.







