2022-01-07

CRUD

Can this be automated and maintainable at the same time?

I was testing Blitz.js today and it’s almost good but they don’t try hard enough, so I think I could create something like this myself. Wanted this since I left Rails ecosystem.

How to build application?

Schema design DSL

Let’s assume simple forum website. I would like to just specify this and get plumbing done.

Post belongsTo:user ?url? title ?body:text ?score:int:0
Comment belongsTo:post belongsTo:user body -by ?parentId:int:0 -parentPath:text:""

The ? before name would mean that it’s optional when inputing and ? would mean it’s optional when commiting. - would mean it’s not required when inputing (will define before commiting).

Default would be required (I fixed enough data integrity issues in my lifetime).

[
  {
    "modelName": "Post",
    "fields": [
      {
        "name": "url",
        "input": {
          "ignore": false,
          "isOptional": true
        },
        "commit": {
          "isOptional": true
        },
        "type": "text"
      },
      {
        "name": "title",
        "input": {
          "ignore": false,
          "isOptional": false
        },
        "commit": {
          "isOptional": false
        },
        "type": "text"
      },
      {
        "name": "body",
        "input": {
          "ignore": false,
          "isOptional": true
        },
        "commit": {
          "isOptional": false
        },
        "type": "text"
      },
      {
        "name": "score",
        "input": {
          "ignore": false,
          "isOptional": true
        },
        "commit": {
          "isOptional": false
        },
        "type": "int",
        "defaultValue": 0
      }
    ]
  },
  {
    "modelName": "Comment",
    "fields": [
      {
        "name": "body",
        "input": {
          "ignore": false,
          "isOptional": false
        },
        "commit": {
          "isOptional": false
        },
        "type": "text"
      },
      {
        "name": "by",
        "input": {
          "ignore": true,
          "isOptional": false
        },
        "commit": {
          "isOptional": false
        },
        "type": "text"
      },
      {
        "name": "parentId",
        "input": {
          "ignore": false,
          "isOptional": true
        },
        "commit": {
          "isOptional": false
        },
        "type": "int",
        "defaultValue": 0
      },
      {
        "name": "parentPath",
        "input": {
          "ignore": true,
          "isOptional": false
        },
        "commit": {
          "isOptional": false
        },
        "type": "text",
        "defaultValue": ""
      }
    ]
  }
]

Generate schema design

Given spec above, how do we generate schema file? Ezzz

CREATE TABLE IF NOT EXISTS post (
    postId int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    url TEXT,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  score INT NOT NULL
);

CREATE TABLE IF NOT EXISTS comment (
    commentId int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    body TEXT NOT NULL,
  by TEXT NOT NULL,
  parentId INT NOT NULL,
  parentPath TEXT NOT NULL
);

Run DB

docker run -p 5432:5432 --name db_name -e POSTGRES_PASSWORD=mysecretpassword -d postgres

Add schema

docker exec -it db_name psql -U postgres

Generate backend

Gonna use https://www.graphile.org/postgraphile/

npx postgraphile -c 'postgres://postgres:mysecretpassword@localhost:5432/postgres' --watch

Write frontend

Use Next.js with https://gqty.dev/docs/getting-started and Tailwind(twin.macro?)

npx create-next-app@latest --typescript

npm install gqty graphql
npm install -D @gqty/cli
npm install @gqty/react
npx gqty generate

In next.config.js:

/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: true,
  async rewrites() {
    return [
      {
        source: '/api/graphql',
        destination: 'http://localhost:5000/graphql' // Proxy to Postgraphile
      }
    ]
  }
}

Voila!

To handle forms one can use https://react-hook-form.com/

Alternative good client for more SPA style - https://react-query.tanstack.com/