Quickbase (quickbase v0.2.0)

Copy Markdown View Source

Client for the Quickbase JSON RESTful API.

Build a client with new/1, then pass it to the API functions:

client = Quickbase.new(realm: "demo.quickbase.com", token: System.fetch_env!("QB_USER_TOKEN"))

{:ok, %{"data" => rows}} =
  Quickbase.query_records(client,
    from: "bck7gp3q2",
    select: [3, 6, 7],
    where: "{'6'.EX.'Done'}"
  )

All functions return {:ok, body} on HTTP 200 or {:error, reason}, where reason is a Quickbase.Error (non-200 response) or an exception (transport error). Bang variants (query_records!/2, etc.) return the body directly and raise on failure.

Working with rows

Quickbase returns records keyed by stringified field id (%{"6" => %{"value" => "Done"}}). See value/2, label_index/1, and by_label/3 for helpers that unwrap them.

Options

new/1 accepts:

  • :realm (required) — your realm hostname, e.g. "demo.quickbase.com"
  • :token (required) — a Quickbase user token
  • :user_agent — optional User-Agent header value
  • any other option is forwarded to Req.new/1 (:plug, :finch, :receive_timeout, ...), so the client is easy to test with Req.Test and tune for production pools.

Read-only calls (query_records/2, run_report/3, list_fields/2, list_tables/2, get_table/3) are retried on transient failures via Req's built-in retry; mutations (upsert_records/3, delete_records/3, create_field/3) are not.

Summary

Functions

Unwraps the value of a field in a row by its label, using an index built with label_index/1. Returns nil when the label is unknown.

Creates a field on a table (POST /fields).

Same as create_field/3 but returns the body and raises on failure.

Deletes records matching a query (DELETE /records).

Same as delete_records/3 but returns the body and raises on failure.

Downloads a file attachment (GET /files/{tableId}/{recordId}/{fieldId}/{versionNumber}).

Gets a table's metadata (GET /tables/{tableId}).

Same as get_table/3 but returns the body and raises on failure.

Gets a temporary authorization token for an app or table, for embedding in browser code without exposing a user token (GET /auth/temporary/{dbid}).

Same as get_temp_token/2 but returns the body and raises on failure.

Builds a %{label => field id string} map from the "fields" list of a response, for use with by_label/3.

Lists the fields of a table (GET /fields).

Same as list_fields/2 but returns the body and raises on failure.

Lists the tables of an app (GET /tables).

Same as list_tables/2 but returns the body and raises on failure.

Builds a client for the given realm and user token.

Queries records in a table (POST /records/query).

Same as query_records/2 but returns the body and raises on failure.

Runs a saved report (POST /reports/{id}/run).

Same as run_report/3 but returns the body and raises on failure.

Lazily streams all records matching a query, paging through POST /records/query under the hood.

Inserts or updates records (POST /records).

Same as upsert_records/4 but returns the body and raises on failure.

Unwraps the value of a field in a row returned by query_records/2 or run_report/3. Accepts the field id as integer or string.

Types

client()

@type client() :: Req.Request.t()

response()

@type response() ::
  {:ok, map() | list()} | {:error, Quickbase.Error.t() | Exception.t()}

Functions

by_label(row, index, label)

@spec by_label(map(), map(), String.t()) :: term()

Unwraps the value of a field in a row by its label, using an index built with label_index/1. Returns nil when the label is unknown.

iex> idx = Quickbase.label_index([%{"id" => 6, "label" => "Status"}])
iex> Quickbase.by_label(%{"6" => %{"value" => "Done"}}, idx, "Status")
"Done"

create_field(client, table_id, field)

@spec create_field(client(), String.t(), map()) :: response()

Creates a field on a table (POST /fields).

Quickbase.create_field(client, "bck7gp3q2", %{"label" => "Status", "fieldType" => "text"})

create_field!(client, table_id, field)

@spec create_field!(client(), String.t(), map()) :: map()

Same as create_field/3 but returns the body and raises on failure.

delete_records(client, table_id, where)

@spec delete_records(client(), String.t(), String.t()) :: response()

Deletes records matching a query (DELETE /records).

Quickbase.delete_records(client, "bck7gp3q2", "{'3'.EX.'42'}")

delete_records!(client, table_id, where)

@spec delete_records!(client(), String.t(), String.t()) :: map()

Same as delete_records/3 but returns the body and raises on failure.

download_file(client, table_id, record_id, field_id, version_number \\ 0)

@spec download_file(
  client(),
  String.t(),
  integer() | String.t(),
  integer() | String.t(),
  integer() | String.t()
) :: {:ok, binary()} | {:error, Quickbase.Error.t() | Exception.t()}

