Skip to content
Connect > Data APIs

Data APIs Quickstart

This walkthrough uses both Data APIs in one round trip: you create a table, insert a row over the REST API, and watch it arrive live over a WebSocket subscription. It takes a few minutes and assumes an AmiCore process with a running Center named center.

1. Enable both surfaces

In local.properties:

1
2
3
4
5
# enable SQL over REST
ami.rest.plugin.classes=*

# start the WebSocket server
ami.websocket.port=3410

Restart the process. Confirm the WebSocket started (log line):

Started shared AmiWebSocket server on port 3410, components=[center]

2. Create a table to stream

A table that REST and WebSocket clients can share must be a CREATE PUBLIC TABLE: that is what makes it shared. Public tables broadcast by default, so they stream without further options; Broadcast="true" below only states the default. Create the QUOTES table over REST (substitute your host and Web port; -k accepts a self-signed dev certificate):

1
2
3
curl -sk -u username:password -G \
  --data-urlencode 'cmd=CREATE PUBLIC TABLE QUOTES (SYM String, bid Double, ask Double, last Double, volume Long) USE Broadcast="true" RefreshPeriodMs="100"' \
  "https://localhost:33333/3forge_rest/query?display=jsonMaps"

Then give it a primary key. The key is a separate CREATE INDEX statement: there is no primary-key clause inside CREATE TABLE:

1
2
3
curl -sk -u username:password -G \
  --data-urlencode 'cmd=CREATE INDEX QUOTES_PK ON QUOTES(SYM HASH) USE Constraint="PRIMARY"' \
  "https://localhost:33333/3forge_rest/query?display=jsonMaps"

Both return "status":"ok", and SHOW TABLES now reports QUOTES with Broadcast:true, Scope:PUBLIC.

Use CREATE PUBLIC TABLE, not CREATE TABLE

A plain CREATE TABLE returns "status":"ok" but the table is transient: a later stateless REST call or a WebSocket watcher gets Unknown table. Only a PUBLIC table is shared live state these APIs can see and subscribe to, and only a PUBLIC table accepts USE options. CREATE REALTIME TABLE and a PRIMARYKEY clause inside CREATE TABLE are not valid syntax. See Concepts → Creating a streamable table.

3. Insert the first row over REST

1
2
3
curl -sk -u username:password -G \
  --data-urlencode "cmd=INSERT INTO QUOTES (SYM, bid, ask, last, volume) VALUES (\"IBM\", 123.40, 123.50, 123.45, 1000)" \
  "https://localhost:33333/3forge_rest/query?display=jsonMaps"

The response reports "rowsAffected":1.

Rows don't survive a restart

This QUOTES table has no PersistEngine, so its rows are lost if the process restarts (the empty table definition remains). That's fine for the quickstart; for data that must persist, see Concepts → Creating a streamable table.

4. Subscribe over WebSocket

Open the reference client that ships with AmiCore (reference-client.html) in a browser, or use the minimal client below. Set the URL to ws://localhost:3410/center and subscribe to QUOTES:

1
2
3
4
5
6
7
<script>
  const ws = new WebSocket("ws://localhost:3410/center");
  ws.onopen = () => ws.send(JSON.stringify({
    action: "subscribe", id: "s1", sql: "SELECT SYM,bid,ask,last FROM QUOTES"
  }));
  ws.onmessage = (e) => console.log(JSON.parse(e.data));
</script>

You should see these frames. The first 2 can arrive in either order:

1
2
3
4
{"type":"status","connected":true}
{"type":"subscribed","id":"s1","table":"QUOTES"}
{"type":"updates","updates":[{"table":"QUOTES","action":"add","id":8000348,"objectId":"8000348","row":{"SYM":"IBM","bid":123.4,"ask":123.5,"last":123.45,"volume":1000}}]}
{"type":"snapshot","tables":["QUOTES"]}

The initial row arrives as an add, and the snapshot frame follows it to mark the end of the initial load: your client now holds the current table. See WebSocket API → First messages on connect.

5. Write again, watch it stream

With the subscription still open, update the row over REST:

1
2
3
curl -sk -u username:password -G \
  --data-urlencode "cmd=UPDATE QUOTES SET bid=123.60, last=123.55 WHERE SYM==\"IBM\"" \
  "https://localhost:33333/3forge_rest/query?display=jsonMaps"

Within about 100 ms (the conflation flush interval) your WebSocket client receives an updates frame with an update action for the IBM row, carrying only the changed fields:

{"type":"updates","updates":[{"table":"QUOTES","action":"update","id":8000348,"objectId":"8000348","row":{"bid":123.6,"last":123.55}}]}

Match it to the earlier add by its objectId (an internal row id, not the SYM). Insert a second symbol and an add arrives the same way.

That is the whole model: REST writes, WebSocket watches.

6. Clean up

1
2
3
curl -sk -u username:password -G \
  --data-urlencode "cmd=DROP TABLE QUOTES" \
  "https://localhost:33333/3forge_rest/query"

Where to go next

  • REST API: every /query parameter and response format.
  • WebSocket streaming API: the full frame protocol, conflation, and focus filtering.
  • Use cases: worked examples for querying, writing, and streaming.