Official TypeScript SDK for integrating Pippa (thebeam.studio) as a headless CMS into your Next.js application.
- 🔐 Secure API authentication with Bearer tokens
- ⚡ ISR caching — automatic revalidation every hour
- 🔄 Webhook support — instant cache invalidation when content changes
- 📦 Zero dependencies — tiny bundle size
- 🎯 Full TypeScript support — complete type safety
- 🚀 Next.js optimized — built for App Router
- 🎨 SEO helpers — metadata and JSON-LD generation
From private git repository:
pnpm add "git+https://github.com/ptrrrr/thebeam-sdk.git"
# or
npm install "git+https://github.com/ptrrrr/thebeam-sdk.git"From npm (if published):
npm install @thebeam/sdk
# or
pnpm add @thebeam/sdkInstall the SDK and run the interactive installer:
# 1. Install the SDK
pnpm add "git+https://github.com/ptrrrr/thebeam-sdk.git"
# 2. Run the installer
pnpm exec beam-init
# or: npx beam-initThe installer will:
- ✅ Ask for your preferred route path (
/blog,/insights,/articles, etc.) - ✅ Check for conflicts and create backups
- ✅ Copy and configure all templates
- ✅ Install missing dependencies
- ✅ Guide you through next steps
That's it! Just add your BEAM_API_KEY to .env.local and visit your blog route.
If you prefer manual control:
- Log in to your Pippa admin panel
- Go to Sites → [Your Site] → Settings
- Enable "Headless Mode"
- Create an API key in the "API Keys" section
// lib/beam.ts
import { createBeamClient } from '@thebeam/sdk';
export const beam = createBeamClient({
apiKey: process.env.BEAM_API_KEY!,
baseUrl: 'https://www.thebeam.studio', // optional
revalidate: 3600, // optional, default: 3600 seconds (1 hour)
});Add to .env.local:
BEAM_API_KEY=beam_your_api_key_here// app/blog/page.tsx
import { beam } from '@/lib/beam';
export default async function BlogPage() {
const { posts, pagination } = await beam.posts.list({ limit: 10 });
return (
<div>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<a href={`/blog/${post.slug}`}>Read more</a>
</article>
))}
</div>
);
}The SDK includes complete Next.js page templates that you can copy into your project for instant blog integration.
# Copy all templates to your project
cp -r node_modules/@thebeam/sdk/templates/* .This includes:
- ✅ Blog homepage (
app/blog/page.tsx) — Paginated post list - ✅ Single post page (
app/blog/[slug]/page.tsx) — Full post with SEO - ✅ Category page (
app/blog/category/[category]/page.tsx) — Filtered posts - ✅ Reusable components — PostCard, PostContent, Pagination
- ✅ Webhook route (
app/api/revalidate/route.ts) — Cache invalidation - ✅ Beam client (
lib/beam.ts) — Pre-configured setup
Production-ready pages with:
- Responsive Tailwind CSS styling
- Full TypeScript support
- Automatic SEO metadata
- JSON-LD structured data
- Image optimization
- ISR caching
- Related posts
- Pagination
- Breadcrumbs
See node_modules/@thebeam/sdk/templates/README.md for detailed setup instructions and customization options.
Creates a new Beam client instance.
Options:
apiKey(required): Your Beam API keybaseUrl(optional): Base URL of your Pippa instance (default:https://www.thebeam.studio)revalidate(optional): ISR revalidation time in seconds (default:3600)
Fetch a paginated list of posts.
const { posts, pagination } = await beam.posts.list({
page: 1, // Page number (default: 1)
limit: 10, // Posts per page (default: 10)
category: 'guides', // Filter by category (optional)
search: 'keyword', // Search query (optional)
});Returns:
{
posts: PostListItem[];
pagination: {
page: number;
limit: number;
total: number;
total_pages: number;
};
}Fetch a single post by slug.
const { post, related_posts } = await beam.posts.get('my-post-slug', {
preview: false, // Include draft posts (default: false)
});Returns:
{
post: Post;
related_posts: RelatedPost[];
}Fetch all categories with post counts.
const { categories } = await beam.categories.list();
// [{ name: 'guides', count: 15 }, ...]Fetch site metadata (name, theme, navigation, social links).
const { site } = await beam.site.get();
console.log(site.name); // "Your Site Name"
console.log(site.theme); // { primary: "#...", ... }
console.log(site.nav); // [{ label: "Home", href: "/" }, ...]Fetch all post URLs for sitemap generation.
const { urls } = await beam.sitemap.get();
// [{ slug: 'my-post', updated_at: '2025-03-10T12:00:00Z', category: 'guides' }, ...]Generate Next.js Metadata object for a post.
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const { post } = await beam.posts.get(params.slug);
return beam.seo.metadata(post, 'https://yoursite.com');
}Generate Article JSON-LD structured data.
const jsonLd = beam.seo.jsonLd(post);
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
);Create a webhook handler for cache invalidation.
// app/api/revalidate/route.ts
import { beam } from '@/lib/beam';
export const POST = beam.revalidate.handler(process.env.BEAM_WEBHOOK_SECRET);When you publish or update a post in Pippa, it will automatically invalidate your Next.js cache via revalidateTag('beam-posts').
Setup:
- Create the route above
- In Pippa admin, go to Sites → [Your Site] → API Keys
- Edit your API key and set:
- Webhook URL:
https://yoursite.com/api/revalidate - Webhook Secret: any random string (save in
.env.local)
- Webhook URL:
All types are exported for your convenience:
import type {
Post,
PostListItem,
RelatedPost,
Category,
Site,
SiteTheme,
Pagination,
BeamMetadata,
ArticleJsonLd,
} from '@thebeam/sdk';Without webhooks:
- Content is cached for the configured
revalidatetime (default: 1 hour) - Stale content is served for 5 minutes while revalidating in the background
With webhooks:
- Content is cached indefinitely
- Instantly invalidated when you publish/update/unpublish content
- First request after change: ~300ms (API call)
- Subsequent requests: served from edge cache (< 50ms)
// lib/beam.ts
import { createBeamClient } from '@thebeam/sdk';
export const beam = createBeamClient({
apiKey: process.env.BEAM_API_KEY!,
});
// app/blog/page.tsx
import { beam } from '@/lib/beam';
export default async function BlogPage() {
const { posts } = await beam.posts.list({ limit: 10 });
return (
<div>
{posts.map((post) => (
<article key={post.id}>
<h2><a href={`/blog/${post.slug}`}>{post.title}</a></h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
// app/blog/[slug]/page.tsx
import { beam } from '@/lib/beam';
import { notFound } from 'next/navigation';
import type { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const data = await beam.posts.get(params.slug);
if (!data) return {};
return beam.seo.metadata(data.post, 'https://yoursite.com');
}
export default async function PostPage({ params }) {
const data = await beam.posts.get(params.slug);
if (!data) notFound();
const { post, related_posts } = data;
const jsonLd = beam.seo.jsonLd(post);
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.body_html }} />
</article>
</>
);
}
// app/api/revalidate/route.ts
import { beam } from '@/lib/beam';
export const POST = beam.revalidate.handler(process.env.BEAM_WEBHOOK_SECRET);For detailed integration guides and examples, see the full integration documentation.
- 100 requests per minute per API key
- Exceeding the limit returns
429 Too Many Requests - Contact support for higher limits if needed
This SDK ships with pre-built dist/ files committed to git. This avoids build issues during installation for consumers.
When updating the SDK:
# 1. Make changes to src/
cd packages/sdk/src
# Edit files...
# 2. Build
cd ..
pnpm build
# 3. Commit both src/ and dist/
git add src/ dist/
git commit -m "Update SDK: description of changes"
# 4. Push to SDK repo
cd /tmp/thebeam-sdk
git pull
cp -r /path/to/usepippa/packages/sdk/* .
git add .
git commit -m "Update SDK"
git pushImportant: Always rebuild and commit dist/ after changing src/ files.
MIT