Skip to content

A TypeScript/JavaScript library for creating and querying Tables on the Tableland network.

License

Notifications You must be signed in to change notification settings

Chainguys/js-tableland

 
 

Repository files navigation

@textile/tableland

Lint and test GitHub package.json version Release standard-readme compliant

A TypeScript/JavaScript library for creating and querying Tables on the Tableland network.

Table of Contents

Background

The Tableland project provides a zero-config Typescript/Javascript SDK that make it easy to interact with the Tableland network from Ethereum-based applications. The @textile/tableland SDK should feel comfortable to developers already familiar with the ethersjs Javascript library. The Tableland SDK provides a small but powerful API surface that integrates nicely with existing ETH development best practices.

Simply import the library, connect to the Tableland network, and you are ready to start creating and updating tables.

Note: Interested in supporting additional chains and ecosystems? Create an Issue and let us know!

Install

Installation is easy using npm or yarn. An ES bundle is also available for those operating purely in a browser environnement.

npm i @textile/tableland

Note: Not seeing the build type you need for your project or idea? Let us know, we're happy to work with you to improve the SDK usability!

Usage

Most common Tableland usage patterns will follow something like the following. In general, you'll need to connect, create, mutate, and query your tables. In that order :)

import { connect } from "@textile/tableland";

const connection = await connect({ network: "testnet" });

let id = connection.create(
  `CREATE TABLE table (id int primary key, name text, primary key (id))`
);
let res = await connection.query(`INSERT (firstname) VALUES ('Murray' INTO ${id})`);
res = await connection.query(`SELECT * FROM ${id}`);

API

Full library documentation available on GitHub!

Connecting to Tableland

The @textile/tableland library includes functions for connecting to remote clients, creating and mutating tables, querying existing tables, and listing all user tables. These top level exports are available as individual function.

The connect function can be used to connect to a remote Tableland host, which is required to interact with the Tableland network. If information about a known Tableland validator is available, this can be specified as the host parameter in the options argument to the connect function.

Upon calling connect, the user will be prompted to sign a self-signed JSON web token. This token is used to verify ownership of the given Ethereum address, and to avoid the user having to sign subsequent Tableland transactions/method calls.

import { connect } from "@textile/tableland";

// By default, connect uses the tableland testnet validator
const connection = await connect({ network: 'testnet', host: "http://testnet.tableland.network" });

Creating Tables

Like most relational database systems, Tableland requires the user to create tables for storing, querying, and relating data. This is done via the create function. The create function takes a plain SQL statement string. All tables require a primary key field called id to be valid. Most valid SQL constraints are supported, and the following data types are currently supported:

  • int2, int4, int8, serial, bigserial
  • text, uri, varchar, bpchar
  • date, timestamptz
  • bool, float4, float8, numeric
  • uuid, json

Note: The above list is tentative and incomplete; the accepted types are still not well defined at the spec level.

// Assumes a connection has already been established as above

const { name, id } = await connection.create(
  "CREATE TABLE table (name text, id int, primary key (id));"
);

Creating a table also generates at unique table identifier (uuid). The table id is globally unique and can be used to reference the table for queries and updates after it has been created.

Currently, tables created by a given Ethereum address are owned by that address. For the time being, this means that the user must be signed in with the address used to create the table in order to mutate the table. However, any address can read the table at any time.

Warn: It is not advised to store sensitive or private information on the Tableland network at this time.

Listing Tables

Once tables have been created for a given address, they can be listed via the list function. This function takes no arguments and returns a list of TableMetadata objects, which contains table name, id, description, and type. The type of a table is defined by its normalized schema, whereas name and description can be specified by the caller upon creation. id is auto-generated by the create function.

// Assumes a connection has already been established as above

const tables = await connection.list();
// [
//   {
//    "id": "random-uuid-string-id",
//    "type": "hash-string-type",
//    "name": "table",
//    "description": "other information"
//   }
// ]

An application can use the list function to discover a user's tables, and determine in they are relevant for the given application.

Mutating Tables

Now that we have a table to work with, it is easy to use vanilla SQL statements to insert new rows, update existing rows, and even delete old rows. These mutating SQL statements will eventually require network fees to be paid to network validators. For the current MVP trials, they remain free. The generic query function can be used to mutate table rows. As an example, inserting new rows can be done like this:

// Assumes a connection has already been established as above

const one = await connection.query(
  `INSERT INTO ${tableId} (id, name) VALUES (0, 'Bobby Tables');`
);

const two = await connection.query(
  `INSERT INTO ${tableId} (id, name) VALUES (0, 'Bobby Tables');`
);

Note: As mentioned previously, table mutations are currently restricted to the table creator address.

This inserted row can then be removed from the table state like this:

const remove = await connection.query(`DELETE FROM ${tableId} WHERE id = 0;`);

Warn: While rows can be deleted from the table state, row information will remain in the table's history for obvious reasons.

Querying Tables

Finally, the moment we've all been waiting for; we are ready to query our table state! You already have all the tools required to get this done. Simply use the query function imported previously to query the latest table state. Currently, queries are extremely flexible in Tableland. You have most SQL query features available to craft your query, though the most common will likely be the classic SELECT * FROM pattern shown here:

const { rows, columns } = await connection.query(`SELECT * FROM ${tableId};`);

The response from a read query contains a ReadQueryResult object, with properties for columns and rows. The columns property contains an enumerated array of column ids and their corresponding ColumnDescriptor information. The rows property is an array of row-wise table data. The rows can be iterated over and used to populate UIs etc.

for (const [rowId, row] of Object.entries(rows)) {
  console.log(`row: ${rowId}`);
  for (const [colId, data] of Object.entries(row)) {
    const { name } = columns[colId];
    console.log(`  ${name}: ${data}`);
  }
}

And now you're ready to start building your next web3 experience with Tableland! If you want to dive deeper, you can check out the full API documentation here.

Feedback

Reach out with feedback and ideas:

Examples

Maintainers

@awmuncy

Contributing

PRs accepted.

To get started clone this repo, then do:

# use the latest node and npm LTS
npm install
npm run build

# see if everything is working
npm test

Small note: If editing the README, please conform to the standard-readme specification.

License

MIT AND Apache-2.0, © 2021 Textile.io

About

A TypeScript/JavaScript library for creating and querying Tables on the Tableland network.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 92.5%
  • JavaScript 7.0%
  • Shell 0.5%