Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/nikolasburk/generated-columns


https://github.com/nikolasburk/generated-columns

Last synced: 25 days ago
JSON representation

Awesome Lists containing this project

README

        

# Generated columns with PostgreSQL and Prisma

This example shows how to use [generated columns](https://www.postgresql.org/docs/12/ddl-generated-columns.html) (aka _computed fields_) with PostgreSQL and Prisma.

While the `GENERATED` keyword that's used in PostgreSQL to define generated columns is not supported in the Prisma schema, you can still customize the SQL that's generated by Prisma Migrate to make sure certain fields of your Prisma models will be using `GENERATED` under the hood.

This project shows an example of such a migration.

## Workflow

### 1. Define your Prisma model

In this step, you define the Prisma model **inclduding the field you want to be generated**. For this simple demo, you can use the following:

```prisma
model User {
id Int @id @default(autoincrement())
firstName String?
lastName String?
fullName String? // GENERATED
}
```

### 2. Create the SQL migration using the `--create-only` option

Next, you'll generate the SQL migration file **without yet applying it to the database** using the following commnad:

```
npx prisma migrate dev --name init --create-only
```

### 3. Customize the generated SQL to include the `GENERATED` keyword

After you ran the command above, you can open the `migration.sql`-file inside of the `migrations/TIMESTAMP-init`-file. It should look as follows:

```sql
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"firstName" TEXT,
"lastName" TEXT,
"fullName" TEXT,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
```

You can now manually edit this file to determine which of the columns should be generated, and by which rules. In this case, we want to concatenate the `firstName` and the `lastName` with a space in between:

```sql
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"firstName" TEXT,
"lastName" TEXT,
+ "fullName" TEXT GENERATED ALWAYS AS (
+ CASE
+ WHEN "firstName" IS NULL THEN "lastName"
+ WHEN "lastName" IS NULL THEN "firstName"
+ ELSE "firstName" || ' ' || "lastName"
+ END
+ ) STORED,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
```

### 4. Apply the migration against the database

Finally, you can use Prisma Migrate to actually make changes against the database schema:

```
npx prisma migrate dev
```

### 5. Query with Prisma Client

You can now send queries against the `User` table using the generated `PrismaClient` instance.

Note that **it is not possible to set a value for the `fullName` field when invoking `prisma.user.create(...)`, e.g.:

```ts
const newUser = await prisma.user.create({
data: {
firstName: "Jane",
lastName: "Doe",
fullName: "asd" // not possible to set a value for a `GENERATED` column
}
})
```

You will see this error:

```
PrismaClientUnknownRequestError:
Invalid `prisma.user.create()` invocation in
/Users/nikolasburk/Desktop/script/script.ts:8:37

5 // A `main` function so that we can use async/await
6 async function main() {
7
→ 8 const newUser = await prisma.user.create(
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Error { kind: Db, cause: Some(DbError { severity: "ERROR", parsed_severity: Some(Error), code: SqlState("42601"), message: "cannot insert into column \"fullName\"", detail: Some("Column \"fullName\" is a generated column."), hint: None, position: None, where_: None, schema: None, table: None, column: None, datatype: None, constraint: None, file: Some("rewriteHandler.c"), line: Some(830), routine: Some("rewriteTargetListIU") }) }) })
at cb (/Users/nikolasburk/Desktop/script/node_modules/@prisma/client/runtime/index.js:38692:17) {
clientVersion: '3.7.0'
}
```

This makes sense because the value of this column will always be computed based on the values of the `firstName` and `lastName` columns in a specific row.

The following code is going to work though:

```ts
const newUser = await prisma.user.create({
data: {
firstName: "Jane",
lastName: "Doe",
}
})
console.log(newUser)
```

This prints the following to the terminal:

```js
{ id: 1, firstName: 'Jane', lastName: 'Doe', fullName: 'Jane Doe' }
```