0
0
mirror of https://github.com/tursodatabase/libsql.git synced 2024-12-15 10:59:47 +00:00
libsql/docs/HTTP_V2_SPEC.md
2023-11-14 10:28:20 -05:00

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.