Skip to main content

relations

This skill should be used when the user asks to "create a relation", "add foreign key", "set up one-to-many", "many-to-many relation", "one-to-one", "self-referential", "junction table", "composite primary key", "FK constraint", "create index on FK", or needs to connect tables in a PostgreSQL database that works with NocoDB and NocoBase.

Source facts

Repository
Agents-Store/codex-plugins
Last source activity
November 1, 2026 at 02:46
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
relations
description
This skill should be used when the user asks to "create a relation", "add foreign key", "set up one-to-many", "many-to-many relation", "one-to-one", "self-referential", "junction table", "composite primary key", "FK constraint", "create index on FK", or needs to connect tables in a PostgreSQL database that works with NocoDB and NocoBase.
# Relations — FK Constraints, Indexes, and Patterns All relations use NocoDB style: **physical FK constraints** + **indexes**. NocoBase reads existing FK constraints without conflicts. ## One-to-Many (HasMany / BelongsTo) **Scenario**: `customers` → `orders` (one customer — many orders). ```sql -- 1. Tables CREATE TABLE "public"."customers" ( "id" serial NOT NULL, "name" text, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); CREATE TABLE "public"."orders" ( "id" serial NOT NULL, "title" text, "amount" decimal(10, 2), "customer_id" int4, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); -- 2. FK constraint (NocoDB style) ALTER TABLE "public"."orders" ADD CONSTRAINT "fk_customers_orders" FOREIGN KEY ("customer_id") REFERENCES "public"."customers" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; -- 3. Index on FK CREATE INDEX "idx_orders_customer_id" ON "public"."orders" ("customer_id"); ``` > **ON DELETE NO ACTION** — NocoDB default. Deleting a parent is blocked if child records exist. Both systems correctly read this behavior. ## One-to-One **Scenario**: `users` → `profiles` (one user — one profile). ```sql CREATE TABLE "public"."users" ( "id" serial NOT NULL, "username" text, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); CREATE TABLE "public"."profiles" ( "id" serial NOT NULL, "user_id" int4 UNIQUE, -- UNIQUE = One-to-One "bio" text, "avatar_url" text, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); -- FK constraint ALTER TABLE "public"."profiles" ADD CONSTRAINT "fk_users_profiles" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; -- Index (UNIQUE already creates an implicit index, but explicit doesn't hurt) CREATE INDEX "idx_profiles_user_id" ON "public"."profiles" ("user_id"); ``` > **Key difference from One-to-Many**: `UNIQUE` on the FK column. NocoDB identifies One-to-One specifically by this constraint. ## Many-to-Many **Scenario**: `students` ↔ `courses`. ```sql -- 1. Main tables CREATE TABLE "public"."students" ( "id" serial NOT NULL, "name" text, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); CREATE TABLE "public"."courses" ( "id" serial NOT NULL, "title" text, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); -- 2. Junction table (NocoDB style — composite PK, no separate id) CREATE TABLE "public"."nc_m2m_students_courses" ( "students_id" int4 NOT NULL, "courses_id" int4 NOT NULL, PRIMARY KEY ("students_id", "courses_id") ); -- 3. FK constraints (NocoDB style) ALTER TABLE "public"."nc_m2m_students_courses" ADD CONSTRAINT "fk_students_courses_1" FOREIGN KEY ("students_id") REFERENCES "public"."students" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; ALTER TABLE "public"."nc_m2m_students_courses" ADD CONSTRAINT "fk_students_courses_2" FOREIGN KEY ("courses_id") REFERENCES "public"."courses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; -- 4. Indexes on both FKs CREATE INDEX "idx_m2m_students_id" ON "public"."nc_m2m_students_courses" ("students_id"); CREATE INDEX "idx_m2m_courses_id" ON "public"."nc_m2m_students_courses" ("courses_id"); ``` > **Composite PK** (`students_id`, `courses_id`) — guarantees pair uniqueness. NocoBase as external source correctly reads such tables. ## Self-Referential (Tree) **Scenario**: `categories` with nesting. ```sql CREATE TABLE "public"."categories" ( "id" serial NOT NULL, "title" text NOT NULL, "parent_id" int4, "created_at" timestamp DEFAULT now(), "updated_at" timestamp DEFAULT now(), PRIMARY KEY ("id") ); -- FK to self ALTER TABLE "public"."categories" ADD CONSTRAINT "fk_categories_parent" FOREIGN KEY ("parent_id") REFERENCES "public"."categories" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; -- Index CREATE INDEX "idx_categories_parent_id" ON "public"."categories" ("parent_id"); ``` ## Indexes ### When to Create | Situation | SQL | Required? | |-----------|-----|-----------| | FK column | `CREATE INDEX` | Yes | | One-to-One FK | `UNIQUE` on column + `CREATE INDEX` | Yes | | Unique field | `UNIQUE` on column | As needed | | Search field | `CREATE INDEX` | As needed | ### Syntax ```sql -- Regular index CREATE INDEX "idx_table_column" ON "public"."table_name" ("column_name"); -- Unique index CREATE UNIQUE INDEX "uq_table_column" ON "public"."table_name" ("column_name"); -- Drop index DROP INDEX IF EXISTS "public"."idx_table_column"; ``` ## One Relation Per Table Pair Between any two tables, use exactly **one** relation type — either a direct FK (One-to-Many) or a M2M junction table, never both. Having both a FK column and a junction table between the same two tables creates duplicate link columns in the UI, because both NocoDB and NocoBase auto-detect FK constraints and junction tables independently. If you need both a "primary" link (single value) and a "uses many" link between the same tables, model it as a single M2M junction — the "primary" is just one row in the junction. > **Historical note.** This rule came from duplicate link columns seen on external PostgreSQL in April 2026. NocoDB has since fixed related bugs — duplicate Link columns created by schema sync (issue 13788, in release 2026.05.2) and many-to-many links on the same table (issue 13349, in release 2026.06.0). The rule stays as good practice because one relation per pair is clearer in both UIs, but the duplicate-column symptom may no longer appear on current releases. Re-test on your target version before relying on either behaviour. ## Relation Checklist - **One relation type per table pair** — FK or junction, not both - FK column type matches parent PK type (`int4` for `serial`, `int8` for `bigserial`) - FK constraint uses `ON DELETE NO ACTION ON UPDATE NO ACTION` - Index created on every FK column - One-to-One has `UNIQUE` on FK column - Junction tables use composite PK from both FK columns - Junction tables have FK constraints on both columns - Junction tables have indexes on both FK columns
View on GitHub