Quickbase Tutorials

Code Pages Developer Lab

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

Lesson 1A

Code Page Here - PeoplePage_xml.html

Read Records with the XML API

Learn how JavaScript identifies Quickbase tables, fields, API operations, authentication requirements, records, and returned field values.

CompleteLegacy XML APIAPI_DoQuery

Lesson Objective

This lesson assumes that you already understand HTML, CSS, JavaScript, the browser DOM, asynchronous functions, and fetch().

The objective is to understand the Quickbase-specific portions of the code: how a table is identified, how fields are referenced, how Quickbase knows which API operation to execute, how the signed-in user's session participates in the request, and how returned XML fields map back to Quickbase fields.

The Central Idea

Quickbase developers work with human-readable table and field names in the application interface, but API code frequently addresses those objects using persistent Quickbase identifiers.

Quickbase UIAPI IdentityValue
People tableTable DBIDTABLE ID HERE
Record ID#Field ID3
NameField ID6
AgeField ID7
Favorite ColorField ID8

1. Identify the Quickbase Table

The visible table name is People, but the XML API request targets the table through its database identifier, or DBID.

const TABLE_DBID = "TABLE ID HERE";

That value later becomes part of the Quickbase API URL:

/db/TABLEIDHERE

The URL therefore determines which Quickbase table receives the API request.

2. Identify Quickbase Fields

The JavaScript object gives readable property names to the permanent numeric Quickbase Field IDs:

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

The property name is a JavaScript label chosen by the developer. The number 6 is the Quickbase Field ID.

Therefore:

FIELD_IDS.name

evaluates to:

6

From Quickbase's perspective, Field ID 6 is the important identity.

3. Build the Quickbase Column List

The legacy XML API uses clist to determine which fields should be returned.

const fieldList = [
  FIELD_IDS.recordId,
  FIELD_IDS.name,
  FIELD_IDS.age,
  FIELD_IDS.favoriteColor,
].join(".");

The JavaScript array:

[3, 6, 7, 8]

becomes the Quickbase XML API field list:

3.6.7.8

which is inserted into:

<clist>3.6.7.8</clist>

4. Construct the XML Request

<qdbapi>
  <apptoken>YOUR_APPLICATION_TOKEN</apptoken>
  <fmt>structured</fmt>
  <clist>3.6.7.8</clist>
  <slist>6</slist>
  <options>sortorder-A</options>
</qdbapi>
XML ElementQuickbase Meaning
<qdbapi>Root element of the legacy Quickbase XML API request.
<apptoken>Supplies the Application Token required by this application.
<fmt>structured</fmt>Requests structured XML containing identifiable field elements.
<clist>Specifies which Quickbase Field IDs should be returned.
<slist>Specifies the Quickbase Field ID used for sorting.
<options>Supplies API_DoQuery behavior such as ascending sort order.

5. Tell Quickbase Which API Operation to Run

"QUICKBASE-ACTION": "API_DoQuery"

The table DBID tells Quickbase where the request should operate.

The QUICKBASE-ACTION header tells the legacy XML API what operation should be performed.

Table:
/db/TABLEIDHERE
Operation:
API_DoQuery

6. Authentication and the Signed-In Session

credentials: "include"

The Code Page is running inside Quickbase while the user is already signed in.

Including browser credentials allows the request to operate through that existing Quickbase session. Quickbase can therefore evaluate the request using the permissions of the signed-in user.

Browser Session

Identifies the signed-in Quickbase user and allows Quickbase to apply that user's permissions.

Application Token

Satisfies the additional Application Token requirement configured for this Quickbase application.

Important

An Application Token is not a User Token. Do not embed a Quickbase User Token in browser-side JavaScript.

7. Quickbase Returns Structured XML

Because the request specifies:

<fmt>structured</fmt>

returned records can be represented approximately like:

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

The numeric id attribute reconnects each returned value to the Quickbase Field ID.

<f id="6">Alice</f>
id="6"
↓
Field ID 6
↓
Name

8. Read the Returned Quickbase Fields

function getFieldValue(record, fieldId) {
  const field =
    record.querySelector(`f[id="${fieldId}"]`);

  return field ? field.textContent.trim() : "";
}

When the code later calls:

getFieldValue(record, FIELD_IDS.name)

the lookup resolves through these steps:

FIELD_IDS.name
↓
6
↓
f[id="6"]
↓
Quickbase Name field
↓
Alice

9. Quickbase API Errors Are Separate from HTTP Errors

The code first checks the HTTP response:

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

It then separately checks the Quickbase XML API response:

const errorCode =
  xml.querySelector("errcode")
    ?.textContent
    ?.trim();

Quickbase uses XML elements including:

<errcode>

Numeric Quickbase API result code.

<errtext>

Human-readable Quickbase error text.

<errdetail>

Additional Quickbase error information.

10. The Complete Request Path

People Code Page
↓
TABLE_DBID = TABLE_ID_HERE
↓
POST /db/TABLE_ID_HERE
↓
QUICKBASE-ACTION: API_DoQuery
↓
credentials: include
↓
<qdbapi>
<apptoken>
<fmt>structured</fmt>
<clist>3.6.7.8</clist>
<slist>6</slist>
↓
QUICKBASE
↓
XML Response
↓
<record>
<f id="3">
<f id="6">
<f id="7">
<f id="8">
↓
JavaScript
↓
HTML Table

Complete Lesson Source

The source below concentrates comments around the Quickbase-specific portions of the working Code Page. Ordinary HTML, CSS, and browser JavaScript are left largely unexplained.

<script>
  /*
   * ============================================================
   * QUICKBASE TABLE REFERENCE
   * ============================================================
   *
   * Every Quickbase table has a unique database identifier,
   * commonly called its DBID.
   *
   * The DBID is not the visible table name "People".
   *
   * Quickbase APIs use this identifier to determine which table
   * the request should operate against.
   *
   * This value later becomes part of the API URL:
   *
   *     /db/DBTABLEID
   */
  const TABLE_DBID = "TABLE ID HERE";


  /*
   * ============================================================
   * QUICKBASE APPLICATION TOKEN
   * ============================================================
   *
   * This application is configured to require an Application
   * Token for legacy XML API requests.
   *
   * The Application Token is not a User Token.
   *
   * The Code Page runs while the user is already signed into
   * Quickbase. The browser session identifies the user.
   *
   * The Application Token satisfies the application's additional
   * API token requirement.
   */
  const APP_TOKEN = "YOUR_APPLICATION_TOKEN";


  /*
   * ============================================================
   * QUICKBASE FIELD IDs
   * ============================================================
   *
   * Quickbase APIs identify fields by numeric Field ID.
   *
   * These JavaScript property names make the code readable,
   * while the numeric values are the actual Quickbase references.
   */
  const FIELD_IDS = {
    recordId: 3,
    name: 6,
    age: 7,
    favoriteColor: 8,
  };


  /*
   * ============================================================
   * READING A QUICKBASE FIELD FROM STRUCTURED XML
   * ============================================================
   *
   * With <fmt>structured</fmt>, Quickbase returns fields similar
   * to:
   *
   *     <f id="6">Alice</f>
   *
   * Therefore f[id="6"] means:
   *
   *     Find Quickbase Field ID 6 in this record.
   */
  function getFieldValue(record, fieldId) {
    const field =
      record.querySelector(`f[id="${fieldId}"]`);

    return field ? field.textContent.trim() : "";
  }


  function createCell(value) {
    const cell = document.createElement("td");
    cell.textContent = value;
    return cell;
  }


  function createColorCell(colorName) {
    const cell = document.createElement("td");

    const wrapper = document.createElement("span");
    wrapper.className = "color-value";

    const swatch = document.createElement("span");
    swatch.className = "color-swatch";
    swatch.style.backgroundColor =
      colorName || "transparent";

    const label = document.createElement("span");
    label.textContent = colorName;

    wrapper.appendChild(swatch);
    wrapper.appendChild(label);

    cell.appendChild(wrapper);

    return cell;
  }


  /*
   * Each item in "records" represents one Quickbase
   * <record> element.
   *
   * FIELD_IDS.name evaluates to 6.
   *
   * That causes getFieldValue() to locate:
   *
   *     <f id="6">
   *
   * inside the record.
   */
  function renderRecords(records) {
    const tableBody =
      document.getElementById("peopleRows");

    tableBody.replaceChildren();

    for (const record of records) {
      const recordId =
        getFieldValue(record, FIELD_IDS.recordId);

      const name =
        getFieldValue(record, FIELD_IDS.name);

      const age =
        getFieldValue(record, FIELD_IDS.age);

      const favoriteColor =
        getFieldValue(
          record,
          FIELD_IDS.favoriteColor
        );

      const row =
        document.createElement("tr");

      row.appendChild(createCell(recordId));
      row.appendChild(createCell(name));
      row.appendChild(createCell(age));
      row.appendChild(
        createColorCell(favoriteColor)
      );

      tableBody.appendChild(row);
    }
  }


  async function loadPeople() {
    const status =
      document.getElementById("status");

    try {

      /*
       * ==========================================================
       * QUICKBASE CLIST
       * ==========================================================
       *
       * clist tells API_DoQuery which fields should be returned.
       *
       * Quickbase expects legacy XML API field IDs separated
       * by periods.
       *
       * [3, 6, 7, 8]
       *
       * becomes:
       *
       * 3.6.7.8
       */
      const fieldList = [
        FIELD_IDS.recordId,
        FIELD_IDS.name,
        FIELD_IDS.age,
        FIELD_IDS.favoriteColor,
      ].join(".");


      /*
       * ==========================================================
       * QUICKBASE XML REQUEST BODY
       * ==========================================================
       */
      const requestBody = `
        <qdbapi>

          <apptoken>${APP_TOKEN}</apptoken>

          <fmt>structured</fmt>

          <clist>${fieldList}</clist>

          <slist>${FIELD_IDS.name}</slist>

          <options>sortorder-A</options>

        </qdbapi>
      `;


      /*
       * ==========================================================
       * QUICKBASE XML API REQUEST
       * ==========================================================
       *
       * TABLE_DBID determines the target table.
       *
       * The URL becomes:
       *
       *     /db/tableid
       */
      const response =
        await fetch(`/db/${TABLE_DBID}`, {

          method: "POST",

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

          headers: {

            /*
             * The body contains XML.
             */
            "Content-Type": "application/xml",

            /*
             * Tell the legacy Quickbase API which operation
             * should process this request.
             */
            "QUICKBASE-ACTION": "API_DoQuery",
          },

          body: requestBody,
        });


      const responseText =
        await response.text();


      /*
       * HTTP errors and Quickbase API errors are
       * different layers.
       */
      if (!response.ok) {
        throw new Error(
          `HTTP ${response.status}: ${responseText}`
        );
      }


      const parser = new DOMParser();

      const xml =
        parser.parseFromString(
          responseText,
          "application/xml"
        );


      const parserError =
        xml.querySelector("parsererror");

      if (parserError) {
        throw new Error(
          "Quickbase returned data that could not be parsed as XML."
        );
      }


      /*
       * ==========================================================
       * QUICKBASE API ERROR RESPONSE
       * ==========================================================
       *
       * Quickbase can return:
       *
       * <errcode>
       * <errtext>
       * <errdetail>
       *
       * errcode 0 means the Quickbase API operation succeeded.
       */
      const errorCode =
        xml.querySelector("errcode")
          ?.textContent
          ?.trim();

      if (errorCode && errorCode !== "0") {

        const errorText =
          xml.querySelector("errtext")
            ?.textContent
            ?.trim() ||
          "Unknown Quickbase API error";

        const errorDetail =
          xml.querySelector("errdetail")
            ?.textContent
            ?.trim() ||
          "";

        throw new Error(
          `Quickbase error ${errorCode}: ${errorText} ${errorDetail}`
        );
      }


      /*
       * ==========================================================
       * QUICKBASE RECORDS
       * ==========================================================
       *
       * Structured XML returns each Quickbase record as:
       *
       * <record>
       *   <f id="3">1</f>
       *   <f id="6">Alice</f>
       *   <f id="7">32</f>
       *   <f id="8">Blue</f>
       * </record>
       */
      const records = [
        ...xml.querySelectorAll("record"),
      ];


      renderRecords(records);


      status.classList.remove("error");

      status.textContent =
        `${records.length} record(s) loaded from Quickbase.`;

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

      status.classList.add("error");

      status.textContent =
        `Unable to load records: ${error.message}`;
    }
  }


  document.addEventListener(
    "DOMContentLoaded",
    loadPeople
  );
</script>

Lesson 1A Complete

At this point you should be able to identify how a Quickbase table, field, API operation, application token, signed-in session, record, and returned field value are represented in the XML API code.