Quickbase Tutorials

Code Pages Developer Lab

A hands-on Quickbase API curriculum built from working Code Page examples.

Lesson 1B

Code Page Here - PeoplePage_rest.html

Read Records with the REST API

Query the same Quickbase People table using the modern JSON REST API while keeping the table, fields, sorting, and rendered result equivalent to Lesson 1A.

CompleteREST APIJSONTemporary Authorization

Lesson Objective

Lesson 1A established how Quickbase tables and fields are represented in API code. Lesson 1B deliberately keeps those same Quickbase objects and changes only the API architecture.

The important question is no longer simply, “How do I query the People table?”

The question is: how does the modern Quickbase REST API represent the same table, fields, authorization, query, records, and values?

A Controlled Comparison

Lesson 1B uses the exact same Quickbase People table as Lesson 1A. That gives us a controlled comparison between the two APIs.

ObjectQuickbase IdentityValue
Quickbase realmRealm hostnamewindow.location.hostname
People tableTable DBIDTABLE_ID_HERE
Record ID#Field ID3
NameField ID6
AgeField ID7
Favorite ColorField ID8

1. Identify the Quickbase Realm

const REALM = window.location.hostname;

This is our first new Quickbase reference compared with Lesson 1A. REST requests include the Quickbase realm hostname in theQB-Realm-Hostname header.

Because the working HTML file is a Quickbase Code Page,window.location.hostnameretrieves the hostname of the Quickbase realm currently hosting that page.

2. The Table DBID and Field IDs Do Not Change

const TABLE_DBID = "TABLE_ID_HERE";

const FIELD_IDS = {
  recordId: 3,
  name: 6,
  age: 7,
  favoriteColor: 8,
};

XML and REST are different interfaces to Quickbase, but the underlying Quickbase objects have not changed.

People
↓
TABLE_ID_HERE
Name
↓
Field ID 6

This is one of the most important observations in the first two lessons: the API changed; the Quickbase identities did not.

3. Obtain Temporary REST Authorization

This is the largest architectural difference from our XML Code Page. Before querying records, the REST example obtains temporary authorization from Quickbase.

const response = await fetch(
  `https://api.quickbase.com/v1/auth/temporary/${TABLE_DBID}`,
  {
    method: "GET",
    credentials: "include",
    headers: {
      "QB-Realm-Hostname": REALM,
      "QB-App-Token": APP_TOKEN,
    },
  },
);

Notice how several Quickbase references converge in this one request.

CodeQuickbase Purpose
/auth/temporary/${TABLE_DBID}Requests temporary authorization associated with the Quickbase table.
credentials: "include"Includes the current signed-in Quickbase browser session.
QB-Realm-HostnameIdentifies the Quickbase realm.
QB-App-TokenSupplies the Application Token required by this application.

4. Read the Temporary Authorization

const data = await response.json();

if (!data.temporaryAuthorization) {
  throw new Error(
    "Quickbase did not return temporaryAuthorization."
  );
}

return data.temporaryAuthorization;

The temporary authorization returned by Quickbase becomes the credential for the next REST request.

Security Note

During our debugging we temporarily logged the returned token so we could prove that authorization worked. That was useful as a checkpoint, but the completed instructional code should never log the token.

5. Build the Quickbase JSON Query

const query = {
  from: TABLE_DBID,

  select: [
    FIELD_IDS.recordId,
    FIELD_IDS.name,
    FIELD_IDS.age,
    FIELD_IDS.favoriteColor,
  ],

  sortBy: [
    {
      fieldId: FIELD_IDS.name,
      order: "ASC",
    },
  ],
};

This JSON object performs essentially the same job as several XML elements from Lesson 1A.

from

Identifies the Quickbase table DBID.

select

Lists the Quickbase Field IDs to return.

sortBy

Specifies the Field ID and sort direction.

XML and REST: Same Request Intent

Lesson 1A — XML

<clist>3.6.7.8</clist>
<slist>6</slist>
<options>sortorder-A</options>

Lesson 1B — REST

select: [3, 6, 7, 8],

sortBy: [
  {
    fieldId: 6,
    order: "ASC",
  },
]

6. Send the Records Query

const response = await fetch(
  "https://api.quickbase.com/v1/records/query",
  {
    method: "POST",

    headers: {
      "QB-Realm-Hostname": REALM,

      Authorization:
        `QB-TEMP-TOKEN ${temporaryToken}`,

      "Content-Type": "application/json",
    },

    body: JSON.stringify(query),
  },
);

REST changes how the API operation itself is selected.

The XML API uses:

QUICKBASE-ACTION: API_DoQuery

REST instead uses a purpose-specific endpoint:

POST https://api.quickbase.com/v1/records/query

7. Quickbase Returns JSON Records

The returned record structure is fundamentally different from structured XML.

XML

<record>
  <f id="3">1</f>
  <f id="6">Alice</f>
  <f id="7">32</f>
  <f id="8">Blue</f>
</record>

