# Records: Link & Unlink

> Part of the NocoDB documentation (Workflows > Scripts > Examples > Generic). Index of all pages: https://nocodb.com/llms.txt. Any docs page is available as Markdown by adding `.md` to its URL.

URL: https://nocodb.com/docs/workflows/scripts/examples/demo/records-link
Last updated: 2026-10-03

A NocoDB script that creates relational fields and links and unlinks records between tables.

This script shows how to create and manage relational fields in NocoDB. It links and unlinks records between two tables with the `LinkToAnotherRecord` field type.

```javascript
// NocoDB Script: Relational Fields Demo (LinkToAnotherRecord, Lookup, Rollup)

const projectsTableName = 'Projects';
const tasksTableName = 'Tasks';

// ---------------------
// Ensure Projects Table
// ---------------------
let projectsTable = base.getTable(projectsTableName);
if (!projectsTable) {
  projectsTable = await base.createTableAsync(projectsTableName, [
    { name: 'Project Name', type: "SingleLineText" },
    { name: 'Client', type: "SingleLineText" }
  ]);
  output.text(`✅ Created table "${projectsTableName}"`);
}

// ---------------------
// Ensure Tasks Table
// ---------------------
let tasksTable = base.getTable(tasksTableName);
if (!tasksTable) {
  tasksTable = await base.createTableAsync(tasksTableName, [
    { name: 'Task Name', type: "SingleLineText" },
    {
      name: 'Status',
      type: "SingleSelect",
      options: {
        choices: [
          { title: 'To Do', color: '#6c757d' },
          { title: 'In Progress', color: '#007bff' },
          { title: 'Done', color: '#28a745' }
        ]
      }
    },
    { name: 'Hours', type: "Number" }
  ]);
  output.text(`✅ Created table "${tasksTableName}"`);
}

// ---------------------
// Create LinkToAnotherRecord: Tasks → Projects
// ---------------------
if (!tasksTable.getField('Project')) {
  await tasksTable.createFieldAsync({
    name: 'Project',
    type: "LinkToAnotherRecord",
    options: {
      relation_type: 'mm',
      related_table_id: projectsTable.id
    }
  });
  output.text(`🔗 Created "Project" field in "Tasks" as LinkToAnotherRecord to "Projects"`);
}

// ---------------------
// Demo: Insert records, link, unlink
// ---------------------

// Step 1: Insert sample projects if empty
const projectRecords = await projectsTable.selectRecordsAsync();
if (projectRecords.records.length === 0) {
  await projectsTable.createRecordsAsync([
    { fields: { 'Project Name': 'Website Redesign', 'Client': 'Acme Corp' }},
    { fields: { 'Project Name': 'Mobile App Launch', 'Client': 'Globex Inc' }}
  ]);
  output.text(`➕ Inserted sample records in "${projectsTableName}"`);
}

// Step 2: Insert sample tasks if empty
const taskRecords = await tasksTable.selectRecordsAsync();
if (taskRecords.records.length === 0) {
  await tasksTable.createRecordsAsync([
    { fields: { 'Task Name': 'Design UI', 'Status': 'To Do', 'Hours': 10 }},
    { fields: { 'Task Name': 'Build Backend', 'Status': 'In Progress', 'Hours': 20 }},
    { fields: { 'Task Name': 'Write Docs', 'Status': 'Done', 'Hours': 5 }}
  ]);
  output.text(`➕ Inserted sample records in "${tasksTableName}"`);
}

// Refresh records
const projects = await projectsTable.selectRecordsAsync();
const tasks = await tasksTable.selectRecordsAsync();

const projectIds = projects.records.map(r => r.id);
const taskIds = tasks.records.map(r => r.id);
const projectField = tasksTable.getField('Project');

// Step 3: Link tasks to projects
const linkUpdates = tasks.records.map((task, i) => ({
  id: task.id,
  fields: {
    [projectField.id]: [{ id: projectIds[i % projectIds.length] }]
  }
}));

await tasksTable.updateRecordsAsync(linkUpdates);
output.text('🔗 Linked each task to a project');

// Step 4: Display linked state
const linkedTasks = await tasksTable.selectRecordsAsync();
output.text('📌 Tasks after linking to projects:');
output.table(linkedTasks.records.map(r => ({
  Task: r.getCellValue('Task Name'),
  Project: r.getCellValue('Project')?.records[0]?.name || ''
})));

// Step 5: Unlink all tasks
const unlinkUpdates = linkedTasks.records.map(r => ({
  id: r.id,
  fields: {
    [projectField.id]: [] // remove link
  }
}));

await tasksTable.updateRecordsAsync(unlinkUpdates);
output.text('❌ Unlinked all tasks from projects');

// Step 6: Display unlinked state
const finalTasks = await tasksTable.selectRecordsAsync();
output.text('📌 Tasks after unlinking:');
output.table(finalTasks.records.map(r => ({
  Task: r.getCellValue('Task Name'),
  Project: r.getCellValue('Project')?.records[0]?.name || '(none)'
})));

```

---

## Related pages

- [Table Operations](https://nocodb.com/docs/workflows/scripts/examples/demo/table-operations.md): A NocoDB script that checks if a table exists, creates a table and lists all tables in the base.
- [Fields: Text-Based](https://nocodb.com/docs/workflows/scripts/examples/demo/field-text-based.md): A NocoDB script that creates and manages text-based fields.
- [Fields: Numerical](https://nocodb.com/docs/workflows/scripts/examples/demo/field-number-based.md): A NocoDB script that creates numerical fields for quantities, percentages, currency and more.
- [Fields: Date & Time](https://nocodb.com/docs/workflows/scripts/examples/demo/field-date-time.md): A NocoDB script that creates Date, Time, DateTime and Duration fields.
- [Fields: Select](https://nocodb.com/docs/workflows/scripts/examples/demo/field-select-based.md): A NocoDB script that creates single select and multi select fields to categorize data.
- [Fields: System](https://nocodb.com/docs/workflows/scripts/examples/demo/field-system.md): A NocoDB script that creates system fields to capture audit information automatically.
- [Fields: User & Collaborator](https://nocodb.com/docs/workflows/scripts/examples/demo/field-user.md): A NocoDB script that creates a User field to assign or reference users in records.
- [Fields: Virtual & Formula](https://nocodb.com/docs/workflows/scripts/examples/demo/field-virtual.md): A NocoDB script that adds calculated or derived data with virtual fields.
