mirror of
https://github.com/tursodatabase/libsql.git
synced 2024-12-15 10:59:47 +00:00
237 lines
6.0 KiB
Markdown
237 lines
6.0 KiB
Markdown
# The sqld HTTP API v2 specification ("Hrana over HTTP")
|
|
|
|
Version 2 of the HTTP API ("Hrana over HTTP") exposes stateful streams from
|
|
Hrana over HTTP. It provides functionality equivalent to Hrana and it is useful
|
|
for environments with missing or incomplete support for WebSockets.
|
|
|
|
This version deprecates version 1 of the HTTP API. Both clients and servers
|
|
should move to version 2 as soon as possible.
|
|
|
|
## Overview
|
|
|
|
The HTTP API uses data structures and semantics from the Hrana 2 protocol.
|
|
|
|
Individual requests on the same stream are tied together by the use of a baton.
|
|
The server returns a baton in every response to a request on the stream, and the
|
|
client then needs to include the baton in the subsequent request. The client
|
|
must serialize the requests: it must wait for a response to the previous request
|
|
before sending next request.
|
|
|
|
The server can also optionally specify a different URL that the client should
|
|
use for the requests on the stream. This can be used to ensure that stream
|
|
requests are "sticky" and reach the same server.
|
|
|
|
The server will close streams after a short period of inactivity, to make sure
|
|
that abandoned streams don't accumulate on the server.
|
|
|
|
## Check support for version 2
|
|
|
|
```typescript
|
|
GET /v2
|
|
```
|
|
|
|
If the server supports this version of the HTTP API, it should return a 2xx
|
|
response for a GET request on `/v2`. This can be used as a crude version
|
|
negotiation mechanism by the client.
|
|
|
|
## Execute requests on a stream
|
|
|
|
```typescript
|
|
POST /v2/pipeline
|
|
|
|
-> {
|
|
"baton": string | null,
|
|
"requests": Array<StreamRequest>,
|
|
}
|
|
|
|
<- {
|
|
"baton": string | null,
|
|
"base_url": string | null,
|
|
"results": Array<StreamResult>
|
|
}
|
|
|
|
type StreamResult =
|
|
| StreamResultOk
|
|
| StreamResultError
|
|
|
|
type StreamResultOk = {
|
|
"type": "ok",
|
|
"response": StreamResponse,
|
|
}
|
|
|
|
type StreamResultError = {
|
|
"type": "error",
|
|
"error": Error,
|
|
}
|
|
```
|
|
|
|
The `pipeline` endpoint is used to execute a pipeline of requests on a stream.
|
|
`baton` in the request specifies the stream. If the client sets `baton` to
|
|
`null`, the server should create a new stream.
|
|
|
|
Server responds with another `baton` value in the response. If the `baton` value
|
|
in the response is `null`, it means that the server has closed the stream. The
|
|
client must use this value to refer to this stream in the next request (the
|
|
`baton` in the response should be different from the `baton` in the request).
|
|
This forces the client to issue the requests serially: it must wait for the
|
|
response from a previous `pipeline` request before issuing another request on
|
|
the same stream.
|
|
|
|
The server should ensure that the `baton` values are unpredictable and
|
|
unforgeable, for example by cryptographically signing them.
|
|
|
|
If the `base_url` in the response is not `null`, the client should use this URL
|
|
when sending further requests on this stream. If it is `null`, the client should
|
|
use the same URL that it has used for the previous request. The `base_url`
|
|
must be an absolute URL with "http" or "https" scheme.
|
|
|
|
The `requests` array in the request specifies a sequence of stream requests that
|
|
should be executed on the stream. The server executes them in order and returns
|
|
the results in the `results` array in the response. Result is either a success
|
|
(`type` set to `"ok"`) or an error (`type` set to `"error"`). The server always
|
|
executes all requests, even if some of them return errors.
|
|
|
|
If the client receives an HTTP error (4xx or 5xx response) in response to the
|
|
`pipeline` endpoint, it means that the server encountered an internal error and
|
|
the stream is no longer valid.
|
|
|
|
## Requests
|
|
|
|
Requests in the HTTP API closely mirror stream requests in Hrana:
|
|
|
|
```typescript
|
|
type StreamRequest =
|
|
| CloseStreamReq
|
|
| ExecuteStreamReq
|
|
| BatchStreamReq
|
|
| SequenceStreamReq
|
|
| DescribeStreamReq
|
|
| StoreSqlStreamReq
|
|
| CloseSqlStreamReq
|
|
|
|
type StreamResponse =
|
|
| CloseStreamResp
|
|
| ExecuteStreamResp
|
|
| BatchStreamResp
|
|
| SequenceStreamResp
|
|
| DescribeStreamResp
|
|
| StoreSqlStreamResp
|
|
| CloseSqlStreamResp
|
|
```
|
|
|
|
### Close stream
|
|
|
|
```typescript
|
|
type CloseStreamReq = {
|
|
"type": "close",
|
|
}
|
|
|
|
type CloseStreamResp = {
|
|
"type": "close",
|
|
}
|
|
```
|
|
|
|
The `close` request closes the stream. It is an error if the client tries to
|
|
execute more requests on the same stream.
|
|
|
|
### Execute a statement
|
|
|
|
```typescript
|
|
type ExecuteStreamReq = {
|
|
"type": "execute",
|
|
"stmt": Stmt,
|
|
}
|
|
|
|
type ExecuteStreamResp = {
|
|
"type": "execute",
|
|
"result": StmtResult,
|
|
}
|
|
```
|
|
|
|
The `execute` request has the same semantics as the `execute` request in Hrana.
|
|
|
|
### Execute a batch
|
|
|
|
```typescript
|
|
type BatchStreamReq = {
|
|
"type": "batch",
|
|
"batch": Batch,
|
|
}
|
|
|
|
type BatchStreamResp = {
|
|
"type": "batch",
|
|
"result": BatchResult,
|
|
}
|
|
```
|
|
|
|
The `batch` request has the same semantics as the `batch` request in Hrana.
|
|
|
|
### Execute a sequence of SQL statements
|
|
|
|
```typescript
|
|
type SequenceStreamReq = {
|
|
"type": "sequence",
|
|
"sql"?: string | null,
|
|
"sql_id"?: int32 | null,
|
|
}
|
|
|
|
type SequenceStreamResp = {
|
|
"type": "sequence",
|
|
}
|
|
```
|
|
|
|
The `sequence` request has the same semantics as the `sequence` request in
|
|
Hrana.
|
|
|
|
### Describe a statement
|
|
|
|
```typescript
|
|
type DescribeStreamReq = {
|
|
"type": "describe",
|
|
"sql"?: string | null,
|
|
"sql_id"?: int32 | null,
|
|
}
|
|
|
|
type DescribeStreamResp = {
|
|
"type": "describe",
|
|
"result": DescribeResult,
|
|
}
|
|
```
|
|
|
|
The `describe` request has the same semantics as the `describe` request in
|
|
Hrana.
|
|
|
|
### Store an SQL text on the server
|
|
|
|
```typescript
|
|
type StoreSqlStreamReq = {
|
|
"type": "store_sql",
|
|
"sql_id": int32,
|
|
"sql": string,
|
|
}
|
|
|
|
type StoreSqlStreamResp = {
|
|
"type": "store_sql",
|
|
}
|
|
```
|
|
|
|
The `store_sql` request has the same semantics as the `store_sql` request in
|
|
Hrana, except that the scope of the SQL texts is just a single stream (in Hrana,
|
|
it is the whole connection).
|
|
|
|
### Close a stored SQL text
|
|
|
|
```typescript
|
|
type CloseSqlStreamReq = {
|
|
"type": "close_sql",
|
|
"sql_id": int32,
|
|
}
|
|
|
|
type CloseSqlStreamResp = {
|
|
"type": "close_sql",
|
|
}
|
|
```
|
|
|
|
The `close_sql` request has the same semantics as the `close_sql` request in
|
|
Hrana, except that the scope of the SQL texts is just a single stream.
|