REST JSON

{
  "3": { "value": 1 },
  "6": { "value": "Alice" },
  "7": { "value": 32 },
  "8": { "value": "Blue" }
}

The representation changed, but Field ID 6 still means the Name field.

8. Read a REST Field Value

function getFieldValue(record, fieldId) {
  return record[String(fieldId)]?.value ?? "";
}

For the Name field:

FIELD_IDS.name
↓
6
↓
String(6)
↓
record["6"]
↓
{ value: "Alice" }
↓
Alice

9. The Record Collection Lives in data.data

const data = await response.json();
                renderRecords(data.data);

Our debugging checkpoints were useful here. The REST request had already succeeded before the table rendered correctly. Inspecting the complete response and then the first record established where the records actually lived and what each record looked like.

REST response object
↓
data
↓
data.data
↓
Quickbase records[]

10. XML vs REST Side by Side

ConceptLesson 1A XMLLesson 1B REST
API styleLegacy XML APIRESTful API
Query endpoint/db/{TABLE_DBID}https://api.quickbase.com/v1/records/query
API operationQUICKBASE-ACTION: API_DoQueryEndpoint determines the operation
Request data<qdbapi> XMLJSON object
Requested fields<clist>3.6.7.8</clist>select: [3, 6, 7, 8]
Sorting<slist>6</slist> + sortorder-AsortBy: [{ fieldId: 6, order: "ASC" }]
Response parsingresponse.text() + DOMParserresponse.json()
Returned field<f id="6">Alice</f>"6": { "value": "Alice" }
Read field valuequerySelector('f[id="6"]')record["6"].value

11. Our XML vs REST Timing Experiment

We added the same overall timer to both Code Pages:

const startTime = performance.now();

// Quickbase request and rendering

const endTime = performance.now();

const renderTime = endTime - startTime;

In our tests, the XML Code Page was running more than twice as fast as the REST version. That is a valid observation, but it does not prove that XML parsing is faster than JSON parsing.

Lesson 1A XML

POST table query
↓
XML response
↓
Parse
↓
Render

Lesson 1B REST

GET temporary authorization
↓
Receive temporary token
↓
POST records query
↓
JSON response
↓
Parse
↓
Render

What We Can Actually Conclude

For this Code Page implementation and very small People dataset, obtaining a new temporary REST authorization before every records query adds another network operation. Therefore our total timer is measuring more work on the REST side than on the XML side.

With only six records, network and authorization latency can easily dominate the cost of parsing XML or JSON. Our training log correctly identified this distinction before drawing conclusions about the APIs themselves.

12. The Complete REST Request Path

Quickbase People Code Page
↓
REALM = window.location.hostname
TABLE_DBID = TABLE_ID_HERE
↓
Temporary Authorization Request
GET /v1/auth/temporary/TABLE_ID_HERE
credentials: include
QB-Realm-Hostname
QB-App-Token
↓
temporaryAuthorization
↓
Records Query
POST /v1/records/query
Authorization: QB-TEMP-TOKEN ...
Content-Type: application/json
↓
JSON Query
from: TABLE_ID_HERE
select: [3, 6, 7, 8]
sortBy: Field ID 6 ASC
↓
QUICKBASE
↓
JSON Response
↓
data.data[]
↓
record["6"].value
↓
JavaScript
↓
HTML Table

Commented Quickbase Source

This source concentrates the comments on Quickbase behavior rather than ordinary JavaScript and DOM manipulation. The Application Token has intentionally been replaced with a placeholder.

/*
 * ============================================================
 * QUICKBASE REALM
 * ============================================================
 *
 * A Quickbase REST request must identify the Quickbase realm.
 *
 * Because this Code Page is running inside Quickbase,
 * window.location.hostname gives us the current realm hostname.
 */
const REALM = window.location.hostname;


/*
 * ============================================================
 * QUICKBASE TABLE DBID
 * ============================================================
 *
 * The visible table name is "People", but REST API requests
 * identify the table using its DBID.
 */
const TABLE_DBID = "TABLE_ID_HERE";


/*
 * ============================================================
 * QUICKBASE APPLICATION TOKEN
 * ============================================================
 *
 * This application requires an Application Token.
 *
 * Do not place a Quickbase User Token in browser JavaScript.
 */
const APP_TOKEN = "YOUR_APPLICATION_TOKEN";


/*
 * ============================================================
 * QUICKBASE FIELD IDs
 * ============================================================
 *
 * The JavaScript property names are chosen by us.
 * The numbers are the actual Quickbase Field IDs.
 */
const FIELD_IDS = {
  recordId: 3,
  name: 6,
  age: 7,
  favoriteColor: 8,
};


/*
 * ============================================================
 * TEMPORARY REST AUTHORIZATION
 * ============================================================
 *
 * Unlike our XML example, the REST Code Page first obtains
 * temporary REST authorization.
 *
 * TABLE_DBID is included in the authorization endpoint.
 */
