doc/user/content/integrations/websocket-api.md
{{< private-preview enabled-by-default="true" />}}
You can access Materialize through its interactive WebSocket API endpoint:
wss://<MZ host address>/api/experimental/sql
The API:
psql). Materialize provides you the username and password upon
setting up your account.CLOSECOPYDECLAREFETCHThe WebSocket API provides two modes with slightly different transactional semantics from one another:
SELECT 1; SELECT 2;CREATE TABLE) in implicit transactions, but
not DML (e.g. INSERT).wss://<MZ host address>/api/experimental/sql
To authenticate using a username and password, send an initial text or binary message containing a JSON object:
{
"user": "<Your email to access Materialize>",
"password": "<Your app password>",
"options": { <Optional map of session variables> }
}
To authenticate using a token, send an initial text or binary message containing a JSON object:
{
"token": "<Your access token>",
"options": { <Optional map of session variables> }
}
Successful authentication will result in:
ParameterStatus messages indicating the values of some initial session settings.BackendKeyData message that can be used for cancellation.ReadyForQuerymessage, at which point the server is ready to receive requests.An error during authentication is indicated by a websocket Close message.
HTTP Authorization headers are ignored.
WebSocket Text or Binary messages can be sent.
The payload is described below in the Input format section.
Each request will respond with some number of response messages, followed by a ReadyForQuery message.
There is exactly one ReadyForQuery message for each request, regardless of how many queries the request contains.
The message payload is a JSON object containing a key, query, which specifies the
SQL string to execute. query may contain multiple SQL statements separated by
semicolons.
{
"query": "select * from a; select * from b;"
}
The message payload is a JSON object containing a key queries, whose value is
array of objects, whose structure is:
| Key | Value |
|---|---|
query | A SQL string containing one statement to execute |
params | An optional array of text values to be used as the parameters to query. null values are converted to null values in Materialize. Note that all parameter values' elements must be text or null; the API will not accept JSON numbers. |
{
"queries": [
{ "query": "select * from a;" },
{ "query": "select a + $1 from a;", "params": ["100"] }
{ "query": "select a + $1 from a;", "params": [null] }
]
}
The response messages are WebSocket Text messages containing a JSON object that contains keys type and payload.
type value | Description |
|---|---|
ReadyForQuery | Sent at the end of each response batch |
Notice | An informational notice. |
CommandStarting | A command has executed and response data will be returned. |
CommandComplete | Executing a statement succeeded. |
Error | Executing a statement resulted in an error. |
Rows | A rows-returning statement is executing, and some Row messages may follow. |
Row | A single row result. |
ParameterStatus | Announces the value of a session setting. |
BackendKeyData | Information used to cancel queries. |
ReadyForQueryExactly one of these is sent at the end of every request batch.
It can be used to synchronize with the server, and means the server is ready for another request.
(However, many requests can be made at any time; there is no need to wait for this message before issuing more requests.)
The payload is a string describing the current transaction state:
I for idle: not in a transaction.T for in a transaction.E for a transaction in an error state. A request starting with ROLLBACK should be issued to exit it.NoticeA notice can appear at any time and contains diagnostic messages that were generated during execution of the query. The payload has the following structure:
{
"message": <informational message>,
"code": <notice code>,
"severity": <"warning"|"notice"|"debug"|"info"|"log">,
"detail": <optional error detail>,
"hint": <optional error hint>,
}
ErrorExecuting a statement resulted in an error. The payload has the following structure:
{
"message": <informational message>,
"code": <error code>,
"detail": <optional error detail>,
"hint": <optional error hint>,
}
CommandStartingA statement has executed and response data will be returned. This message can be used to know if rows or streaming data will follow. The payload has the following structure:
{
"has_rows": <boolean>,
"is_streaming": <boolean>,
}
The has_rows field is true if a Rows message will follow.
The is_streaming field is true if there is no expectation that a CommandComplete message will ever occur.
This is the case for SUBSCRIBE queries.
CommandCompleteExecuting a statement succeeded.
The payload is a string containing the statement's tag.
RowsA rows-returning statement is executing and some number (possibly 0) of Row messages will follow.
Either a CommandComplete or Error message will then follow indicating there are no more rows and the final result of the statement.
The payload has the following structure:
{
"columns":
[
{
"name": <column name>,
"type_oid": <type oid>,
"type_len": <type len>,
"type_mod": <type mod>
}
...
]
}
The inner object's various type_X fields are lower-level details that can be used to convert the row results from a string to a more specific data type.
type_oid is the OID of the data type.
type_len is the data size type (see pg_type.typlen).
type_mod is the type modifier (see pg_attribute.atttypmod).
RowA single row result.
Will only occur after a Rows message.
The payload is an array of JSON values corresponding to the columns from the Rows message.
Numeric results are converted to strings to avoid possible JavaScript number inaccuracy.
ParameterStatusAnnounces the value of a session setting. These are sent during startup and when a statement caused a session parameter to change. The payload has the following structure:
{
"name": <name of parameter>,
"value": <new value of parameter>,
}
BackendKeyDataInformation used to cancel queries. The payload has the following structure:
{
"conn_id": <connection id>,
"secret_key": <secret key>,
}
You can model these with the following TypeScript definitions:
type Auth =
| { user: string; password: string; options?: { [name: string]: string } }
| { token: string; options?: { [name: string]: string } }
;
interface Simple {
query: string;
}
interface ExtendedRequest {
query: string;
params?: (string | null)[];
}
interface Extended {
queries: ExtendedRequest[];
}
type SqlRequest = Simple | Extended;
interface Notice {
message: string;
severity: string;
detail?: string;
hint?: string;
}
interface Error {
message: string;
code: string;
detail?: string;
hint?: string;
}
interface ParameterStatus {
name: string;
value: string;
}
interface CommandStarting {
has_rows: boolean;
is_streaming: boolean;
}
interface BackendKeyData {
conn_id: number; // u32
secret_key: number; // u32
}
interface Column {
name: string;
type_oid: number; // u32
type_len: number; // i16
type_mod: number; // i32
}
interface Description {
columns: Column[];
}
type WebSocketResult =
| { type: "ReadyForQuery"; payload: string }
| { type: "Notice"; payload: Notice }
| { type: "CommandComplete"; payload: string }
| { type: "Error"; payload: Error }
| { type: "Rows"; payload: Description }
| { type: "Row"; payload: any[] }
| { type: "ParameterStatus"; payload: ParameterStatus }
| { type: "CommandStarting"; payload: CommandStarting }
| { type: "BackendKeyData"; payload: BackendKeyData }
;
$ echo '{"query": "select 1,2; values (4), (5)"}' | websocat wss://<MZ host address>/api/experimental/sql
{"type":"CommandStarting","payload":{"has_rows":true,"is_streaming":false}}
{"type":"Rows","payload":{"columns":[{"name":"?column?","type_oid":23,"type_len":4,"type_mod":-1},{"name":"?column?","type_oid":23,"type_len":4,"type_mod":-1}]}}
{"type":"Row","payload":["1","2"]}
{"type":"CommandComplete","payload":"SELECT 1"}
{"type":"CommandStarting","payload":{"has_rows":true,"is_streaming":false}}
{"type":"Rows","payload":{"columns":[{"name":"column1","type_oid":23,"type_len":4,"type_mod":-1}]}}
{"type":"Row","payload":["4"]}
{"type":"Row","payload":["5"]}
{"type":"CommandComplete","payload":"SELECT 2"}
{"type":"ReadyForQuery","payload":"I"}