Downloads a file attachment (GET /files/{tableId}/{recordId}/{fieldId}/{versionNumber}).

Returns the raw file bytes as the body.

download_file!(client, table_id, record_id, field_id, version_number \\ 0)

@spec download_file!(
  client(),
  String.t(),
  integer() | String.t(),
  integer() | String.t(),
  integer() | String.t()
) :: binary()

Same as download_file/5 but returns the body and raises on failure.

get_table(client, table_id, app_id)

@spec get_table(client(), String.t(), String.t()) :: response()

Gets a table's metadata (GET /tables/{tableId}).

get_table!(client, table_id, app_id)

@spec get_table!(client(), String.t(), String.t()) :: map()

Same as get_table/3 but returns the body and raises on failure.

get_temp_token(client, dbid)

@spec get_temp_token(client(), String.t()) :: response()

Gets a temporary authorization token for an app or table, for embedding in browser code without exposing a user token (GET /auth/temporary/{dbid}).

get_temp_token!(client, dbid)

@spec get_temp_token!(client(), String.t()) :: map()

Same as get_temp_token/2 but returns the body and raises on failure.

label_index(fields)

@spec label_index([map()]) :: %{optional(String.t()) => String.t()}

Builds a %{label => field id string} map from the "fields" list of a response, for use with by_label/3.

iex> Quickbase.label_index([%{"id" => 6, "label" => "Status"}])
%{"Status" => "6"}

list_fields(client, table_id)

@spec list_fields(client(), String.t()) :: response()

Lists the fields of a table (GET /fields).

list_fields!(client, table_id)

@spec list_fields!(client(), String.t()) :: list()

Same as list_fields/2 but returns the body and raises on failure.

list_tables(client, app_id)

@spec list_tables(client(), String.t()) :: response()

Lists the tables of an app (GET /tables).

list_tables!(client, app_id)

@spec list_tables!(client(), String.t()) :: list()

Same as list_tables/2 but returns the body and raises on failure.

new(opts)

@spec new(keyword()) :: client()

Builds a client for the given realm and user token.

Returns a Req.Request that the other functions in this module accept.

query_records(client, opts)

@spec query_records(
  client(),
  keyword()
) :: response()

Queries records in a table (POST /records/query).

Options

  • :from (required) — table id
  • :select — list of field ids to return
  • :where — a Quickbase query string
  • :sort_by — list of maps, e.g. [%{"fieldId" => 6, "order" => "ASC"}]
  • :group_by — list of maps, e.g. [%{"fieldId" => 6, "grouping" => "equal-values"}]
  • :skip / :top — pagination offset and page size

See stream_records/2 to page through large result sets lazily.

query_records!(client, opts)

@spec query_records!(
  client(),
  keyword()
) :: map()

Same as query_records/2 but returns the body and raises on failure.

run_report(client, report_id, table_id)

@spec run_report(client(), integer() | String.t(), String.t()) :: response()

Runs a saved report (POST /reports/{id}/run).

Quickbase.run_report(client, 7, "bck7gp3q2")

run_report!(client, report_id, table_id)

@spec run_report!(client(), integer() | String.t(), String.t()) :: map()

Same as run_report/3 but returns the body and raises on failure.

stream_records(client, opts)

@spec stream_records(
  client(),
  keyword()
) :: Enumerable.t()

Lazily streams all records matching a query, paging through POST /records/query under the hood.

Takes the same options as query_records/2; :top controls the page size (default 500) and :skip the starting offset. Emits individual row maps. Raises on request failure mid-stream.

client |> Quickbase.stream_records(from: "bck7gp3q2", select: [3]) |> Enum.count()

upsert_records(client, table_id, records, opts \\ [])

@spec upsert_records(client(), String.t(), [map()], keyword()) :: response()

Inserts or updates records (POST /records).

records is a list of row maps keyed by field id:

Quickbase.upsert_records(client, "bck7gp3q2", [
  %{"6" => %{"value" => "Task name"}, "7" => %{"value" => 12}}
])

Pass merge_field_id: to update existing records matching on that field, and fields_to_return: to get field values back in the response.

upsert_records!(client, table_id, records, opts \\ [])

@spec upsert_records!(client(), String.t(), [map()], keyword()) :: map()

Same as upsert_records/4 but returns the body and raises on failure.

value(row, fid)

@spec value(map(), integer() | String.t()) :: term()

Unwraps the value of a field in a row returned by query_records/2 or run_report/3. Accepts the field id as integer or string.

iex> Quickbase.value(%{"6" => %{"value" => "Done"}}, 6)
"Done"