beekeeper-studio/beekeeper-studio · error

SurrealDB does not use traditional foreign key constraints.

Error message

SurrealDB does not use traditional foreign key constraints. Modify field definitions instead.

What it means

SurrealDB has no traditional FOREIGN KEY constraints to drop; relationships are expressed as record<...> typed fields. SurrealDBChangeBuilder.dropRelations() always throws, directing callers to change the field definitions instead.

Source

Thrown at apps/studio/src/shared/lib/sql/change_builder/SurrealDBChangeBuilder.ts:127

    if (!drops?.length) return null;

    return drops.map(spec => 
      `REMOVE INDEX ${this.wrapIdentifier(spec.name)} ON TABLE ${this.wrapIdentifier(this.table)}`
    ).join('; ');
  }

  // SurrealDB uses record links instead of foreign keys
  singleRelation(spec: CreateRelationSpec): string {
    const fromColumn = this.wrapIdentifier(spec.fromColumn);
    const toTable = this.wrapIdentifier(spec.toTable);
    
    // In SurrealDB, we define a field that references another table
    return `DEFINE FIELD ${fromColumn} ON TABLE ${this.wrapIdentifier(this.table)} TYPE record<${toTable}>`;
  }

  // SurrealDB doesn't have traditional foreign key constraints to drop
  dropRelations(_names: string[]): string | null {
    throw new Error('SurrealDB does not use traditional foreign key constraints. Modify field definitions instead.');
  }

  // Override the main alterTable method to handle SurrealDB's limitations
  alterTable(spec: AlterTableSpec): string {
    const statements: string[] = [];

    // Handle column additions
    if (spec.adds?.length) {
      statements.push(...this.addColumns(spec.adds));
    }

    // Handle column drops
    if (spec.drops?.length) {
      statements.push(...this.dropColumns(spec.drops));
    }

    // Handle column alterations (type changes, defaults)
    if (spec.alterations?.length) {

View on GitHub (pinned to 4e3e03e322)

Solutions

  1. Redefine the referencing field's type (e.g. 'DEFINE FIELD other_id ON TABLE t TYPE string' or REMOVE FIELD) instead of dropping a constraint.
  2. Guard with a dialect check and skip dropRelations for SurrealDB connections.
  3. If the goal is removing a relationship entirely, run 'REMOVE FIELD <field> ON TABLE <table>'.

Example fix

// before
builder.dropRelations(['fk_post_user'])
// after
"REMOVE FIELD user ON TABLE post;" // or redefine field TYPE
Defensive patterns

Strategy: fallback

Validate before calling

if (dialect === 'surrealdb') {
  // no FK constraints: redefine or remove the referencing field instead
  return names.map(f => `REMOVE FIELD ${f} ON TABLE ${table}`).join('; ')
}
builder.dropRelations(names)

Type guard

const supportsForeignKeyDdl = (dialect: string) => dialect !== 'surrealdb'

Try / catch

try {
  stmt = builder.dropRelations(names)
} catch (e) {
  if (e.message.includes('foreign key constraints')) {
    stmt = redefineOrRemoveReferenceFields(names) // REMOVE FIELD or change TYPE record<T>
  } else throw e
}

Prevention

When it happens

Trigger: Any call to dropRelations(names) — e.g. an alter-table flow that removes FK constraints from a SurrealDB table, or a generic 'drop constraint' action in the schema editor.

Common situations: Running shared constraint-removal code paths written for SQL databases against SurrealDB; porting migration scripts with 'DROP CONSTRAINT' statements; cleaning up FKs before a table drop.

Related errors


AI-assisted analysis of beekeeper-studio/beekeeper-studio@4e3e03e322 (2026-08-31). Data as JSON: /api/errors/06119dd33dece368. Report an issue: GitHub.