glyph-cms
v4.5.0
Published
Lightweight serverless headless CMS for Next.js — Prisma & Drizzle supported
Downloads
645
Readme
🔮 Glyph CMS
The drop-in Next.js admin panel that builds itself.
You don't need another SaaS tool just to manage your content.
Glyph is a headless CMS that mounts directly into your Next.js app. You write Zod schemas to define your data. Glyph reads them and auto-generates a full admin dashboard and REST API.
No separate repositories. No third-party servers. No webhooks to configure.
Use cases:
- Marketing teams that need to edit landing pages without bothering developers.
- Custom e-commerce inventory dashboards without the Shopify tax.
- Internal admin panels to manage SaaS users and feature flags.
Features:
- Schema-driven: Zod is the single source of truth for your database validation, API routes, and UI forms.
- Serverless by default: It lives in your
app/directory and deploys right alongside your Next.js code. - Audit logs: Know exactly who changed what database row at 3 AM.
- Role-based access: Keep your marketing team out of your core app settings.
Quick Start
npm install glyph-cms@latest zod@^3.22.0If you're using Prisma, make sure you have the required dependencies:
npm install @prisma/client@latest
npm install -D prisma@latestNote: If you are deploying to a serverless environment (like Vercel) and using Neon/Supabase, you should also install
@prisma/adapter-pgandpgas per official Prisma docs.
If you're using Drizzle ORM, install these instead:
npm install drizzle-orm@latest
npm install -D drizzle-kit@latestnpx glyph-cms initThis scaffolds everything you need in seconds:
schemas/index.ts— Define your content shape here using Zod. You can export all your models from this single file.glyph.config.ts— Register your schemas, collections, and admin users.app/api/cms/[...params]/route.ts— The generated REST API handler.app/cms/[[...slug]]/page.tsx— The admin dashboard UI.
Add the Tailwind source to globals.css so the dashboard looks crisp:
@source "../../node_modules/glyph-cms/dist/**/*.{js,mjs}";Set your admin credentials in .env, run npm run dev, and head to /cms.
How It Works: Zod to CMS Magic
Write normal Zod. Glyph figures out the rest. It automatically maps Zod types to intuitive UI inputs.
// schemas/index.ts
import { z } from "zod";
export const PostSchema = z.object({
title: z.string().min(1).describe("Post Title"),
body: z.string(), // Automatically becomes a rich Textarea
coverImage: z.string().url().optional(), // URL input
published: z.boolean().default(false), // Toggle switch
category: z.enum(["tech", "design"]), // Dropdown select
tags: z.array(z.string()), // Add/remove list repeater
});
// You can export all your other schemas here too!
// export const CategorySchema = z.object({ ... })Register it in your config, and you instantly have a fully functioning CMS for that table:
// glyph.config.ts
import { defineConfig } from "glyph-cms";
import { PostSchema } from "@/schemas";
export default defineConfig({
schemas: { Post: PostSchema },
users: [{ username: process.env.GLYPH_ADMIN_USERNAME!, password: process.env.GLYPH_ADMIN_PASSWORD!, role: "admin" }],
});🔒 Roles, Permissions & Public APIs
By default, everything requires admin authentication. But you have granular control:
// Only needed when you want public reads or role restrictions:
collections: [
// Anyone can read posts via the API, but only admins can create/edit.
{ name: "Post", public: true },
// Only users with the "superadmin" role can even see the Settings page.
{ name: "Settings", allowedRoles: ["superadmin"] },
],Why the REST API?
The API routes (/api/cms/*) power the admin dashboard. However, if you set public: true on a collection, you can fetch data directly from your frontend or mobile app using standard GET /api/cms/post requests without needing to write custom queries!
🕵️♂️ Audit Logs
Need to know who broke the production homepage? Enable Audit Logs.
// glyph.config.ts
export default defineConfig({
auditLog: true, // Enable the logger
schemas: {
GlyphLog: GlyphLogSchema,
},
collections: [
// Make GlyphLog read-only by providing an empty array.
// This prevents anyone from modifying logs in the dashboard.
{ name: "GlyphLog", allowedRoles: [] }
],
// ...
});When enabled, Glyph tracks every single create, update, and delete action. It stores the exact diff of what changed, who changed it, and when.
Required Database Table:
You must create a table named GlyphLog in your database for audit logs to work.
For Prisma (schema.prisma):
model GlyphLog {
id String @id @default(cuid())
action String // "CREATE", "UPDATE", "DELETE"
collection String // "Post", "User", etc.
recordId String // The ID of the modified record
diff Json? // The changes made
user String // Username of the admin who made the change
createdAt DateTime @default(now())
}For Drizzle (db/schema.ts):
import { pgTable, text, timestamp, jsonb } from "drizzle-orm/pg-core";
export const glyphLog = pgTable("glyph_log", {
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
action: text("action").notNull(),
collection: text("collection").notNull(),
recordId: text("recordId").notNull(),
diff: jsonb("diff"),
user: text("user").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});Required Zod Schema (schemas/index.ts):
import { z } from "zod";
export const GlyphLogSchema = z.object({
action: z.string(),
collection: z.string(),
recordId: z.string(),
diff: z.any().optional(),
user: z.string(),
});⚡ CLI Magic: Generate Pages
Don't want to build the frontend pages yourself? Let Glyph scaffold Server Components for you:
# Generates a fully-typed React Server Component page that fetches from the API
npx glyph-cms generate --slug aboutNote for Next.js 15 users:
If you experience caching issues or errors with generated marketing pages, you may need to enable cacheComponents: true in your next.config.ts:
const nextConfig = {
experimental: {
cacheComponents: true, // required for "use cache" in Next 15
}
};Need to clean up?
npx glyph-cms remove --slug aboutUsing Drizzle ORM
Glyph supports Drizzle out of the box (PostgreSQL & SQLite only).
Step 1: Map your Drizzle tables in glyph.config.ts:
// glyph.config.ts
import { defineConfig } from "glyph-cms";
import { PostSchema } from "@/schemas";
import * as schema from "@/db/schema";
export default defineConfig({
schemas: {
Post: PostSchema,
},
// Drizzle only — map each collection to its Drizzle table object
tables: {
Post: schema.posts,
},
users: [{ username: process.env.GLYPH_ADMIN_USERNAME!, password: process.env.GLYPH_ADMIN_PASSWORD!, role: "admin" }],
});Step 2: Wire it up in your API route:
// app/api/cms/[...params]/route.ts
import { createCmsHandler, createDrizzleAdapter } from "glyph-cms";
import { db } from "@/db";
import config from "@/glyph.config";
const handler = createCmsHandler(
config,
createDrizzleAdapter(db, config.tables!)
);
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };🛠 Recommended Workflow
For Prisma Users
- Update Database Schema: Add your tables in
schema.prisma. - Apply Migrations: Run
npx prisma migrate dev --name "update"to sync the DB. - Create Zod Schema: Add your schemas to
schemas/index.ts. - Update Config: Register the schema and collection in
glyph.config.ts. - Start Server: Run
npm run devand manage your content.
For Drizzle Users
- Update Database Schema: Add your tables in your Drizzle schema file (e.g.
db/schema.ts). - Apply Migrations: Run
npx drizzle-kit push(or generate/migrate) to sync the DB. - Create Zod Schema: Add your schemas to
schemas/index.ts. - Update Config: Add the schema and table to
glyph.config.ts. - Start Server: Run
npm run devand manage your content.
🚨 Troubleshooting & Common Errors
1. Prisma TableDoesNotExist or Invalid model.count() invocation
- Why it happens: You added a model to
schema.prismabut the actual database tables don't exist yet, or you ranprisma migrate resetwhich wiped the tables because no migration file existed for them. - Fix: Always run
npx prisma migrate dev --name "add_models"after changingschema.prisma.
2. PrismaClient is unable to be run in the browser or Missing Client
- Why it happens: The Prisma Client hasn't been generated after a schema update.
- Fix: Run
npx prisma generate.
3. Collection has no registered schema
- Why it happens: You added a collection to
collections: ["MyModel"]inglyph.config.tsbut forgot to import and map its Zod schema. - Fix: Make sure it's mapped in the
schemasobject:schemas: { MyModel: MyModelSchema }.
4. Next.js Error: Only plain objects can be passed to Client Components
- Why it happens: You are trying to pass the full
configobject (which contains Zod classes) from a Next.js Server Component to the<GlyphAdmin>Client Component. - Fix: Pass
apiPathdirectly instead of the full config:<GlyphAdmin apiPath={config.apiPath || "/api/cms"} />
API
| Route | Method | Description |
|---|---|---|
| /:collection | GET | List (paginated, searchable) |
| /:collection/:id | GET | Get one |
| /:collection | POST | Create |
| /:collection/:id | PUT | Update |
| /:collection/:id | DELETE | Delete |
Query params: ?page=1&limit=20&search=hello&orderBy=createdAt&dir=desc
MIT © devberryy
