A TypeScript/JavaScript library for creating and querying Tables on the Tableland network.
- @textile/tableland
- Table of Contents
- Background
- Install
- Usage
- API
- Feedback
- Examples
- Maintainers
- Contributing
- License
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!
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!
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}`);
Full library documentation available on GitHub!
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" });
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.
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.
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.
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.
Reach out with feedback and ideas:
- To-do App (source)
- Loot Extension (source)
- More on the way!
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.
MIT AND Apache-2.0, © 2021 Textile.io