Publish queries (http and websockets)
You might decide that instead of submitting queries directly to Flow, or using one of our SDKs to execute a query programmatically, you’d rather just publish the query as an HTTP(s) endpoint.
Saved queries
Any queries committed to your taxonomy that have an @HttpOperation
annotation will automatically be converted into an HTTP query.
For example:
import taxi.http.HttpOperation
@HttpOperation(url = '/api/q/movies/{movieId}', method = 'GET')
query getMoviesAndReivews(@PathVariable("movieId") movieId: MovieId) {
given { movieId }
find {
title : MovieTitle
reviewScore : ReviewScore
}
}
Any declared query
that’s in a Taxi project published to Flow can be converted into an HTTP operation.
HttpOperation annotation
Queries need to have an @HttpOperation
annotation:
Parameter | Description |
---|---|
url |
The URL the query is exposed under. It must start |
method |
The HTTP method to respond to, one of |
Variables can be defined in your path using {myVar}
, which are then made available in your query declaration using a @PathVariable
annotation.
For security reasons, queries are only exposed under the /api/q endpoint. Any queries where URL in the @HttpOperation annotation starts with something other than /api/q are ignored.
|
Declare the query
Named queries are defined using a query
syntax:
Path Variables
// Declares a query named getMoviesAndReviews`
query getMoviesAndReviews(
// Define variables to be passed in
@PathVariable("movieId") movieId: MovieId
) { ... }
Request Body
For a POST query, the request body is also available:
query doASearch(@RequestBody searchRequest: SearchRequest) {
...
}
Given clause
If you’ve written Taxi queries before, you’re used to using the given clause to provide data into the query.
For saved queries, that data is often passed as a variable, so the syntax is slightly different:
// Not a saved query, given clause has a value:
given { movieId: MovieId = 123 }
find { ... }
// A saved query, the movieId comes from the params:
query findMoviesAndReviews(@PathVariable(...) movieId: MovieId) {
given { movieId } // exposes the parameter into the query as a fact.
find { ... }
}
Stream results with server-sent events (SSE)
To receive query results as a continuous stream of server-sent events, set the Accept header to text/event-stream
.
This method can be applied to both standard Request/Response queries and streaming queries.
When using SSE, especially with queries returning multiple records (e.g., database queries), the time-to-first-byte is generally faster. This efficiency is due to records being transmitted as soon as they become available, unlike the standard approach where all records are delivered together at the end of the request.
Example request using curl
:
curl -X GET 'http://localhost:9021/api/q/streamingTest' \
-H 'Accept: text/event-stream;charset-UTF-8'
Saved streams
Streaming queries can be exposed over either HTTP (using server-sent events), or Websocket (or both).
Saved streams are executed in the background. By connecting to the output stream, you can observe the results.
Note that when you observe a result feed from a streaming query, results are published from the point the request is received. Previous results are not published on result stream.
You can expose streaming queries on both server-sent event endpoints and Websockets - simply add both the annotations. |
Publish on WebSocket
To publish a query over a websocket, annotate the query with the WebsocketOperation
annotation, declaring the path
:
For security reasons, websocket streams are only exposed under the /api/s path. Any queries where path in the @WebsocketOperation annotation starts with something other than /api/s are ignored.
|
@WebsocketOperation(path = '/api/s/newReleases')
query getNewReleaseAnnouncements {
stream { NewReleaseAnnouncement } as {
title : MovieTitle
reviewScore : ReviewScore
}[]
}
Publish as server-sent events
Streaming queries can also be published over HTTP by declaring a @HttpOperation
,
and then requesting a text/event-stream
response:
@HttpOperation(url = '/api/q/newReleases', method = 'GET')
query getNewReleaseAnnouncements {
stream { NewReleaseAnnouncement } as {
title : MovieTitle
reviewScore : ReviewScore
}[]
}
Once this is published, results can be streamed by requesting a text/event-stream
:
curl -X GET 'http://localhost:9021/api/q/newReleases' \
-H 'Accept: text/event-stream;charset-UTF-8'
Authentication to Query Endpoints
When Flow has been deployed with Identity Provider integration enabled, the query endpoints are secured using OAuth2.0. In order to access the query endpoints, you must provide a valid access token in the Authorization HTTP header. The access token is obtained by authenticating with the Identity Provider. For more information on how to obtain an access token, see Obtain an Access Token.
Instead of executing with the requesting users permissions (as request / response queries do), persistent streaming queries execute with a system account - the Executor user.
Configuring the Executor user
flow.security.openIdp.executorRole
prefixed parameters are required to configure the Executor user, see this Docker Compose file. Some of the parameters are pre-configured, see potential modification of security pre-configuration.
Observers vs Executors
Persistent streams are always executed under the permissions of the Executor user. However, these streams can also be observed by other users, through published http or websocket endpoint.
In this scenario, policies are applied twice:
-
First, the stream is executed using the permissions of the Executor user
-
Then, when being observed, the results of the stream are then re-evaluated using the permissions of the user observing the stream As a result, the observed output may differ from the actual data being emitted by the stream.