Last active
January 24, 2025 00:31
-
-
Save tyteen4a03/57e89cf64380f29928acb46b17e8ae67 to your computer and use it in GitHub Desktop.
Coerce Postgres columns schema types by @ShilohFox (License: MIT)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { bigint, integer, serial, smallint } from "drizzle-orm/pg-core"; | |
import type { Field } from "payload"; | |
import toSnakeCase from "to-snake-case"; | |
/* | |
* Iterates through the list of fields and prepares changes using Payload's Drizzle exposure. | |
* The fields marked with `numberType: "smallint" | "integer" | "bigint"` in their `custom` | |
* field will use their respective column type in the schema, so postgres can coerce them. | |
* | |
* fields: The fields to comb through recursively. | |
* namePrefix: The camelCase name to prefix column field names, if the fields are from a group. | |
* slugPrefix: The snake_case name to prefix db column slugs, if the fields are from a group. | |
* | |
* returns: The modified columns if there were any modifications made. | |
*/ | |
export function combFields( | |
fields: Field[], | |
ignoreId = false, | |
namePrefix = "", | |
slugPrefix = "", | |
// biome-ignore lint/suspicious/noExplicitAny: generic | |
): { modified: false } | { modified: true; columns: { [name: string]: any } } { | |
// Tracks whether or not there were any changes made, and thus "extending" the table | |
let extending = false; | |
// Stores changes made to any columns | |
// biome-ignore lint/suspicious/noExplicitAny: generic | |
let columns: { [name: string]: any } = {}; | |
// Use the field definition to deduce whether we want to change its numeric type to a specific numeric type | |
for (const field of fields) { | |
// Recurse into field groups and append applicable changes | |
if (field.type === "group") { | |
const combedFields = combFields( | |
field.fields, | |
true, | |
`${namePrefix}${field.name}_`, | |
`${slugPrefix}${toSnakeCase(field.name)}_`, | |
); | |
if (!combedFields.modified) continue; | |
extending = true; | |
columns = { ...columns, ...combedFields.columns }; | |
continue | |
} | |
if (field.type === "collapsible" || field.type === "row") { | |
const combedFields = combFields( | |
field.fields | |
); | |
if (!combedFields.modified) continue; | |
extending = true; | |
columns = { ...columns, ...combedFields.columns }; | |
continue | |
} | |
if (field.type === "tabs") { | |
for (const tab of field.tabs) { | |
const combedFields = combFields( | |
tab.fields | |
); | |
if (!combedFields.modified) continue; | |
extending = true; | |
columns = { ...columns, ...combedFields.columns }; | |
} | |
continue; | |
} | |
// TODO: Handle blocks correctly | |
if (field.type === "blocks") continue; | |
if (field.type !== "number" || field.custom?.numberType === undefined) continue; | |
// At this point, the field qualifies for the schema change | |
extending = true; | |
const fieldName = namePrefix + field.name; | |
const fieldSlug = slugPrefix + toSnakeCase(field.name); | |
// Generate column type from numberType value | |
switch (field.custom.numberType) { | |
case "serial": | |
columns[fieldName] = serial(fieldSlug); | |
break; | |
case "integer": | |
columns[fieldName] = integer(fieldSlug); | |
break; | |
case "smallint": | |
columns[fieldName] = smallint(fieldSlug); | |
break; | |
case "bigint": | |
columns[fieldName] = bigint(fieldSlug, { mode: "bigint" }); | |
break; | |
default: | |
throw new Error(`Unsupported numberType ${field.custom.numberType} in field ${field.name}`); | |
} | |
// Generate any necessary modifiers for the column | |
if (field.defaultValue !== undefined) { | |
columns[fieldName].default(field.defaultValue); | |
} | |
if (field.required !== undefined && field.required) { | |
columns[fieldName].notNull(); | |
} | |
if (field.unique !== undefined && field.unique) { | |
columns[fieldName].unique(); | |
} | |
if (!ignoreId && field.name === "id") { | |
columns[fieldName] = serial(fieldSlug).primaryKey(); | |
} | |
} | |
if (!extending) return { modified: false }; | |
return { | |
modified: extending, | |
columns, | |
}; | |
} // combFields() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
db: postgresAdapter({ | |
pool: { | |
connectionString: process.env.DATABASE_URI || "", | |
}, | |
afterSchemaInit: [ | |
({ schema, extendTable, adapter }) => { | |
// Use our collection definitions to generate the int field changes we want to see in the world | |
for (const collection of allCollections) { | |
const combedFields = combFields(collection.fields); | |
if (combedFields.modified) { | |
extendTable({ | |
table: schema.tables[toSnakeCase(collection.slug)], | |
columns: combedFields.columns, | |
}); | |
} | |
} | |
return schema; | |
}, | |
], | |
}), | |
... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment