DeesseJS

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

On this page