Less is more, especially when it comes to APIs
⚙️

Less is more, especially when it comes to APIs

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

📢
TL;DR This blog post provides an overview of all the changes coming to the core Tableland Validator API. This includes details on the new REST API, changes to parameters and API paths, and a whole lot more. For developers who use the RESTful query API in their smart contracts, they’re going to want to pay attention!

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.

ℹ️
I’d also recommend folks take a look at this interactive version of our new API, which lets you do things like test the new API calls against a real live testnet Validator.

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 JOINs, more nuanced INSERTs and UPDATEs, 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!