<!-- Source: https://developers.memberstack.com/admin-node-package/data-tables -->
<!-- Markdown version of a Memberstack developer documentation page. Canonical HTML: https://developers.memberstack.com/admin-node-package/data-tables -->

# Data Tables

Data Tables let you store and query structured, app-scoped data from your server. The Admin Package exposes them under `memberstack.dataTables`, with methods to read table schemas and to create, query, update, and delete records.

Before You Start

-   Initialize the Admin Package with your secret key as shown in the [Quick Start](/admin-node-package/quick-start) guide
-   Create your tables and fields in the Memberstack dashboard (Data Tables) first — the Admin Package reads and writes _records_, it does not create tables or fields
-   The Admin API has full access and ignores per-table access rules (createRule/readRule/etc.)
-   Records are environment-scoped: a sandbox key (`sk_sb_…`) only sees sandbox records

## Overview

Every method takes a `table` (the table key from your dashboard, e.g. `"contacts"`, or a table id like `"tbl_…"`). Record values live under a `data` object keyed by your field keys. Responses are wrapped in `{ data }`, the same as the member methods.

| Method | Description |
| --- | --- |
| `dataTables.list()` | List all tables and their fields |
| `dataTables.get()` | Get one table by key or id |
| `dataTables.createRecord()` | Create a record |
| `dataTables.getRecord()` | Get one record by id |
| `dataTables.queryRecords()` | Query records (findMany / findUnique) |
| `dataTables.updateRecord()` | Update a record |
| `dataTables.deleteRecord()` | Delete a record |

## Field Types

Each table field has a type that determines how its value is stored and returned. Fields are defined in the dashboard (Data Tables); the type is reported on each field in `list()` / `get()`.

| Type | Description |
| --- | --- |
| TEXT | Plain text (also TEXT\_FILTERABLE, TEXT\_UNIQUE, EMAIL, URL variants) |
| NUMBER | Integer value |
| DECIMAL | Decimal value (see the note on string vs number below) |
| BOOLEAN | true / false |
| DATE | ISO date-time string |
| REFERENCE | A link to one record in another table |
| REFERENCE\_MANY | Links to many records in another table |
| MEMBER\_REFERENCE | A link to a single member |
| MEMBER\_REFERENCE\_MANY | Links to many members |

## List Data Tables

Retrieve every table in your app along with its field definitions.

list.js Copy

```js
const { data } = await memberstack.dataTables.list();

console.log(data.tables);
```

**Response:**

response.js Copy

```js
{
  data: {
    tables: [
      {
        id: "tbl_abc123",
        key: "contacts",
        name: "Contacts",
        createRule: "AUTHENTICATED",
        readRule: "PUBLIC",
        updateRule: "AUTHENTICATED_OWN",
        deleteRule: "ADMIN_ONLY",
        createdAt: "2026-01-15T10:30:00.000Z",
        updatedAt: "2026-01-15T10:30:00.000Z",
        fields: [
          {
            id: "fld_1",
            key: "name",
            name: "Name",
            type: "TEXT",
            required: true,
            defaultValue: null,
            tableOrder: 1,
            referencedTableId: null
          }
          // ...more fields
        ]
      }
      // ...more tables
    ]
  }
}
```

Show 17 more lines

## Get a Data Table

Fetch a single table (and its fields) by key or id.

get-table.js Copy

```js
const { data } = await memberstack.dataTables.get({
  table: "contacts"
});

console.log(data.key);    // "contacts"
console.log(data.fields); // [{ key: "name", type: "TEXT", ... }, ...]
```

Not Found

Requesting a table that does not exist rejects with a `404` and the message `"Data table not found"`.

## Create a Record

Create a record by passing the table and a `data` object keyed by your field keys.

create-record.js Copy

```js
const { data: record } = await memberstack.dataTables.createRecord({
  table: "contacts",
  data: {
    name: "Ada Lovelace",
    score: 9.5,
    active: true
  },
  // Optional: associate the record with a member (must exist in this app/env)
  memberId: "mem_abc123"
});

console.log(record.id);
```

**Response:**

response.js Copy

```js
{
  data: {
    id: "rec_xyz789",
    tableKey: "contacts",
    data: {
      name: "Ada Lovelace",
      score: "9.5",   // DECIMAL is a string here — see note below
      active: true
    },
    createdAt: "2026-01-15T10:30:00.000Z",
    updatedAt: "2026-01-15T10:30:00.000Z",
    internalOrder: 42
  }
}
```

Required and Optional Parameters

-   **Required:**
    
    -   `table` — the table key or id
    -   `data` — an object of field values (must be an object, not an array)
    
-   **Optional:**
    
    -   `memberId` — associate the record with a member; the member must exist in this app and environment
    

## Get a Record

Fetch a single record by id. This is a convenience wrapper around a `findUnique` query, so the record is returned under `data.record`.

get-record.js Copy

```js
const { data } = await memberstack.dataTables.getRecord({
  table: "contacts",
  recordId: "rec_xyz789"
});

console.log(data.record.data.name);  // "Ada Lovelace"
console.log(data.record.data.score); // 9.5  (a number on reads)
```

Not Found

If the record id does not exist, `getRecord()` rejects with a `404` and the message `"Record not found"`.

