Tableland Validator APIs have some important changes that developers should be aware of, including new REST APIs and parameter changes.
Less is more, especially when it comes to APIs
Tableland Validator APIs have some important changes that developers should be aware of.
By @Carson Farmer
One of the really nice things about having “simplicity” as a key performance indicator on a protocol like Tableland (which we do) is that often, things feel more powerful the more you take away. It also means that we are more likely to see code changes that remove or reduce complexity, rather than the other way around. This is actually not always easy, as studies have shown (here’s a Scientific American summary of that paper). This is why I am so pleased to share our latest Validator API updates… we’ve really managed to think outside the box on this and arrive at an API footprint that is hyper-focused and slimmed down to the bear essentials.
We actually took a subtractive approach to API design, and the more we took away, the more powerful the whole system felt.
This is good news for Validators (fewer APIs and API calls means less “work” on the part of validators), and for developers; and in this post, I’ll tell you why.
I thought rather than just linking everyone to our new OpenAPI Validator API Specification (which you should certainly check out), I’d do a little walkthrough of all the changes. From what’s gone, to what’s changed, to what we added.
But first, here’s a quick summary of our approach to the new API designs:
TL;DR
- We switched to a global API version prefix, which will strictly follow semver best practices.
- We dropped JSON RPC in favor of a more RESTful approach to API design.
- API paths are prefixed with
/api/v{majorVersion}
, ignoring the minor and patch version values. - There is no longer any auth tokens required on any endpoints.
- By convention, all required params are included in an API path, and all optional params are included as query params. The only exception is the
statement
param in the/query
endpoint, which while required, is allowed to be a query param. - We have switched to using the concept of a table owner rather than a table controller (which is a separate thing).
- Minor and patch updates to the spec will be done "in-place", and major updates will require a new spec doc with the major version bumped. This is an uncommon thing. It is still open as to how many, and for how long, we support previous major versions of the spec.
- Anything that can be done client side, should be done client side. This means we've dropped all the validation endpoints, and parser stuff should now be done client-side.
- Validator are also not going to relay anything for the clients. This means all mutating functionality must do through the smart contract. Notice that there are only
GET
based APIs specified in the updates below
But we aren’t just taking things away… these optimizations and the smaller API footprint comes with a more robust SQL specification, so that you can do more powerful JOIN
s, more nuanced INSERT
s and UPDATE
s, and even support UPSERT
-style SQL calls! Read on to find out more about all the improvements we have in store.
What’s gone?
The first thing to point out is that we’ve dropped our old JSON RPC API completely. This was our primary API endpoint for things like queries, getting transaction status information, getting information about tables, schemas, and even things like relaying transactions. Dropping JSON RPC for a smaller subset of simpler RESTful (ish) APIs was a big decision, but one we’re pretty happy with. This has reduced the complexity of our backend, made client-side querying simpler, and opens up more browser-based use-cases that were hard to address when JSON RPC parameters were required.
Dropping our old rpc
endpoint means also dropping the following methods: validateCreateTable
, validateWriteQuery
, runReadQuery
, relayWriteQuery
, getReceipt
, setController
. It also means Validators are no longer relaying any user-sent transactions to the on-chain registry contract (TablelandTables). This is a significant change, so I’ll repeat it here:
Validators are no longer relaying any client-sent smart contract transactions.
This is better for sustainability, because it makes costs on the network more transparent. It also highlights some of the inefficiencies with our old API: Validators don’t need to provide endpoints for things that developers can readily get from the smart contract, like listing Tables, getting Controller information, and more.
With these APIs gone, developers can do a better job of leveraging the right tool for the right job. For instance, you don’t need to query a Validator to get the list of Tables you own… just hit the TablelandTables smart contract’s tokensOfOwner
endpoint. Simpler, right? What about getting access controller information? Again, just query the smart contract… no need to ask the Validators for this information.
So rather than a mix of RESTful and JSON RPC APIs, we have just three core REST APIs (and a few utility APIs), which we’ll cover in the following sections. You can read up on the discussion(s) that lead to this new API design in this GitHub ticket, and read on to learn a bit more about the final outcome.
What’s changed?
Besides the above endpoint removals, there are a few core tweaks to existing endpoints worth mentioning. But before we get to that, there is one endpoint that didn’t really change in any practical ways (aside from now supporting the versioned API path), and that is the /query
API. This is arguable the “workhorse” API of the Validator API specification, so it is worth covering in detail.
Query the network
So as you might have guessed, the query
endpoint is for querying the Tableland network. This means developers can submit valid SQL queries in the form of SELECT
statements, and get responses back. In other words, query
can be used to return the results of a SQL read query against the Tabeland network.
As with all Validator APIs now, query is a GET
request. It takes a set of query parameters that help control the format of the JSON response:
Name | In | Type | Required | Description |
statement | query | string | true | The SQL read query statement |
format | query | string | false | The requested response format: |
extract | query | boolean | false | Whether to extract the JSON object from the single property of the surrounding JSON object. |
unwrap | query | boolean | false | Whether to unwrap the returned JSON objects from their surrounding array. |
The format
parameter can be “objects”
(returns the query results as a JSON array of JSON objects) or “`table”` (return the query results as a JSON object with columns and rows properties), and the statement
parameter is required.
How to call it
# You can also use wget or httpie
curl -X GET https://testnets.tableland.network/api/v1/query\?statement\=select%20\*%20from%20healthbot_80001_1 \
-H 'Accept: application/json'
What you’ll get back
The return format will depend largely on the set of parameters used. However, the above query using the default format
, extract
, and unwrap
values will yield:
[
{
"counter":61035
}
]
If you’ve been working with Tableland APIs for a while now, you’ll notice that this is actually pretty much exactly how our previous query API endpoint worked! So what has actually changed?
Get transaction status
Getting information about transaction progress has changed slightly. Previously, you needed to use our JSON RPC API to access this information, and that API actually required a SIWE (Sign In With Ethereum) style JWT token. This was all a bit of a hassle for something as simple as transaction status. So as mentioned above, we don’t require any authentication step, and this transaction receipt information is now entirely available via our super-duper new REST API.
By providing the receipt
endpoint with your target chainId
and transactionHash
in the form of path parameters, you can now get the status of a given transaction receipt by hash:
Name | In | Type | Required | Description |
chainId | path | integer(int32) | true | The parent chain to target |
transactionHash | path | string | true | The transaction hash to request |
This is super useful for things like polling for a CREATE
statement sent to the Tableland smart contract, or to find out if your INSERT
statement has finalized. The official Javascript SDK now uses this endpoint for providing a Promise
-based API for table mutations.
How to call it
# You can also use wget or httpie
curl -X GET https://testnets.tableland.network/api/v1/receipt/{chainId}/{transactionHash} \
-H 'Accept: application/json'
What you’ll get back
{
"table_id": "healthbot_80001_1",
"transaction_hash": "0xtransactionHash",
"block_number": 1,
"chain_id": 80001,
"error": "The query statement is invalid",
"error_event_idx": 1
}
In the above response, error
and error_event_idx
are optional, and will only be included if the given transaction actually throws an error on the Validator(s). The error_event_idx
value will indicate the index of the actual statement that caused the error when multiple statements are provided.
Get table information
Another subtle but pretty important change is how users can query for table information; namely ERC721-compliant metadata to drive the TablelandTables NFT assets and table schema information. We’ve merged these two table information sources into a single “Table” schema, which contains the usual ERC721 metadata fields, as well as a “schema”
key, with table schema information of the form:
{
"columns": [
{
"name": "string",
"type": "string",
"constraints": [
"string"
]
}
],
"table_constraints": [
"string"
]
}
The actual path for getting table information has also changed slightly, which is important for smart contract developers who were direct linking to Tableland metadata information. The tables
endpoint now returns information about a single table, including schema information, and requires chainId
and tableId
values in the form of path parameters:
Name | In | Type | Required | Description |
chainId | path | integer(int32) | true | The parent chain to target |
tableId | path | string | true | Table identifier |
How to call it
# You can also use wget
curl -X GET https://testnets.tableland.network/api/v1/tables/{chainId}/{tableId} \
-H 'Accept: application/json'
What you’ll get back
{
"name": "healthbot_5_1",
"external_url": "https://testnet.tableland.network/tables/healthbot_5_1",
"animation_url": "https://render.tableland.xyz/anim/?chain=1&id=1",
"image": "https://render.tableland.xyz/healthbot_5_1",
"attributes": {
"display_type": "date",
"trait_type": "created",
"value": 1657113720
},
"schema": {
"columns": [
{
"name": "id",
"type": "integer",
"constraints": [
"NOT NULL",
"PRIMARY KEY",
"UNIQUE"
]
}
],
"table_constraints": [
"PRIMARY KEY (id)"
]
}
}
What’s new?
Not much. We didn’t add a single new core endpoint. We did however add two new utility endpoints: health
and version
.
Get version information
Now that we’ve moved to a fully versioned RESTful (ish) HTTP API, the version you are working against is quite relevant. So you can query the version
endpoint and get everything you need to know about a Node’s API version(s). This endpoint returns version information about the validator daemon.
How to call it
# You can also use wget or httpie
curl -X GET https://testnets.tableland.network/api/v1/version \
-H 'Accept: application/json'
What you’ll get back
{
"version": 0,
"git_commit": "79688910d4689dcc0991a0d8eb9d988200586d8f",
"git_branch": "foo/experimentalfeature",
"git_state": "dirty",
"git_summary": "v1.2.3_dirty",
"build_date": "2022-11-29T16:28:04Z",
"binary_version": "v1.0.1"
}
Get health status
Is a given Validator API working? Is it healthy? Well now you can ask one! The new health
endpoint returns OK
if the validator considers itself healthy, so you’ll know right away if you can use it for queries etc. Pretty handy right?
How to call it
# You can also use wget or httpie
curl -X GET https://testnets.tableland.network/api/v1/health
What you’ll get back
No content, just a 200 status!
What’s next?
The next big thing on our roadmap is getting more folks to run their own validator(s) and building on the network. This release is actually a pre-cursor to that happening. The simplified REST APIs, smaller API footprint, and easier deploy steps (details on that coming soon) are all being developed with the goal of making it easier for folks to participate in the Tableland network — both in terms of hardware/software and development effort! So while we aren’t quite ready to open the floodgates to permissionless node operators just yet, we are getting close. Expect updates on this front in the very near future.
In the mean time, for folks already running nodes, or folks developing against the Validator APIs, we’re very interested to hear any feedback, issues, praise, or just general chatter about these API changes. We spent a lot of time examining existing use-cases to make sure the changes weren’t going to affect existing applications too much. We think we covered those bases well, but if we missed something let us know! And of course, don’t forget to check out our official API Specification on GitHub. This is the source of truth for our API implementation(s). Additionally, that OpenAPI Specification document can be used to generate client(s) for your favorite languages (we already use it to generate the TS/JS client), docs, and a whole lot more. Check out for a nice tool to generate Typescript types for example.
Interactive demo
The best way to get a feel for the new APIs is to play with them. Try that out here in the embedded interactive API console!
- Less is more, especially when it comes to APIs
- TL;DR
- What’s gone?
- What’s changed?
- Query the network
- How to call it
- What you’ll get back
- Get transaction status
- How to call it
- What you’ll get back
- Get table information
- How to call it
- What you’ll get back
- What’s new?
- Get version information
- How to call it
- What you’ll get back
- Get health status
- How to call it
- What you’ll get back
- What’s next?
- Interactive demo