async function getTemporaryToken() {
  const response = await fetch(
    `https://api.quickbase.com/v1/auth/temporary/${TABLE_DBID}`,
    {
      method: "GET",

      /*
       * Include the signed-in Quickbase browser session.
       */
      credentials: "include",

      headers: {
        /*
         * Tell Quickbase which realm this request belongs to.
         */
        "QB-Realm-Hostname": REALM,

        /*
         * Supply the Application Token required by this app.
         */
        "QB-App-Token": APP_TOKEN,
      },
    },
  );

  /*
   * REST responses are JSON.
   */
  const data = await response.json();

  if (!response.ok) {
    throw new Error(
      `HTTP ${response.status}: ${JSON.stringify(data)}`
    );
  }

  /*
   * The successful response must contain the temporary
   * authorization value needed for the REST query.
   */
  if (!data.temporaryAuthorization) {
    throw new Error(
      "Quickbase did not return temporaryAuthorization."
    );
  }

  /*
   * Never console.log() this token in finished code.
   */
  return data.temporaryAuthorization;
}


/*
 * ============================================================
 * READING A QUICKBASE REST FIELD
 * ============================================================
 *
 * A REST record uses the Quickbase Field ID as a JSON property.
 *
 * Simplified example:
 *
 * {
 *   "3": { "value": 1 },
 *   "6": { "value": "Alice" },
 *   "7": { "value": 32 },
 *   "8": { "value": "Blue" }
 * }
 *
 * If fieldId is 6:
 *
 *     record[String(fieldId)]
 *
 * becomes:
 *
 *     record["6"]
 *
 * and .value returns the Quickbase field value.
 */
function getFieldValue(record, fieldId) {
  return record[String(fieldId)]?.value ?? "";
}


async function loadPeople() {
  /*
   * Benchmark starts before REST authorization so the measured
   * total includes the complete Code Page REST process.
   */
  const startTime = performance.now();

  const status = document.getElementById("status");

  try {
    /*
     * ==========================================================
     * STEP 1 — GET TEMPORARY QUICKBASE REST AUTHORIZATION
     * ==========================================================
     */
    const temporaryToken = await getTemporaryToken();


    /*
     * ==========================================================
     * STEP 2 — BUILD THE QUICKBASE REST QUERY
     * ==========================================================
     *
     * from:
     *   identifies the Quickbase table.
     *
     * select:
     *   specifies which Field IDs should be returned.
     *
     * sortBy:
     *   tells Quickbase to sort using Field ID 6 (Name)
     *   in ascending order.
     */
    const query = {
      from: TABLE_DBID,

      select: [
        FIELD_IDS.recordId,
        FIELD_IDS.name,
        FIELD_IDS.age,
        FIELD_IDS.favoriteColor,
      ],

      sortBy: [
        {
          fieldId: FIELD_IDS.name,
          order: "ASC",
        },
      ],
    };


    /*
     * ==========================================================
     * STEP 3 — QUERY QUICKBASE RECORDS
     * ==========================================================
     */
    const response = await fetch(
      "https://api.quickbase.com/v1/records/query",
      {
        method: "POST",

        headers: {
          /*
           * Identify the Quickbase realm.
           */
          "QB-Realm-Hostname": REALM,

          /*
           * Authorize this REST request using the temporary
           * token acquired in Step 1.
           */
          Authorization:
            `QB-TEMP-TOKEN ${temporaryToken}`,

          /*
           * The request body contains JSON.
           */
          "Content-Type": "application/json",
        },

        /*
         * Convert the JavaScript query object into JSON text.
         */
        body: JSON.stringify(query),
      },
    );


    /*
     * Convert the REST JSON response into JavaScript objects.
     */
    const data = await response.json();

    if (!response.ok) {
      throw new Error(
        `HTTP ${response.status}: ${JSON.stringify(data)}`
      );
    }


    /*
     * ==========================================================
     * QUICKBASE REST RECORD COLLECTION
     * ==========================================================
     *
     * Returned records are located in:
     *
     *     data.data
     *
     * Each item represents one Quickbase record.
     */
    renderRecords(data.data);


    /*
     * Stop the timer after all records have been rendered.
     */
    const endTime = performance.now();

    const renderTime = endTime - startTime;

    status.classList.remove("error");

    status.textContent =
      `REST query returned ${data.data.length} record(s) in ${renderTime.toFixed(2)} ms.`;

  } catch (error) {
    console.error(error);

    status.classList.add("error");

    status.textContent =
      `REST query failed: ${error.message}`;
  }
}

document.addEventListener(
  "DOMContentLoaded",
  loadPeople
);

Lesson 1B Complete

You should now be able to identify the Quickbase realm, table DBID, Field IDs, Application Token, temporary authorization request, temporary REST token, JSON records query, REST response collection, and individual field values in a Quickbase REST Code Page.

More importantly, you should be able to look at Lessons 1A and 1B and recognize which pieces represent the same Quickbase objects even though XML and REST express them differently.