## Query Records

Use `queryRecords()` for filtering, sorting, pagination, relationships, and counts. It accepts a Prisma-like query with either `findMany` (many records) or `findUnique` (one record by id) — exactly one of the two.

find-many.jsfind-unique.jscount.js Copy

```js
const { data } = await memberstack.dataTables.queryRecords({
  table: "contacts",
  query: {
    findMany: {
      where: { active: { equals: true }, score: { gte: 5 } },
      orderBy: { score: "desc" },
      take: 10
    }
  }
});

console.log(data.records);    // matching records
console.log(data.pagination); // { limit, endCursor, hasMore }
```

```js
const { data } = await memberstack.dataTables.queryRecords({
  table: "contacts",
  query: {
    findUnique: { where: { id: "rec_xyz789" } }
  }
});

console.log(data.record);
```

```js
// Return only the count of matching records
const { data } = await memberstack.dataTables.queryRecords({
  table: "contacts",
  query: { findMany: { _count: true } }
});

console.log(data._count); // e.g. 42
```

Response Shape

-   `findMany` → `{ data: { records, pagination } }` (pagination is present when `take` is set)
-   `findMany` with `_count: true` → `{ data: { _count } }`
-   `findUnique` → `{ data: { record } }`

### Query Options (findMany)

| Option | Type | Description |
| --- | --- | --- |
| `where` | object | Filter conditions (see operators below) |
| `include` | object | Include related records or counts |
| `select` | object | Select specific fields (cannot use with include) |
| `orderBy` | object \| array | Sort results |
| `take` | number | Limit number of results (max 100) |
| `skip` | number | Offset pagination (max 10000, cannot use with after) |
| `after` | number \| string | Cursor pagination — pass the previous endCursor (cannot use with skip) |
| `_count` | boolean | Return only the count of matching records |

`findUnique` requires `where.id` and supports `include` / `select` only — pagination and ordering are not allowed.

### Where Operators

Filter conditions support the following operators:

| Operator | Example |
| --- | --- |
| `equals` | `{ score: { equals: 9.5 } }` |
| `not` | `{ status: { not: 'archived' } }` |
| `in` | `{ status: { in: ['active', 'pending'] } }` |
| `notIn` | `{ status: { notIn: ['deleted'] } }` |
| `lt / lte` | `{ score: { lte: 100 } }` |
| `gt / gte` | `{ score: { gte: 50 } }` |
| `contains` | `{ name: { contains: 'Ada' } }` |
| `startsWith` | `{ name: { startsWith: 'A' } }` |
| `endsWith` | `{ email: { endsWith: '@acme.com' } }` |

Combine conditions with `AND`, `OR`, and `NOT`, e.g. `where: { OR: [{ score: { gte: 90 } }, { active: { equals: true } }] }`.

## Update a Record

Update a record by id. Only the fields you include in `data` are changed.

update-record.js Copy

```js
const { data: record } = await memberstack.dataTables.updateRecord({
  table: "contacts",
  recordId: "rec_xyz789",
  data: {
    score: 8.25
  }
});

console.log(record.data.score); // "8.25"
```

Tips for updating records:

-   Pass only the fields you want to change — other fields are left untouched
-   `data` must be a non-empty object
-   For REFERENCE / MEMBER\_REFERENCE fields, use `connect` / `disconnect` (e.g. `{ author: { connect: { id: "rec_…" } } }`)

## Delete a Record

Delete a record by id. The deleted record is returned.

delete-record.js Copy

```js
const { data: record } = await memberstack.dataTables.deleteRecord({
  table: "contacts",
  recordId: "rec_xyz789"
});

console.log(`Deleted record: ${record.id}`);
```

Important

Deleting a record is permanent and cannot be undone. Implement a confirmation step and back up important data if needed.

## A Note on DECIMAL Fields

DECIMAL: string vs number

DECIMAL field values are returned as **strings** from `createRecord()`, `updateRecord()`, and `deleteRecord()`, but as **numbers** from `getRecord()` and `queryRecords()`. Coerce with `Number(record.data.score)` if you need a consistent numeric type.

## Error Handling

Errors reject with an object containing a `code` and `message`. "Not found" cases return a `404` status; validation problems return `400`.

error-handling.js Copy

```js
try {
  await memberstack.dataTables.getRecord({
    table: "contacts",
    recordId: "rec_missing"
  });
} catch (error) {
  // { code: "generic-message", message: "Record not found" }
  console.error(error.message);
}
```

| Message | When |
| --- | --- |
| Data table not found | The table key/id does not exist (404) |
| Record not found | The record id does not exist (404) |
| Member not found | createRecord memberId is not in this app/env (404) |
| data must be an object | createRecord/updateRecord data is not an object (400) |
| data cannot be empty | updateRecord data is an empty object (400) |
| Either query.findMany or query.findUnique parameter is required | queryRecords query has neither (400) |

Next Steps

[

Member Actions

](/admin-node-package/member-actions)[

Token and Webhook Verification

](/admin-node-package/verification)[

Common Use Cases

](/admin-node-package/common-use-cases)

[

Previous

Member Actions

](/admin-node-package/member-actions)[

Next

Verification

](/admin-node-package/verification)
