Zmaj Headless CMS

website·Indexed 29 days ago

https://zmaj.vercel.app/

A headless CMS that provides a RESTful API for databases and an integrated React-based admin panel for data management. It features a type-safe ORM (a thin wrapper around Sequelize), support for RBAC and ABAC authorization, webhooks for collection changes, and flexible storage options for local files or S3 buckets. Zmaj includes a client SDK for type-safe API consumption and supports Postgres databases and optional Redis caching.

Tokens
14.7K
Snippets
81
Records
111
Agent score
75%

What's inside Zmaj

  1. Overview of Zmaj Headless CMS

    Zmaj is a headless CMS that provides a RESTful API and an admin panel for database management. Unlike many CMS platforms, Zmaj uses database tables directly without a proprietary abstraction layer; collections in Zmaj correspond exactly to database tables, and fields correspond to columns. Relations are enforced via foreign keys. Zmaj stores its own internal data in tables with the zmaj_ prefix, allowing for easy removal by deleting those specific tables.

    Warning: Zmaj has not yet reached a stable version and should not be used in production environments.

  2. Overview of Collections REST API endpoints

    Collections are accessed via the /api/collections base endpoint. The API supports standard CRUD operations based on the collection name and record ID.
    UsageMethodURL
    Get paginated recordsGET/api/collections/:collectionName
    Get specific recordGET/api/collections/:collectionName/:id
    Create recordPOST/api/collections/:collectionName
    Update recordPUT/api/collections/:collectionName/:id
    Delete recordDELETE/api/collections/:collectionName/:id
  3. Overview of the Zmaj integrated admin panel

    Zmaj includes an integrated admin panel that provides a graphical user interface (GUI) for nearly all functionality supported by the API. The admin panel is built using a stack consisting of React, react-admin, react-query, @headlessui/react, react-aria, and Tailwind CSS.
  4. Understand Zmaj CRUD event hook lifecycle

    Zmaj allows developers to hook into CRUD events using four distinct lifecycle stages. Hooks in the 'start' and 'finish' stages have access to the database transaction via event.trx.

    • before: Occurs before the transaction starts.
    • start: Occurs immediately after the transaction has started.
    • finish: Occurs immediately before the transaction is finished.
    • after: Occurs after the transaction has ended.

    Error Handling:

    • If an error is thrown during start or finish events, the transaction is cancelled and no changes are saved to the database.
    • If an error is thrown during the after event, the API returns an error response, but database changes are preserved.
  5. Use Email and Password or Magic Link authentication

    Zmaj supports two email-based authentication flows:

    1. Email and Password: Standard sign-in that generates a short-lived access token and a long-lived refresh token. Client SDKs and the admin panel refresh these tokens automatically.
    2. Magic Links: Sends a custom sign-in link to the user's email (valid for 5 minutes). This requires a configured email provider and uses the passport-magic-login package.
  6. Use Zmaj as a Headless CMS via REST API

    Zmaj functions as a headless CMS by providing a RESTful API, allowing developers to decouple the backend content management from the frontend presentation layer.
  7. Understand Zmaj's Role and Permission Model

    Zmaj uses a hybrid of RBAC and ABAC powered by CASL. Users are assigned a single role. Permissions are stored in the zmaj_permissions table and linked to roles in the zmaj_roles table.

    There are two mandatory roles:

    • Admin: Full access to everything.
    • Public: Assigned to unsigned-in users or specific registered users.

    A permission defines an action, a resource (e.g., a collection), specific fields the user can access, and conditions that must be met for the resource to be accessible.

  8. Manage Zmaj applications using the Admin Panel

    Zmaj includes a built-in Admin Panel that allows users to configure their application settings and manage content easily.
  9. Create and run custom user migrations in Zmaj

    User migrations allow for custom schema changes and are executed after system migrations.

    1. Generate: Use the Zmaj CLI to create a migration file containing up and down logic. Zmaj uses the exported name to determine the execution order.
    2. Import: Import the generated migration file into the application.
    3. Execute: User migrations only run if both autoRunMigrations and autoRunUserMigrations are set to true.

    Note: User migrations cannot currently be run via the CLI; they must be configured in the server setup.

    # Generate a new migration
    npx zmaj create-migration my_migration_name
    import * as MyMigration from "2023_01_03_20_12_36__my_migration.js"
    
    runServer({
        migrations: {
            autoRunMigrations: true,
            autoRunUserMigrations: true,
            migrations: [MyMigration],
        },
    })
    
  10. Implement CRUD event hooks using @OnCrudEvent

    Use the @OnCrudEvent decorator within a NestJS @Injectable() service to trigger logic based on specific CRUD actions and lifecycle stages. You can target specific collections or use * as a wildcard for all collections or all actions.
    import { OnCrudEvent, UpdateAfterEvent, EmailService, runServer } from "zmaj"
    import { Injectable } from "@nestjs/common"
    
    type Post = { id: string; title: string }
    
    @Injectable()
    class MyService {
        constructor(private emailService: EmailService) 
    
        @OnCrudEvent({
            action: "update", // 'create', 'read', 'update', 'delete', or '*'
            type: "after",    // 'before', 'start', 'finish', 'after'
            collection: "posts", // Collection name or '*'
        })
        async sendEmailOnUpdate(event: UpdateAfterEvent<Post>) {
            const ids = event.result.map((post) => post.id)
            await this.emailService.sendEmail({
                to: "admin@example.test",
                text: `There was a change in posts ${ids.join(", ")}.`,
            })
        }
    }
    
    await runServer({
        customProviders: [MyService],
    })
  11. Create the first admin user

    You can create an initial admin user via the CLI or the browser. The CLI method requires a configured .env file with valid database parameters and a secret key.
    # Via CLI
    npx zmaj create-admin admin@example.com
  12. Implement Many-to-One (M2O) and One-to-Many (O2M) relations

    M2O and O2M relations are defined by a single non-unique foreign key column. The 'owning side' is the table containing the foreign key (M2O), and the 'referencing side' is the table containing the primary key (O2M). Zmaj automatically adapts to these schema changes.
    await knex.schema.createTable("posts", (t) => {
        t.uuid("id").primary()
        t.string("title")
    })
    
    await knex.schema.createTable("comments", (t) => {
        t.uuid("id").primary()
        t.text("body")
        // The foreign key column creates the M2O relation
        t.uuid("post_id")
        t.foreign("post_id").references("id").inTable("posts")
    })