A web server that offers an HTTP/JSON API for interacting with Datomic databases.
This is not an official Datomic project or documentation and it is not affiliated with Datomic in any way.
Ensure you have Java and Clojure installed.
cp .env.example .env
clj -M:run
[main] INFO flare.components.server - Starting server on http://0.0.0.0:3042 as peer
Ensure you have curl, bb, and jq installed.
Transact a Schema:
echo '
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
Assert a Fact:
echo '
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
Read the Data by Querying:
echo '
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/q \
-X GET \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"inputs": [
{
"database": {
"latest": true
}
}
],
"query": $(cat)
}
JSON
{
"data": [
[
4611681620380877802,
"The Tell-Tale Heart",
"Horror"
]
]
}
You can set up Datomic by following its official documentation: Pro Setup.
Alternatively, if you have Docker, you can leverage datomic-pro-docker.
Clone the repository and copy the Docker Compose template:
git clone https://github.com/gbaptista/datomic-pro-docker.git
cd datomic-pro-docker
cp compose/datomic-postgresql.yml docker-compose.yml
Start PostgreSQL as Datomic's storage service:
docker compose up -d datomic-storage
docker compose logs -f datomic-storage
Create the table for Datomic databases:
docker compose run datomic-tools psql \
-h datomic-storage \
-U datomic-user \
-d my-datomic-storage \
-c 'CREATE TABLE datomic_kvs (
id TEXT NOT NULL,
rev INTEGER,
map TEXT,
val BYTEA,
CONSTRAINT pk_id PRIMARY KEY (id)
);'
You will be prompted for a password, which is unsafe
.
Start the Datomic Transactor:
docker compose up -d datomic-transactor
docker compose logs -f datomic-transactor
Create your database named my-datomic-database
:
docker compose run datomic-tools clojure -M -e "$(cat <<'CLOJURE'
(require '[datomic.api :as d])
(def db-uri "datomic:sql://my-datomic-database?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
(d/create-database db-uri)
(System/exit 0)
CLOJURE
)"
Your Datomic database is ready to start using Flare in Peer Mode:
graph RL
Transactor --- Storage[(Storage)]
Flare("Flare (Peer)") -.- Storage
Flare --- Transactor
Application([Application]) --- Flare
If you want to use Client Mode, start a Datomic Peer Server:
docker compose up -d datomic-peer-server
docker compose logs -f datomic-peer-server
Your Datomic database is ready to start using Flare in Client Mode:
graph RL
Transactor --- Storage[(Storage)]
PeerServer --- Transactor
PeerServer -.- Storage
Flare(Flare) --- PeerServer[Peer Server]
Application([Application]) --- Flare
With Datomic running, you are ready to run Flare:
git clone https://github.com/gbaptista/datomic-flare.git
cd datomic-flare
The server can operate in Peer Mode, embedding com.datomic/peer
to establish a Peer directly within the server, or in Client Mode, using com.datomic/client-pro
to connect to a Datomic Peer Server.
Copy the .env.example
file and fill it with the appropriate information.
cp .env.example .env
If you want Peer Mode:
FLARE_PORT=3042
FLARE_BIND=0.0.0.0
FLARE_MODE=peer
FLARE_PEER_CONNECTION_URI="datomic:sql://my-datomic-database?jdbc:postgresql://localhost:5432/my-datomic-storage?user=datomic-user&password=unsafe"
If you want Client Mode:
FLARE_PORT=3042
FLARE_BIND=0.0.0.0
FLARE_MODE=client
FLARE_CLIENT_ENDPOINT=localhost:8998
FLARE_CLIENT_SECRET=unsafe-secret
FLARE_CLIENT_ACCESS_KEY=unsafe-key
FLARE_CLIENT_DATABASE_NAME=my-datomic-database
Ensure you have Java and Clojure installed.
Run the server:
clj -M:run
[main] INFO flare.components.server - Starting server on http://0.0.0.0:3042 as peer
Ensure you have curl, bb, and jq installed, and you should be able to start firing requests to the server:
curl -s http://localhost:3042/meta \
-X GET \
-H "Content-Type: application/json" \
| jq
{
"data": {
"mode": "peer",
"datomic-flare": "1.0.0",
"org.clojure/clojure": "1.12.0",
"com.datomic/peer": "1.0.7187",
"com.datomic/client-pro": "1.0.81"
}
}
If you are using Clojure, you already have native access to Datomic APIs and probably should not be using Flare.
If you are using Java, you already have native access to Datomic APIs and probably should not be using Flare.
Ensure you have curl, bb, and jq installed.
bash flare.sh
echo '
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
echo '
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
echo '
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/q \
-X GET \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"inputs": [
{
"database": {
"latest": true
}
}
],
"query": $(cat)
}
JSON
go run flare.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type QueryRequestBody struct {
Inputs []interface{} `json:"inputs"`
Query string `json:"query,omitempty"`
Data string `json:"data,omitempty"`
}
type DatabaseInput struct {
Database struct {
Latest bool `json:"latest"`
} `json:"database"`
}
func makeRequest(method string, url string, body QueryRequestBody) string {
jsonBody, _ := json.Marshal(body)
client := &http.Client{}
request, _ := http.NewRequest(method, url, bytes.NewBuffer(jsonBody))
request.Header.Set("Content-Type", "application/json")
response, _ := client.Do(request)
defer response.Body.Close()
responseBody, _ := ioutil.ReadAll(response.Body)
return string(responseBody)
}
func main() {
responseBody := makeRequest("POST", "http://localhost:3042/datomic/transact", QueryRequestBody{
Data: `
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}
{:db/ident :book/published_at_year
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "The year the book was published."}]
`,
})
fmt.Println(responseBody)
responseBody = makeRequest("POST", "http://localhost:3042/datomic/transact", QueryRequestBody{
Data: `
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"
:book/published_at_year 1843}]
`,
})
fmt.Println(responseBody)
responseBody = makeRequest("GET", "http://localhost:3042/datomic/q", QueryRequestBody{
Inputs: []interface{}{
DatabaseInput{Database: struct {
Latest bool `json:"latest"`
}{Latest: true}},
"The Tell-Tale Heart",
},
Query: `
[:find ?e ?title ?genre ?year
:in $ ?title
:where [?e :book/title ?title]
[?e :book/genre ?genre]
[?e :book/published_at_year ?year]]
`,
})
fmt.Println(responseBody)
}
You can use standard browser APIs like fetch. However, it does not accept GET
requests with a body, so we use POST
instead. Read more about GET
vs. POST
.
async function main() {
let response;
response = await fetch('http://localhost:3042/datomic/transact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: `
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
`
})
});
console.log(await response.json());
response = await fetch('http://localhost:3042/datomic/transact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: `
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
`
})
});
console.log(await response.json());
response = await fetch('http://localhost:3042/datomic/q', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
inputs: [
{ database: { latest: true } }
],
query: `
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
`
})
});
console.log(await response.json());
}
main();
Create a package.json
file:
{
"dependencies": {
"axios": "^1.7.7"
}
}
npm install
node flare.js
const axios = require('axios');
async function main() {
let response;
response = await axios.post('http://localhost:3042/datomic/transact',
{ data: `
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
`
},
{ headers: { 'Content-Type': 'application/json' } }
)
console.log(response.data);
response = await axios.post('http://localhost:3042/datomic/transact',
{ data: `
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
`
},
{ headers: { 'Content-Type': 'application/json' } }
);
console.log(response.data);
response = await axios.get('http://localhost:3042/datomic/q', {
headers: { 'Content-Type': 'application/json' },
data: {
inputs: [
{ database: { latest: true } }
],
query: `
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
`
}
});
console.log(response.data);
}
main();
Create a composer.json
file:
{
"require": {
"guzzlehttp/guzzle": "^7.9.2"
}
}
composer install
php flare.php
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'http://localhost:3042',
'headers' => ['Content-Type' => 'application/json'],
]);
$response = $client->post('/datomic/transact', [
'json' => [
'data' => '
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
'
]
]);
echo $response->getBody();
$response = $client->post('/datomic/transact', [
'json' => [
'data' => '
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
'
]
]);
echo $response->getBody();
$response = $client->get('/datomic/q', [
'json' => [
'inputs' => [
['database' => ['latest' => true]]
],
'query' => '
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
'
]
]);
echo $response->getBody();
Create a requirements.txt
file:
requests ~= 2.32.3
pip install -r requirements.txt
python flare.py
import requests
response = requests.post(
'http://localhost:3042/datomic/transact',
headers={'Content-Type': 'application/json'},
json={
"data": """
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
"""
}
)
print(response.json())
response = requests.post(
'http://localhost:3042/datomic/transact',
headers={'Content-Type': 'application/json'},
json={
"data": """
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
"""
}
)
print(response.json())
response = requests.get(
'http://localhost:3042/datomic/q',
headers={'Content-Type': 'application/json'},
json={
"inputs": [
{"database": {"latest": True}}
],
"query": """
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
"""
}
)
print(response.json())
You might want to check out the Flare Ruby Gem.
Create a Gemfile
file:
source 'https://rubygems.org'
gem 'faraday', '~> 2.12'
gem 'faraday-typhoeus', '~> 1.1'
gem 'typhoeus', '~> 1.4', '>= 1.4.1'
bundle
bundle exec ruby flare.rb
require 'json'
require 'faraday'
require 'faraday/typhoeus'
Faraday.default_adapter = :typhoeus
response = Faraday.post('http://localhost:3042/datomic/transact') do |request|
request.headers['Content-Type'] = 'application/json'
request.body = {
data: <<~EDN
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}]
EDN
}.to_json
end
puts response.body
response = Faraday.post('http://localhost:3042/datomic/transact') do |request|
request.headers['Content-Type'] = 'application/json'
request.body = {
data: <<~EDN
[{:db/id -1
:book/title "The Tell-Tale Heart"
:book/genre "Horror"}]
EDN
}.to_json
end
puts response.body
response = Faraday.get('http://localhost:3042/datomic/q') do |request|
request.headers['Content-Type'] = 'application/json'
request.body = {
inputs: [
{ database: { latest: true } }
],
query: <<~EDN
[:find ?e ?title ?genre
:where [?e :book/title ?title]
[?e :book/genre ?genre]]
EDN
}.to_json
end
puts response.body
Ensure you have curl, bb, and jq installed.
curl -s http://localhost:3042/meta \
-X GET \
-H "Content-Type: application/json" \
| jq
{
"data": {
"mode": "peer",
"datomic-flare": "1.0.0",
"org.clojure/clojure": "1.12.0",
"com.datomic/peer": "1.0.7187",
"com.datomic/client-pro": "1.0.81"
}
}
In Client Mode, this operation is not supported as the Peer Server does not have it available.
curl -s http://localhost:3042/datomic/create-database \
-X POST \
-H "Content-Type: application/json" \
-d '
{
"name": "moonlight"
}
' \
| jq
{
"data": true
}
In Client Mode, this operation is not supported as the Peer Server does not have it available.
curl -s http://localhost:3042/datomic/delete-database \
-X DELETE \
-H "Content-Type: application/json" \
-d '
{
"name": "moonlight"
}
' \
| jq
{
"data": true
}
Flare on Peer Mode:
curl -s http://localhost:3042/datomic/get-database-names \
-X GET \
-H "Content-Type: application/json" \
| jq
Flare on Client Mode:
curl -s http://localhost:3042/datomic/list-databases \
-X GET \
-H "Content-Type: application/json" \
| jq
{
"data": [
"my-datomic-database"
]
}
echo '
[{:db/ident :book/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the book."}
{:db/ident :book/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the book."}
{:db/ident :book/published_at_year
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "The year the book was first published."}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@6c2743f5",
"db-after": "datomic.db.Db@7b188823",
"tx-data": [
[13194139534315, 50, "2024-10-06T13:23:28.602Z", 13194139534315, true],
[74, 10, ":book/published_at_year", 13194139534315, true],
[74, 40, 22, 13194139534315, true],
[74, 41, 35, 13194139534315, true],
[74, 62, "The year the book was first published.", 13194139534315, true],
[0, 13, 74, 13194139534315, true]
],
"tempids": {
"-9223300668110597889": 72,
"-9223300668110597888": 73,
"-9223300668110597887": 74
}
}
}
echo '
[{:db/id -1
:book/title "Pride and Prejudice"
:book/genre "Romance"
:book/published_at_year 1813}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@75d129e2",
"db-after": "datomic.db.Db@21369e69",
"tx-data": [
[13194139534316, 50, "2024-10-06T13:23:28.618Z", 13194139534316, true],
[4611681620380877805, 72, "Pride and Prejudice", 13194139534316, true],
[4611681620380877805, 73, "Romance", 13194139534316, true],
[4611681620380877805, 74, 1813, 13194139534316, true]
],
"tempids": {
"-1": 4611681620380877805
}
}
}
echo '
[{:db/id -1
:book/title "Near to the Wild Heart"
:book/genre "Novel"
:book/published_at_year 1943}
{:db/id -2
:book/title "A Study in Scarlet"
:book/genre "Detective"
:book/published_at_year 1887}
{:db/id -3
:book/title "The Tell-Tale Heart"
:book/genre "Horror"
:book/published_at_year 1843}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@55451c2f",
"db-after": "datomic.db.Db@6a23cf34",
"tx-data": [
[13194139534318, 50, "2024-10-06T13:23:28.636Z", 13194139534318, true],
[4611681620380877807, 72, "Near to the Wild Heart", 13194139534318, true],
[4611681620380877807, 73, "Novel", 13194139534318, true],
[4611681620380877807, 74, 1943, 13194139534318, true],
[4611681620380877808, 72, "A Study in Scarlet", 13194139534318, true],
[4611681620380877808, 73, "Detective", 13194139534318, true],
[4611681620380877808, 74, 1887, 13194139534318, true],
[4611681620380877809, 72, "The Tell-Tale Heart", 13194139534318, true],
[4611681620380877809, 73, "Horror", 13194139534318, true],
[4611681620380877809, 74, 1843, 13194139534318, true]
],
"tempids": {
"-1": 4611681620380877807,
"-2": 4611681620380877808,
"-3": 4611681620380877809
}
}
}
In Client Mode, this operation is not supported as the Peer Server does not have it available.
curl -s http://localhost:3042/datomic/entity \
-X GET \
-H "Content-Type: application/json" \
-d '
{
"database": {
"latest": true
},
"id": 4611681620380877807
}
' \
| jq
{
"data": {
":book/title": "Near to the Wild Heart",
":book/genre": "Novel",
":book/published_at_year": 1943,
":db/id": 4611681620380877807
}
}
echo '
[:find ?e ?title ?genre ?year
:where [?e :book/title ?title]
[?e :book/genre ?genre]
[?e :book/published_at_year ?year]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/q \
-X GET \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"inputs": [
{
"database": {
"latest": true
}
}
],
"query": $(cat)
}
JSON
{
"data": [
[
4611681620380877808,
"A Study in Scarlet",
"Detective",
1887
],
[
4611681620380877807,
"Near to the Wild Heart",
"Novel",
1943
],
[
4611681620380877809,
"The Tell-Tale Heart",
"Horror",
1843
],
[
4611681620380877805,
"Pride and Prejudice",
"Romance",
1813
]
]
}
echo '
[:find ?e ?title ?genre ?year
:in $ ?title
:where [?e :book/title ?title]
[?e :book/genre ?genre]
[?e :book/published_at_year ?year]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/q \
-X GET \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"inputs": [
{
"database": {
"latest": true
}
},
"The Tell-Tale Heart"
],
"query": $(cat)
}
JSON
{
"data": [
[
4611681620380877809,
"The Tell-Tale Heart",
"Horror",
1843
]
]
}
echo '
[{:db/id 4611681620380877806 :book/genre "Gothic"}]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@164175ab",
"db-after": "datomic.db.Db@31164dc",
"tx-data": [
[13194139534322, 50, "2024-10-06T13:23:28.701Z", 13194139534322, true],
[4611681620380877806, 73, "Gothic", 13194139534322, true]
],
"tempids": {
}
}
}
Retract the value of an attribute:
echo '
[[:db/retract 4611681620380877806 :book/genre "Gothic"]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@7e48b4f5",
"db-after": "datomic.db.Db@ae68e67",
"tx-data": [
[13194139534323, 50, "2024-10-06T13:23:28.715Z", 13194139534323, true],
[4611681620380877806, 73, "Gothic", 13194139534323, false]
],
"tempids": {
}
}
}
Retract an attribute:
echo '
[[:db/retract 4611681620380877804 :book/genre]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@17175ebd",
"db-after": "datomic.db.Db@1f28ff07",
"tx-data": [
[13194139534324, 50, "2024-10-06T13:23:28.729Z", 13194139534324, true]
],
"tempids": {
}
}
}
Retract an entity:
echo '
[[:db/retractEntity 4611681620380877805]]
' \
| bb -e '(pr-str (edn/read-string (slurp *in*)))' \
| curl -s http://localhost:3042/datomic/transact \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<JSON \
| jq
{
"data": $(cat)
}
JSON
{
"data": {
"db-before": "datomic.db.Db@24fc1f0b",
"db-after": "datomic.db.Db@1ad72845",
"tx-data": [
[13194139534325, 50, "2024-10-06T13:23:28.745Z", 13194139534325, true],
[4611681620380877805, 72, "Pride and Prejudice", 13194139534325, false],
[4611681620380877805, 73, "Romance", 13194139534325, false],
[4611681620380877805, 74, 1813, 13194139534325, false]
],
"tempids": {
}
}
}
curl -s http://localhost:3042/datomic/datoms \
-X GET \
-H "Content-Type: application/json" \
-d '
{
"database": {
"latest": true
},
"index": "eavt"
}
' \
| jq
{
"data": [
[0, 10, "db", 0, true],
[0, 11, 0, 54, true],
[0, 11, 3, 0, true],
[0, 11, 4, 0, true]
]
}
Here are samples of how you may deploy Flare in your infrastructure.
graph RL
Transactor --- Storage[(Storage)]
Flare("Flare (Peer)") -.- Storage
Flare --- Transactor
Application([Application]) --- Flare
graph RL
Transactor --- Storage[(Storage)]
FlareA("Flare (Peer)") -.- Storage
FlareB("Flare (Peer)") -.- Storage
FlareA --- Transactor
FlareB --- Transactor
ApplicationA([Application]) --- FlareA
ApplicationB([Application]) --- FlareA
ApplicationC([Application]) --- FlareB
ApplicationD([Application]) --- FlareB
graph RL
Transactor --- Storage[(Storage)]
PeerServer --- Transactor
PeerServer -.- Storage
Flare(Flare) --- PeerServer[Peer Server]
Application([Application]) --- Flare
graph RL
Transactor --- Storage[(Storage)]
PeerServer --- Transactor
PeerServer -.- Storage
FlareA(Flare) --- PeerServer[Peer Server]
FlareB(Flare) --- PeerServer
ApplicationA([Application]) --- FlareA
ApplicationB([Application]) --- FlareA
ApplicationC([Application]) --- FlareB
ApplicationD([Application]) --- FlareB
graph RL
Transactor --- Storage[(Storage)]
PeerServerA[Peer Server] --- Transactor
PeerServerA -.- Storage
PeerServerB[Peer Server] --- Transactor
PeerServerB -.- Storage
FlareA(Flare) --- PeerServerA
FlareB(Flare) --- PeerServerA
FlareC(Flare) --- PeerServerB
FlareD(Flare) --- PeerServerB
ApplicationA([Application]) --- FlareA
ApplicationB([Application]) --- FlareA
ApplicationC([Application]) --- FlareB
ApplicationD([Application]) --- FlareB
ApplicationE([Application]) --- FlareC
ApplicationF([Application]) --- FlareC
ApplicationG([Application]) --- FlareD
ApplicationH([Application]) --- FlareD
-
Languages that play well with HTTP and JSON can interact with Datomic right away;
-
Plug and play into any of the many flavors of Datomic's flexible infrastructure architecture;
-
Minimal and transparent layer, not a DSL or framework, just straightforward access to Datomic;
-
Despite JSON, both queries and transactions are done in edn, enabling, e.g., powerful Datalog queries.
-
Languages have different data types, so edn -> JSON -> [Your Language] and vice-versa: something will be lost in translation and expressiveness;
-
An extra layer in the architecture adds a new hop to requests, potentially increasing latency;
-
Being one step away from Clojure reduces our power to fully leverage its types, data structures, immutability, and other desired properties;
-
Some tricks that would be easy to do in Clojure + Datomic become more cumbersome: transaction functions, advanced Datalog datasources, lazy loading, etc.
This API offers GET
endpoints that accept a JSON body, an approach used by projects like Elasticsearch.
If you encounter issues, switch from GET
to POST
. The server will mirror the behavior of the GET
requests.
Semantically, GET
should not have bodies. Semantically, POST
implies side effects, which is not the case here. So, we are aware that both are not ideal.
We considered using query strings with GET
, but parsing was cumbersome and length limits were a risk, so we use GET
with a body and POST
as a fallback.
Future HTTP evolutions may address this use case, such as the proposed HTTP QUERY
method.
clj -M:run
clj -M:repl
clj -M:format
clj -M:lint
cljfmt fix deps.edn src/
clj-kondo --lint deps.edn src/