Plugin Examples
Example plugins to help you get started
Plugin Examples
This page contains practical examples of DeesseJS plugins that you can use as references or starting points for your own plugins.
Example 1: SEO Plugin
Add SEO fields to collections and generate meta tags:
// seo-plugin/index.ts
import { definePlugin } from '@deessejs/core'
interface SEOFields {
seoTitle?: string
metaDescription?: string
ogImage?: string
noIndex?: boolean
}
export const seoPlugin = definePlugin({
name: 'seo-plugin',
version: '1.0.0',
activate() {
return {
collections: {
extend: {
posts: {
fields: [
{
name: 'seoTitle',
type: 'string',
admin: {
label: 'SEO Title',
description: 'Override the default title for SEO',
},
},
{
name: 'metaDescription',
type: 'text',
admin: {
label: 'Meta Description',
description: 'Description for search engines (160 chars)',
validate: (value) => {
if (value && value.length > 160) {
return 'Meta description should be 160 characters or less'
}
return true
},
},
},
{
name: 'ogImage',
type: 'media',
admin: {
label: 'Open Graph Image',
description: 'Image shown when sharing on social media',
},
},
{
name: 'noIndex',
type: 'boolean',
admin: {
label: 'No Index',
description: 'Prevent search engines from indexing this page',
},
defaultValue: false,
},
],
},
},
},
}
},
})Usage in Frontend
// app/posts/[slug]/page.tsx
import { db } from '@deessejs/db'
export async function generateMetadata({ params }) {
const post = await db.posts.findUnique({
where: { slug: params.slug },
})
return {
title: post.seoTitle || post.title,
description: post.metaDescription,
openGraph: {
images: post.ogImage ? [post.ogImage] : [],
},
robots: {
index: !post.noIndex,
},
}
}Example 2: Slug Auto-Generation
Automatically generate URL-friendly slugs from titles:
// slug-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const slugPlugin = definePlugin({
name: 'slug-plugin',
version: '1.0.0',
activate() {
return {
hooks: {
beforeCreate: async ({ collection, data }) => {
if (collection === 'posts' && data.title && !data.slug) {
data.slug = generateSlug(data.title)
}
return data
},
beforeUpdate: async ({ collection, data, id }) => {
if (collection === 'posts' && data.title && data.autoSlug) {
data.slug = generateSlug(data.title)
}
return data
},
},
}
},
})
function generateSlug(text: string): string {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '')
}Example 3: Webhook Notifications
Send webhooks on content changes:
// webhook-plugin/index.ts
import { definePlugin } from '@deessejs/core'
interface WebhookConfig {
url: string
events: string[]
secret?: string
}
export const webhookPlugin = definePlugin<{ webhooks: WebhookConfig[] }>({
name: 'webhook-plugin',
version: '1.0.0',
activate({ webhooks }) {
return {
hooks: {
async afterCreate({ collection, item }) {
await sendWebhooks('create', collection, item, webhooks)
},
async afterUpdate({ collection, item }) {
await sendWebhooks('update', collection, item, webhooks)
},
async afterDelete({ collection, id }) {
await sendWebhooks('delete', collection, { id }, webhooks)
},
},
}
},
})
async function sendWebhooks(
event: string,
collection: string,
data: any,
webhooks: WebhookConfig[]
) {
const payload = {
event,
collection,
data,
timestamp: new Date().toISOString(),
}
for (const webhook of webhooks) {
if (!webhook.events.includes(event)) continue
try {
const response = await fetch(webhook.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(webhook.secret && {
'X-Webhook-Secret': webhook.secret,
}),
},
body: JSON.stringify(payload),
})
if (!response.ok) {
console.error(`Webhook failed: ${response.statusText}`)
}
} catch (error) {
console.error(`Webhook error:`, error)
}
}
}Configuration
// deesse.config.ts
export const config = defineConfig({
plugins: [
webhookPlugin({
webhooks: [
{
url: 'https://api.example.com/webhooks/deessejs',
events: ['create', 'update', 'delete'],
secret: process.env.WEBHOOK_SECRET,
},
],
}),
],
})Example 4: View Counter Plugin
Track how many times content is viewed:
// view-counter-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const viewCounterPlugin = definePlugin({
name: 'view-counter-plugin',
version: '1.0.0',
activate() {
return {
collections: {
extend: {
posts: {
fields: [
{
name: 'views',
type: 'number',
admin: {
label: 'Views',
readOnly: true,
},
defaultValue: 0,
},
],
},
},
},
api: {
routes: [
{
method: 'POST',
path: '/api/views/:id',
handler: './api/incrementView',
},
],
},
}
},
})API Handler
// api/incrementView.ts
import { NextRequest, NextResponse } from 'next/server'
import { db } from '@deessejs/db'
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const post = await db.posts.update({
where: { id: params.id },
data: {
views: {
increment: 1,
},
},
})
return NextResponse.json({ views: post.views })
}Usage in Frontend
// components/ViewCounter.tsx
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export function ViewCounter({ postId, initialViews }: { postId: string; initialViews: number }) {
const router = useRouter()
const [views, setViews] = useState(initialViews)
useEffect(() => {
fetch(`/api/views/${postId}`, { method: 'POST' })
.then((res) => res.json())
.then((data) => setViews(data.views))
}, [postId])
return <span>{views} views</span>
}Example 5: Reading Time Calculator
Calculate estimated reading time for articles:
// reading-time-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const readingTimePlugin = definePlugin({
name: 'reading-time-plugin',
version: '1.0.0',
activate() {
return {
hooks: {
beforeCreate: async ({ collection, data }) => {
if (collection === 'posts' && data.content) {
data.readingTime = calculateReadingTime(data.content)
}
return data
},
beforeUpdate: async ({ collection, data }) => {
if (collection === 'posts' && data.content) {
data.readingTime = calculateReadingTime(data.content)
}
return data
},
},
collections: {
extend: {
posts: {
fields: [
{
name: 'readingTime',
type: 'number',
admin: {
label: 'Reading Time (minutes)',
readOnly: true,
},
},
],
},
},
},
}
},
})
function calculateReadingTime(content: string): number {
const wordsPerMinute = 200
const words = content.trim().split(/\s+/).length
return Math.ceil(words / wordsPerMinute)
}Example 6: Custom Status Workflow
Add custom status workflow to posts:
// status-workflow-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const statusWorkflowPlugin = definePlugin({
name: 'status-workflow-plugin',
version: '1.0.0',
activate() {
return {
collections: {
extend: {
posts: {
fields: [
{
name: 'status',
type: 'enum',
enum: ['draft', 'review', 'approved', 'published', 'archived'],
defaultValue: 'draft',
admin: {
label: 'Status',
description: 'Content workflow status',
},
},
{
name: 'reviewedBy',
type: 'reference',
relation: 'users',
admin: {
label: 'Reviewed By',
condition: (data) => data.status === 'review',
},
},
{
name: 'publishedAt',
type: 'datetime',
admin: {
label: 'Published At',
condition: (data) => data.status === 'published',
},
},
],
},
},
},
hooks: {
beforeUpdate: async ({ collection, data, id }) => {
if (collection === 'posts') {
const current = await db.posts.findUnique({ where: { id } })
// Auto-set publishedAt when status changes to published
if (
data.status === 'published' &&
current?.status !== 'published'
) {
data.publishedAt = new Date()
}
}
return data
},
},
}
},
})Example 7: Content Revision History
Keep track of all content changes:
// revision-history-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const revisionHistoryPlugin = definePlugin({
name: 'revision-history-plugin',
version: '1.0.0',
activate() {
return {
collections: {
create: [
{
name: 'revisions',
fields: [
{
name: 'collection',
type: 'string',
required: true,
},
{
name: 'itemId',
type: 'string',
required: true,
},
{
name: 'data',
type: 'json',
required: true,
},
{
name: 'createdAt',
type: 'datetime',
defaultValue: () => new Date(),
},
{
name: 'createdBy',
type: 'reference',
relation: 'users',
},
],
},
],
},
hooks: {
beforeUpdate: async ({ collection, data, id }) => {
// Save current state before updating
const current = await db[collection].findUnique({ where: { id } })
if (current) {
await db.revisions.create({
data: {
collection,
itemId: id,
data: current,
},
})
}
return data
},
},
}
},
})Example 8: Scheduled Publishing
Schedule content to be published automatically:
// scheduled-publishing-plugin/index.ts
import { definePlugin } from '@deessejs/core'
export const scheduledPublishingPlugin = definePlugin({
name: 'scheduled-publishing-plugin',
version: '1.0.0',
activate() {
return {
api: {
routes: [
{
method: 'POST',
path: '/api/cron/publish-scheduled',
handler: './api/publishScheduled',
},
],
},
}
},
})Cron Handler
// api/publishScheduled.ts
import { NextResponse } from 'next/server'
import { db } from '@deessejs/db'
export async function POST() {
const now = new Date()
// Find all scheduled posts that should be published
const posts = await db.posts.findMany({
where: {
status: 'scheduled',
publishAt: {
lte: now,
},
},
})
// Publish them
for (const post of posts) {
await db.posts.update({
where: { id: post.id },
data: {
status: 'published',
publishedAt: now,
},
})
}
return NextResponse.json({ published: posts.length })
}Next Steps
- Learn how to Create Plugins
- Explore Plugin API Reference
- Return to Plugins Overview