title: "Connect to Materialize via HTTP" description: "How to use Materialize via HTTP" menu: main:
parent: "integrations"
weight: 50
name: "HTTP API"
You can access Materialize through its "session-less" HTTP API endpoint:
https://<MZ host address>/api/sql
The API:
psql
). Materialize provides you the username and password upon
setting up your account.CLOSE
COPY
DECLARE
FETCH
SUBSCRIBE
The HTTP 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
).Download our OpenAPI v3 spec for this interface: environmentd-openapi.yml.
https://<MZ host address>/api/sql
Accessing the endpoint requires basic authentication. Reuse the same credentials as with a SQL client (e.g. psql
):
You can optionally specify configuration parameters for each request, as a URL-encoded JSON object, with the options
query parameter:
https://<MZ host address>/api/sql?options=<object>
For example, this is how you could specify the application_name
configuration parameter with JavaScript:
// Create and encode our parameters object.
const options = { application_name: "my_app" };
const encoded = encodeURIComponent(JSON.stringify(options));
// Add the object to our URL as the "options" query parameter.
const url = new URL(`https://${mzHostAddress}/api/sql`);
url.searchParams.append("options", encoded);
The request body 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 request body 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 output format is a JSON object with one key, results
, whose value is
an array of the following:
Result | JSON value |
---|---|
Rows | {"rows": <2D array of JSON-ified results>, "desc": <array of column descriptions>, "notices": <array of notices>} |
Error | {"error": <Error object from execution>, "notices": <array of notices>} |
Ok | {"ok": <tag>, "notices": <array of notices>} |
Each committed statement returns exactly one of these values; e.g. in the case
of "complex responses", such as INSERT INTO...RETURNING
, the presence of a
"rows"
object implies "ok"
.
The "notices"
array is present in all types of results and contains any
diagnostic messages that were generated during execution of the query. It has
the following structure:
{"severity": <"warning"|"notice"|"debug"|"info"|"log">, "message": <informational message>}
Note that the returned values include the results of statements which were ultimately rolled back because of an error in a later part of the transaction. You must parse the results to understand which statements ultimately reflect the resultant state.
Numeric results are converted to strings to avoid possible JavaScript number inaccuracy. Column descriptions contain the name, oid, data type size and type modifier of a returned column.
You can model these with the following TypeScript definitions:
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 Column {
name: string;
type_oid: number; // u32
type_len: number; // i16
type_mod: number; // i32
}
interface Description {
columns: Column[];
}
type SqlResult =
| {
tag: string;
rows: any[][];
desc: Description;
notices: Notice[];
} | {
ok: string;
notices: Notice[];
} | {
error: Error;
notices: Notice[];
};
Use the extended input format to run a transaction:
curl 'https://<MZ host address>/api/sql' \
--header 'Content-Type: application/json' \
--user '<username>:<passsword>' \
--data '{
"queries": [
{ "query": "CREATE TABLE IF NOT EXISTS t (a int);" },
{ "query": "CREATE TABLE IF NOT EXISTS s (a int);" },
{ "query": "BEGIN;" },
{ "query": "INSERT INTO t VALUES ($1), ($2)", "params": ["100", "200"] },
{ "query": "COMMIT;" },
{ "query": "BEGIN;" },
{ "query": "INSERT INTO s VALUES ($1), ($2)", "params": ["9", null] },
{ "query": "COMMIT;" }
]
}'
Response:
{
"results": [
{"ok": "CREATE TABLE", "notices": []},
{"ok": "CREATE TABLE", "notices": []},
{"ok": "BEGIN", "notices": []},
{"ok": "INSERT 0 2", "notices": []},
{"ok": "COMMIT", "notices": []},
{"ok": "BEGIN", "notices": []},
{"ok": "INSERT 0 2", "notices": []},
{"ok": "COMMIT", "notices": []}
]
}
Use the simple input format to run a query:
curl 'https://<MZ host address>/api/sql' \
--header 'Content-Type: application/json' \
--user '<username>:<passsword>' \
--data '{
"query": "SELECT t.a + s.a AS cross_add FROM t CROSS JOIN s; SELECT a FROM t WHERE a IS NOT NULL;"
}'
Response:
{
"results": [
{
"desc": {
"columns": [
{
"name": "cross_add",
"type_len": 4,
"type_mod": -1,
"type_oid": 23
}
]
},
"notices": [],
"rows": [],
"tag": "SELECT 0"
},
{
"desc": {
"columns": [
{
"name": "a",
"type_len": 4,
"type_mod": -1,
"type_oid": 23
}
]
},
"notices": [],
"rows": [],
"tag": "SELECT 0"
}
]
}