DEV Community 👩‍💻👨‍💻

Cover image for Generating CMS from Prisma Schema
Danny Kim
Danny Kim

Posted on

Generating CMS from Prisma Schema

One of Prisma's best features is its TypeScript client generated from Prisma schema. For me, it is a killer feature that differentiates Prisma from other JavaScript ORMs. By using the generated TypeScript client, I don't need to worry about writing custom types no matter how complex my queries are. Even better, Prisma's code generator system is designed to be extensible, so we can make it generate more than just TS client.

Motivated by this flexibility of Prisma generator, I tried to generate a content management system (CMS) from a Prisma schema. This project is still in proof-of-concept stage, but I think it has a potential to be used for building internal admin dashboards.

Existing Projects

There are already content management systems out there that are built on top of Prisma, so let's take a look at some major ones first:

KeystoneJS

Image description
KeystoneJS is a headless CMS with a built-in ORM. If you design a database schema using Keystone schema API, this tool generates everything from a GraphQL server to admin UIs. It is an amazing productivity booster. It started to support Prisma as a database adapter from 2021 therefore widened the gap between other headless CMS tools and itself.

Although Keystone is a one of the superior frameworks, it is limited in a sense that it cannot be easily attached to exiting projects because Keystone needs to manage the database itself.

PalJS

Image description
PalJS is a GraphQL API + admin UI generation tool built by a solo developer. It covers e2e just like KeystoneJS, but it is much more flexible because PalJS is a Prisma generator! As long as you have a Prisma schema (either prisma db pulled or written yourself), you can attach PalJS to any project.

It is a bit difficult to customize the generated UI because everything is wrapped in components.

prisma-generator-proto

Image description
So I built a proof-of-concept project called prisma-generator-proto to overcome the limitations of existing projects.

Making use of Prisma generator interface

Even though some content management systems are standalone services (e.g. Wordpress), others use a CMS for managing data for existing services. Something like Keystone can be good for greenfield projects, but it is challenging to adopt in existing projects. So I wanted prisma-generator-proto to be a CMS generated from a Prisma schema for ease of adoption.

Providing UI as React hooks

It is definitely convenient to have the entire UI/pages generated like PalJS, most teams need to customize their admin UI as they grow. If you have ever tried to customize components from UI libraries like Material UI, you would know how tricky it is to change behavior of packaged components. So prisma-generator-proto only exposes data & operations as React hooks without any styling. Developers can build admin UI by mixing these generated hooks with an UI component library.

Prisma generator under the hood

All Prisma generators have a similar mechanism.
Image description

Prisma schema

It all starts from a file called schema.prisma:

datasource db {
  url      = env("DATABASE_URL")
  provider = "postgresql"
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(255)
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?
}
Enter fullscreen mode Exit fullscreen mode

As you can see above, there are three main directives in schema.prisma:

  • datasource : Defines how to connect to the database
  • model : Defines each data model
  • generator : Configures a generator that consumes this schema file. By default, we generate TypeScript client with prisma-client-js generator, and we can declare additional generators using generator directive again.

schema.prisma file can either be hand-written or be pulled from existing database with prisma db pull CLI.

prisma generate

prisma generate command sequentially runs each generators declared in schema.prisma. Prisma takes care of parsing the schema, and each generator receives a parsed result called "DMMF". Each DMMF contains the following information as JS objects:

  • A list of all models
  • A list of fields within each model
  • Type, default value, nullability of each field
  • Model associations

Also, Prisma lets each generator where to output the generated files, so a generator's job is just to create some files from DMMF and writing them in the designated output directory.

Progress

Here's what prisma-generator-proto can do now:

  • REST API generation

It generates an Express router containing CRUD routes for all models. It does not generate the Express application itself.

Usage:

import express from "express";
import { router } from "../__generated__";

const app = express();
app.use(express.json());

// Use the generated CRUD routes!
app.use("/api", router);

app.listen(3001, () => {
  console.log("Listening...");
});
Enter fullscreen mode Exit fullscreen mode
  • Client code generation

It generates React hooks that calls the generated REST API routes.

Usage:

import Form from "../components/Form";
import React from "react";
import Table from "../components/Table";
import styles from "../styles/DataScreen.module.css";
import { useCountry } from "../__generated__/hooks";
const Countries = () => {
  // Do CRUD operations with the generated hooks
  const [countries, addHandler, formState, setFormState] = useCountry();
  return (
    <div className={styles.screen}>
      <Form
        name="Country"
        keys={["name", "population", "continentId", "isUnMember"]}
        onSubmit={addHandler}
        state={formState}
        setState={setFormState}
      />
      <Table
        name="Country"
        list={countries}
        keys={["id", "name", "population", "continentId", "isUnMember"]}
      />
    </div>
  );
};
export default Countries;
Enter fullscreen mode Exit fullscreen mode

Try it yourself

prisma-generator-proto is available on NPM.

You can find the source code in this GitHub repository.

Read more & dig deeper

Although what I made is a simple CMS prototype, there are all sorts of crazy generators out there. Check this list out if you want to see more community generators!

Top comments (0)

Find what you were looking for? Sign up so you can:

 
🌚 Enable dark mode
🔠 Change your default font
📚 Adjust your experience level to see more relevant content