Skip to content

Foreign keys

Foreign keys are defined in the constraints object of table definition using the foreignKey function.

Single column

ts
import { integer, foreignKey, primaryKey, table } from "@monolayer/pg/schema";

const users = table({ 
  columns: {
    id: integer().generatedAlwaysAsIdentity(), 
  },
  constraints: {
    primaryKey: primaryKey(["id"]),
  },
});

const documents = table({
  columns: {
    id: integer().generatedAlwaysAsIdentity(),
    userId: integer(),
  },
  constraints: {
    primaryKey: primaryKey(["id"]),
    foreignKeys: [ 
      foreignKey(["userId"], users, ["id"]), 
    ], 
  },
});

Multiple of columns

ts
import {
  integer,
  foreignKey,
  primaryKey,
  table,
  timestampWithTimeZone
} from "@monolayer/pg/schema";

const projects = table({ 
  columns: {
    id: integer(), 
    departmendId: integer(), 
  },
  constraints: {
    primaryKey: primaryKey(["id", "departmentId"]),
  },
});

const assignments = table({
  columns: {
    dueDate: timestampWithTimeZone(),
    projectId: integer().generatedAlwaysAsIdentity(),
    departmentId: integer(),
  },
  constraints: {
    foreignKeys: [ 
      foreignKey( 
        ["projectId", "departmentId"], 
        projects, 
        ["id", "departmentId"] 
      ),
    ], 
  },
});

With actions

When you define a foreign key constraint, you can specify actions to taken when the referenced row in the parent table is deleted or updated. There are five actions that you can set when a referenced row is updated or deleted:

ACTIONBehavior
CASCADEWhen a referenced row is deleted, row(s) referencing it should be automatically deleted as well
SET NULLReferencing column(s) in the referencing row(s) will be set to NULL when the referenced row is deleted or when the referenced row key is updated
SET DEFAULTReferencing column(s) in the referencing row(s) will be set to their default value when the referenced row is deleted or when the referenced row key is updated
RESTRICTPrevents the deletion of a referenced row up updated of a referenced key
NO ACTIONIf any referencing rows still exist when the constraint is checked, an error is raised. Similar to RESTRICT. The difference is that NO ACTION allows the check to be deferred until later in the transaction, whereas RESTRICT does not

INFO

The default action for a foreign key is NO ACTION.

Read more about foreign keys in PostgreSQL - Foreign Keys

Use the modifiers deleteRule and updateRule to specify the foreign key actions:

ts

const documents = table({
  columns: {
    id: integer().generatedAlwaysAsIdentity(),
    userId: integer(),
  },
  constraints: {
    primaryKey: primaryKey(["id"]),
    foreignKeys: [
      foreignKey(["userId"], users, ["id"]) 
        .deleteRule("set null") 
        .updateRule("cascade"), 
    ],
  },
});

Self-referential

ts
import { integer, foreignKey, primaryKey, table } from "@monolayer/pg/schema";

const tree = table({
  columns: {
    nodeId: integer().generatedAlwaysAsIdentity(), 
    parentId: integer(), 
  },
  constraints: {
    primaryKey: primaryKey(["nodeId"]),
    foreignKeys: [ 
      foreignKey(["parentId"], ["nodeId"]), 
    ], 
  },
});