Skip to main content

Cookbook

This is a cookbook of all the API calls that are available to the Fluree HTTP API.


info

If you have a Fluree ledger (self-hosted or on Nexus), you can run the entirety of the following Cookbook through Postman.

Run in Postman

Simply update the request URLs to appropriately target your Fluree instance (as the default is set to http://localhost:58090). If you are running against a locally-hosted instance, make sure to use Postman for Desktop to enable localhost requests.

[DO FIRST] SETUP​


note

If you're using the Cookbook examples below, make sure to start by creating a ledger with the following API request.

Ledger Creation​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"schema:age": 4,
"schema:name": "Freddy",
"ex:verified": true
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"schema:age": 2,
"ex:nickname": "Letty",
"schema:name": "Leticia",
"schema:follows": [{ "@id": "ex:freddy" }]
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:name": "Betty",
"schema:follows": [{ "@id": "ex:freddy" }]
},
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:name": "Andrew Johnson",
"schema:follows": [
{ "@id": "ex:freddy" },
{ "@id": "ex:letty" },
{ "@id": "ex:betty" }
]
}
]
}


Querying​


Select Statements​

tip

The following query examples demonstrate the various ways to use the select keyword in Fluree Queries.


Graph Queries​

info

We're able to use the select key in order to express graph crawls across our data. By following property paths that relate one entity to another, we are able to express graph crawls that include interconnected data in our query results.


* (Wildcard)​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": { "?s": ["*"] }
}

Example Response

[
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
}
]


Graph Crawls​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": { "?s": ["schema:name", "schema:age"] }
}

Example Response

[
{
"schema:age": 35,
"schema:name": "Andrew Johnson"
},
{
"schema:age": 82,
"schema:name": "Betty"
},
{
"schema:age": 4,
"schema:name": "Freddy"
},
{
"schema:age": 2,
"schema:name": "Leticia"
}
]


Expanded Graph Crawls​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*", { "schema:follows": ["*"] }]
}
}

Example Response

[
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
}
],
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Leticia"
}
]


Graph Crawls by Depth​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": { "?s": ["*"] },
"depth": 3
}

Example Response

[
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Leticia"
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Betty"
}
],
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"schema:age": 4,
"schema:name": "Freddy"
},
"schema:name": "Leticia"
}
]


SelectDistinct​

info

We can replace our select key with selectDistinct. As the following queries demonstrate, if our result set might have included identical entries, selectDistinct will ensure the returned results only include unique entries.


Without Distinct​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:follows": {
"schema:name": "?nameOfFollower"
}
},
"select": "?nameOfFollower"
}

Example Response

[
"Freddy",
"Freddy",
"Freddy",
"Leticia",
"Betty"
]


With Distinct​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:follows": {
"schema:name": "?nameOfFollower"
}
},
"selectDistinct": "?nameOfFollower"
}

Example Response

[
"Freddy",
"Leticia",
"Betty"
]


Grouped Aggregates​

info

Often, our query results are more useful to us when grouped according to a particular logic variable. If we want to group our results, we can use the groupBy key to express to Fluree that query results should be grouped accordingly.


Ungrouped​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:follows": "?follows"
},
"select": ["?name", "(count ?follows)"]
}

Example Response

[
[
[
"Andrew Johnson",
"Andrew Johnson",
"Andrew Johnson",
"Betty",
"Leticia"
],
5
]
]


Grouped​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:follows": "?follows"
},
"select": ["?name", "(count ?follows)"],
"groupBy": "?name"
}

Example Response

[
[
"Andrew Johnson",
3
],
[
"Betty",
1
],
[
"Leticia",
1
]
]


Reverse Graph Crawls​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"from": "cookbook/base",
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"friended": { "@reverse": "schema:follows" }
},
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["@id", "schema:name", { "friended": ["@id", "schema:name"] }]
}
}

Example Response

[
{
"@id": "ex:andrew",
"friended": [],
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:betty",
"friended": {
"@id": "ex:andrew",
"schema:name": "Andrew Johnson"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"friended": [
{
"@id": "ex:andrew",
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:betty",
"schema:name": "Betty"
},
{
"@id": "ex:letty",
"schema:name": "Leticia"
}
],
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"friended": {
"@id": "ex:andrew",
"schema:name": "Andrew Johnson"
},
"schema:name": "Leticia"
}
]


Direct Binding Results​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": ["?name", "?age", "?type"],
"where": {
"@id": "?s",
"schema:name": "?name",
"schema:age": "?age",
"@type": "?type"
}
}

Example Response

[
[
"Andrew Johnson",
35,
"schema:Person"
],
[
"Betty",
82,
"ex:Yeti"
],
[
"Freddy",
4,
"ex:Yeti"
],
[
"Leticia",
2,
"ex:Yeti"
]
]


Where Clauses​

tip

The following query examples demonstrate the various ways to use the where keyword in Fluree Queries. The where keyword sets constraints on the data we're querying for, allowing us to do things like filter and join data based on our query needs. We are also able to capture logic variables that can be re-used in the select statement.


Multiple Constraints​

info

We can constrain the scope of our query in various ways. The following examples demonstrate how to set constraints on such considerations as properties, types, values, or a combination of all of these.


Property Constraint​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name"
},
"select": "?name"
}

Example Response

[
"Andrew Johnson",
"Betty",
"Freddy",
"Leticia"
]


Type Constraint​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"@type": "ex:Yeti"
},
"select": "?s"
}

Example Response

[
"ex:betty",
"ex:letty",
"ex:freddy"
]


Value Constraint​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "Freddy"
},
"select": "?s"
}

Example Response

[
"ex:freddy"
]


Multiple Constraints​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@type": "ex:Yeti",
"ex:verified": true,
"schema:name": "?name"
},
"select": "?name"
}

Example Response

[
"Freddy"
]


Optional Constraints​

info

Some constraints might implicitly filter out certain data unless we express those constraints as optional. Optional constraints return null values for logic variables when no fact exists, rather than implicitly dropping results with no matching condition.


Without Optional​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@type": "ex:Yeti",
"ex:verified": "?verified",
"schema:name": "?name"
},
"select": ["?name", "?verified"]
}

Example Response

[
[
"Freddy",
true
]
]


With Optional​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": [
{
"@id": "?s",
"@type": "ex:Yeti",
"schema:name": "?name"
},
[
"optional",
{
"@id": "?s",
"ex:verified": "?verified"
}
]
],
"select": ["?name", "?verified"]
}

Example Response

[
[
"Betty",
null
],
[
"Freddy",
true
],
[
"Leticia",
null
]
]


Filtering Functions​

info

While we can implicitly filter data with joins expressed via logic variables, we can also express filter functions that can filter based on various functions and logical expressions. The following examples demonstrate these capabilities.


Unfiltered​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
],
[
"Freddy",
4
],
[
"Leticia",
2
]
]


Basic Filtering​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": [
{
"schema:name": "?name",
"schema:age": "?age"
},
["filter", "(> ?age 10)"]
],
"select": ["?name", "?age"]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
]
]


Multiple Filters​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": [
{
"schema:name": "?name",
"schema:age": "?age"
},
["filter", "(> ?age 10)", "(< ?age 50)"]
],
"select": ["?name", "?age"]
}

Example Response

[
[
"Andrew Johnson",
35
]
]


Union / Full Outer Join​

info

Generally, joining data via logic variables performs an inner join. If we want to join data that might match a logic variable via a logical OR as opposed to a logical AND, we can use the union keyword. This effectively performs a full outer join. The following examples demonstrate this capability.


Without Union​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"ex:nickname": "?name"
},
"select": "?name"
}

Example Response

[]


With Union​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": [["union", { "schema:name": "?name" }, { "ex:nickname": "?name" }]],
"select": "?name"
}

Example Response

[
"Andrew Johnson",
"Betty",
"Freddy",
"Leticia",
"Letty"
]


Binding Calculated Values​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": ["?name", "?age", "?canVote"],
"where": [
{
"schema:name": "?name",
"schema:age": "?age"
},
["bind", "?canVote", "(>= ?age 18)"]
]
}

Example Response

[
[
"Andrew Johnson",
35,
true
],
[
"Betty",
82,
true
],
[
"Freddy",
4,
false
],
[
"Leticia",
2,
false
]
]


Other Keywords​

tip

In the examples above, we explored the various keywords and syntax for select and where clause expressions. The following keywords exist outside of those clauses at the top-level of a query. They can perform things like grouping, grouped-filtering, ordering, and value-specification.


Grouping Results​

info

Often, our query results are more useful to us when grouped according to a particular logic variable. If we want to group our results, we can use the groupBy key to express to Fluree that query results should be grouped accordingly.


Ungrouped​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name"
},
"select": "?name"
}

Example Response

[
"Andrew Johnson",
"Betty",
"Freddy",
"Leticia"
]


Grouped​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@type": "?type",
"schema:name": "?name"
},
"select": ["?type", "?name"],
"groupBy": "?type"
}

Example Response

[
[
"schema:Person",
[
"Andrew Johnson"
]
],
[
"ex:Yeti",
[
"Betty",
"Freddy",
"Leticia"
]
]
]


Filtering Grouped Results​

info

If we've used groupBy to group results according to particular binding, we can also filter grouped results with the having key. If we use filter to attempt this, the filtering woudl take place across all values, rather than values specific to a particular grouped subset. The following examples demonstrate this capability.


Unfiltered Group​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"groupBy": "?name"
}

Example Response

[
[
"Andrew Johnson",
[
35
]
],
[
"Betty",
[
82
]
],
[
"Freddy",
[
4
]
],
[
"Leticia",
[
2
]
]
]


Filtered Group​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"groupBy": "?name",
"having": "(> (avg ?age) 10)"
}

Example Response

[
[
"Andrew Johnson",
[
35
]
],
[
"Betty",
[
82
]
]
]


Ordering Results​

info

Often we want our result set ordered according to a particular logic variable (e.g. ?age or ?priority). We can use the orderBy key to accomplish this, and the following examples demonstrate this capability.


Unordered Results​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
],
[
"Freddy",
4
],
[
"Leticia",
2
]
]


Ordered Results​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"orderBy": "?age"
}

Example Response

[
[
"Leticia",
2
],
[
"Freddy",
4
],
[
"Andrew Johnson",
35
],
[
"Betty",
82
]
]


Specifying Values​

info

Often we might want to use a logic variable binding in our where or select statements, but we also want to constrain the possibility of values for that logic variable to a certain single or multiple value. The following examples demonstrate that set of capabilities.


Without Specified Values​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
],
[
"Freddy",
4
],
[
"Leticia",
2
]
]


With Single Specified Value​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"values": ["?age", [35]]
}

Example Response

[
[
"Andrew Johnson",
35
]
]


With Multiple Values on One Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"values": ["?age", [35, 82]]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
]
]


With Multiple Properties​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:name": "?name",
"schema:age": "?age"
},
"select": ["?name", "?age"],
"values": [
["?name", "?age"],
[
["Andrew Johnson", 35],
["Betty", 82]
]
]
}

Example Response

[
[
"Andrew Johnson",
35
],
[
"Betty",
82
]
]


Subclasses​

tip

Because Fluree is a semantic graph database, we are able to establish relationships not just across data object entities, but across our vocabulary concepts, which themselves are simply data. When those semantic relationships exist, Fluree can make inferences about our data at query-time. The following examples demonstrate how subclass relationships make it possible to query for entities of a parent class and see query results for data that is only explicitly described as an instance of that parent's subclasses.


Add Parent Class​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:Humanoid",
"@type": "rdfs:Class"
},
{
"@id": "ex:Yeti",
"rdfs:subClassOf": { "@id": "ex:Humanoid" }
},
{
"@id": "schema:Person",
"rdfs:subClassOf": { "@id": "ex:Humanoid" }
}
]
}


Parent Class Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"@type": "ex:Humanoid"
},
"select": {
"?s": ["*"]
}
}


Equivalent Property​

tip

Because Fluree is a semantic graph database, we are able to establish relationships not just across data object entities, but across our vocabulary concepts, which themselves are simply data. When those semantic relationships exist, Fluree can make inferences about our data at query-time. The following examples demonstrate how equivalent proeprty relationships make it possible to query for values on particular properties, but see results for symmetically- or transitively-qualified equivalent properties.


Populate test data​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"foaf": "http://xmlns.com/foaf/0.1/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:andrew",
"schema:givenName": "Andrew"
},
{
"@id": "ex:freddy",
"foaf:name": "Freddy"
},
{
"@id": "ex:letty",
"ex:firstName": "Leticia"
},
{
"@id": "ex:betty",
"ex:firstName": "Betty"
}
]
}


Map equivalent properties​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"schema": "http://schema.org/",
"ex": "http://example.org/",
"foaf": "http://xmlns.com/foaf/0.1/",
"owl": "http://www.w3.org/2002/07/owl#"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "schema:givenName",
"@type": "rdf:Property"
},
{
"@id": "ex:firstName",
"@type": "rdf:Property",
"owl:equivalentProperty": { "@id": "schema:givenName" }
},
{
"@id": "foaf:name",
"@type": "rdf:Property",
"owl:equivalentProperty": { "@id": "ex:firstName" }
}
]
}


Query for property defined to be equivalent​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"schema:givenName": "?name"
},
"selectDistinct": "?name"
}

Example Response

[
"Betty",
"Leticia",
"Andrew",
"Freddy"
]


Query for symmetric property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/"
},
"from": "cookbook/base",
"where": {
"ex:firstName": "?name"
},
"selectDistinct": "?name"
}

Example Response

[
"Betty",
"Leticia",
"Andrew",
"Freddy"
]


Query for transitive properties​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"foaf": "http://xmlns.com/foaf/0.1/"
},
"from": "cookbook/base",
"where": {
"foaf:name": "?name"
},
"select": "?name"
}

Example Response

[
"Betty",
"Leticia",
"Andrew",
"Freddy"
]


Querying with the graph crawl​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"foaf": "http://xmlns.com/foaf/0.1/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"foaf:name": "?name"
},
"select": {
"?s": ["*"]
}
}

Example Response

[
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
},
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
}
]


Aliasing Queries (Property #1)​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"foaf": "http://xmlns.com/foaf/0.1/",
"schema:givenName": "ex:firstName"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:givenName": "?name"
},
"select": {
"?s": ["*"]
}
}

Example Response

[
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:givenName": "Betty",
"schema:name": "Betty"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:givenName": "Leticia",
"schema:name": "Leticia"
},
{
"@id": "ex:andrew",
"@type": "schema:Person",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": "Andrew Johnson"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
}
]


SPARQL​

tip

Because Fluree is a semantic graph database, specifically a JSON-LD database, it is capable of understanding and resolving queries expressed via SPARQL (the SPARQL Protocol and RDF Query Language). The following examples demonstrate some basic SPARQL queries. (NOTE: these requests are expressed as _Content-Type: application/sparql-query_ and not _application-json_)


Basic SPARQL Query​

Request Headers

  • Content-Type: application/sparql-query

Request Body


/*
Action: POST
Endpoint: /query
*/
PREFIX ex: <http://example.org/>
PREFIX schema:<http://schema.org/>
SELECT ?yeti ?name
FROM <cookbook/base>
WHERE {
?yeti <@type> ex:Yeti;
schema:name ?name.
}

Example Response

[
[
"ex:betty",
"Betty"
],
[
"ex:letty",
"Leticia"
],
[
"ex:freddy",
"Freddy"
]
]


Average Aggregate Query​

Request Headers

  • Content-Type: application/sparql-query

Request Body


/*
Action: POST
Endpoint: /query
*/
PREFIX schema:<http://schema.org/>
SELECT (AVG(?age) AS ?avgAge)
FROM <cookbook/base>
WHERE {
?s schema:age ?age.
}

Example Response

[
[
30.75
]
]


Count Aggregate Query​

Request Headers

  • Content-Type: application/sparql-query

Request Body


/*
Action: POST
Endpoint: /query
*/
PREFIX schema:<http://schema.org/>
SELECT (COUNT(?friend) AS ?numFriends)
FROM <cookbook/base>
WHERE {
?s schema:follows ?friend.
}

Example Response

[
[
5
]
]


[BUG] Filter Query​

Request Headers

  • Content-Type: application/sparql-query

Request Body


/*
Action: POST
Endpoint: /query
*/
PREFIX schema:<http://schema.org/>
SELECT ?s ?age
FROM <cookbook/base>
WHERE {
?s schema:age ?age .
FILTER(?age > 10) .
}


Transacting​


Inserting Data​

tip

When transacting data, we use either the insert or delete keys (or a combination of the two to express a kind of update). The following examples demonstrate how to use the insert keyword to assert new facts against your current data state.


Creating a Ledger​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/"
},
"ledger": "cookbook/create",
"insert": [
{
"@id": "ex:note1",
"@type": "ex:Note",
"ex:description": "To create a ledger, you need to provide some initial data, just like you would make an 'initial commit' when using git"
}
]
}


Inserting a Single Record​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data"
}
]
}


Inserting Multiple Records​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:w3c",
"@type": "schema:Organization",
"schema:description": "We ❤️ Internet"
},
{
"@id": "ex:mosquitos",
"@type": "ex:Monster",
"schema:description": "We ❤️ Human Blood"
}
]
}


Deleting Data​

tip

When transacting data, we use either the insert or delete keys (or a combination of the two to express a kind of update). The following examples demonstrate how to use the delete keyword to retract facts from your current data state.


[DO FIRST] Add Data to Delete​

info

We include the following new data insert so that we have sufficient data to subsequently delete in the examples that follow this one.


Add Data to Delete​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data"
},
{
"@id": "ex:w3c",
"@type": "schema:Organization",
"schema:description": "We ❤️ Internet"
},
{
"@id": "ex:mosquitos",
"@type": "ex:Monster",
"schema:description": "We ❤️ Human Blood"
}
]
}


Deleting Entire Subjects​

info

Often we want to not simply delete an individual set of facts, but every fact across a subject. In SQL, this effectively drops a row, but in JSON-LD / RDF, subjects (aka rows) only exist insofar as they have property-object facts expressed for them. Therefore, deletions of entire subjects are effectively deletions of every property-object fact for that subject. The following examples demonstrate how to express these kinds of delete statements.


Querying Before Delete​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:description": "?o"
},
"selectDistinct": { "?s": ["*"] }
}

Example Response

[
{
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data"
},
{
"@id": "ex:mosquitos",
"@type": "ex:Monster",
"schema:description": "We ❤️ Human Blood"
},
{
"@id": "ex:w3c",
"@type": "schema:Organization",
"schema:description": "We ❤️ Internet"
}
]


Deleting All Facts on Subject​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "ex:mosquitos",
"?p": "?o"
},
"delete": {
"@id": "ex:mosquitos",
"?p": "?o"
}
}


Querying After Delete​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:description": "?o"
},
"selectDistinct": { "?s": ["*"] }
}

Example Response

[
{
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data"
},
{
"@id": "ex:w3c",
"@type": "schema:Organization",
"schema:description": "We ❤️ Internet"
}
]


Deleting Particular Facts​

info

When we want to keep some facts about a subject in our data state, but want to delete or update particular facts, we can use where clauses to subselect current data state for a subject, and then use the delete clause to retract those particular data state elements. The following examples demonstrate how to delete particular facts in this way.


Deleting Values on a Single Predicate for a Single Subject​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "ex:mosquitos",
"schema:description": "?o"
},
"delete": {
"@id": "ex:mosquitos",
"schema:description": "?o"
}
}


Deleting All Subjects with a Particular Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "?s",
"schema:description": "?x",
"?p": "?o"
},
"delete": {
"@id": "?s",
"?p": "?o"
}
}


Deleting All Subjects with a Particular Value on a Particular Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "?s",
"@type": "schema:Organization",
"?p": "?o"
},
"delete": {
"@id": "?s",
"?p": "?o"
}
}


Updating Data​

tip

When we think about updating data, we typically mean that we want to replace an existing value or set of values with updated data state. In JSON-LD, this is expressed as a delete + insert. The following examples demonstrate how to perform these kinds of udpates.


[DO FIRST] Add Data to Update​

info

We include the following new data insert so that we have sufficient data to subsequently delete in the examples that follow this one.


Add Data to Update​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data"
},
{
"@id": "ex:w3c",
"@type": "schema:Organization",
"schema:description": "We ❤️ Internet"
},
{
"@id": "ex:mosquitos",
"@type": "ex:Monster",
"schema:description": "We ❤️ Human Blood"
}
]
}


Update​


Update Regardless of Value​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "ex:mosquitos",
"schema:description": "?o"
},
"delete": {
"@id": "ex:mosquitos",
"schema:description": "?o"
},
"insert": {
"@id": "ex:mosquitos",
"schema:description": "We ❤️ All Blood"
}
}


Compare and Swap Update (Only Update if Current Value is X)​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"where": {
"@id": "?s",
"schema:description": "We ❤️ All Blood"
},
"delete": {
"@id": "?s",
"schema:description": "We ❤️ All Blood"
},
"insert": {
"@id": "?s",
"schema:description": ["We ❤️ Human Blood", "We ❤️ Animal Blood"]
}
}


Schema​


Default Flexibility​

tip

By default, JSON-LD is flexible by design when it comes to schema restrictions. The very premise of JSON-LD or RDF data is that we are not data's only publishers, and we can leverage and join data across the web's data systems. With that in mind, if data were restricted to schema considerations by default, we may not be able to easily merge and join our data. The following examples demonstrate Fluree's support for multiple data types or class types (aka tables in the SQL world).


Multiple @type Values​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/multiple-type-values",
"insert": {
"@id": "ex:nessie",
"@type": ["schema:Thing", "ex:Cryptid", "ex:SeaMonster"],
"ex:name": "Nessie",
"ex:age": 1458
}
}


Mixed Data Types​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/"
},
"ledger": "cookbook/data-types",
"insert": {
"@id": "ex:nessie",
"@type": "ex:Cryptid",
"ex:name": "Nessie",
"ex:age": [1458, "unknown"]
}
}


SHACL​

tip

While JSON-LD's default posture of flexibility can be of great use when working with multiple producers of data, we may have application obligations to validate our data against certain schema or data shape concerns. The semantic standard for expressing these concerns is SHACL (i.e. the Shapes Contraint Language). The following examples demonstrate how to use SHACL to express constraints like required properties, data types, cardinality, and closed class shapes.


Required Properties​

info

We can use SHACL to express that certain properties may be required on instances of, say, a particular class. The following examples demonstrate how to express required property constraints with SHACL.


Create Ledger w/ SHACL Rule​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"sh": "http://www.w3.org/ns/shacl#"
},
"ledger": "cookbook/required-property",
"insert": {
"@id": "ex:UserShape",
"@type": ["sh:NodeShape"],
"sh:targetClass": { "@id": "ex:User" },
"sh:property": [
{
"sh:path": { "@id": "schema:name" },
"sh:minCount": 1
}
]
}
}


[FAILS] Transact Without Required Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/required-property",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:age": 77
}
}


Transact With Required Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/required-property",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:name": "Dolly Parton"
}
}


Data Type Constraints​

info

We can use SHACL to express that certain properties should be restricted to certain data types. The following examples demonstrate how to express data type constraints with SHACL.


Create Ledger w/ SHACL Rule​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"sh": "http://www.w3.org/ns/shacl#",
"xsd": "http://www.w3.org/2001/XMLSchema#"
},
"ledger": "cookbook/data-type",
"insert": {
"@id": "ex:UserShape",
"@type": ["sh:NodeShape"],
"sh:targetClass": { "@id": "ex:User" },
"sh:property": [
{
"sh:path": { "@id": "schema:name" },
"sh:datatype": { "@id": "xsd:string" }
}
]
}
}


[FAILS] Transact Invalid Data Type​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/data-type",
"insert": {
"@id": "ex:dolly",
"@type": "ex:User",
"schema:name": [9, 5]
}
}


Transact Valid Data Type​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/data-type",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:name": "Dolly Parton"
}
}


Cardinality​

info

We can use SHACL to express that certain properties should be restricted to single-cardinality (where a default, flexible position is that properties can take any number of values on a particular subject). The following examples demonstrate how to express cardinality constraints with SHACL.


Create Ledger w/ SHACL Rule​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"sh": "http://www.w3.org/ns/shacl#"
},
"ledger": "cookbook/cardinality",
"insert": {
"@id": "ex:UserShape",
"@type": ["sh:NodeShape"],
"sh:targetClass": { "@id": "ex:User" },
"sh:property": [
{
"sh:path": { "@id": "schema:name" },
"sh:maxCount": 1
}
]
}
}


Transact Correct Cardinality​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/cardinality",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:name": "Dolly Parton"
}
}


[FAILS] Transact Incorrect Cardinality​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/cardinality",
"insert": {
"@id": "ex:dolly",
"schema:name": ["Dolly Parton", "Queen of Country"]
}
}


Class / Property Constraints (Closed Shape)​

info

We can use SHACL to express that certain classes should be restricted to a close set of properties. The following examples demonstrate how to express closed shape constraints with SHACL.


Create Ledger w/ SHACL Rule​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /create
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"sh": "http://www.w3.org/ns/shacl#",
"xsd": "http://www.w3.org/2001/XMLSchema#"
},
"ledger": "cookbook/closed-shape",
"insert": {
"@id": "ex:UserShape",
"@type": ["sh:NodeShape"],
"sh:targetClass": { "@id": "ex:User" },
"sh:property": [
{
"sh:path": { "@id": "schema:name" },
"sh:datatype": { "@id": "xsd:string" }
}
],
"sh:ignoredProperties": [{ "@id": "@type" }],
"sh:closed": true
}
}


Transact with Valid Shape​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/closed-shape",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:name": "Dolly Parton"
}
}


[FAILS] Transact with Excluded Properties​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/closed-shape",
"insert": {
"@id": "ex:dolly",
"@type": ["ex:User"],
"schema:name": "Dolly Parton",
"ex:quotes": ["If you want the rainbow, you gotta put up with the rain!"]
}
}


JSON-LD Utilities​


Using Context​

tip

In JSON-LD and RDF, entities and vocabulary elements are ideally expressed with globally unique IRIs, making it possible to share open-system meaning around our vocabularies and our data. While this is a tremendous value, it is often tedious to work with fully-qualified IRIs. The notion of context (or @context) allows us to simplify these IRIs and use shorthand to better represent the meaning of our classes, properties, and data entities.


The @context Keyword​

info

The following examples demonstrate how to use the @context keyword in queries and transactions to simplify both our requests against Fluree as well as the result sets that our queries return.


Queries​

info

The following examples demonstrate how to query (and see query results) that leverage a @context or default context.


Without @context​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"from": "cookbook/base",
"where": {
"@id": "?s",
"http://schema.org/name": "Freddy"
},
"select": { "?s": ["*"] }
}

Example Response

[
{
"@id": "http://example.org/freddy",
"@type": "http://example.org/Yeti",
"http://example.org/verified": true,
"http://schema.org/age": 4,
"http://schema.org/name": "Freddy",
"http://xmlns.com/foaf/0.1/name": "Freddy"
}
]


With @context​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"from": "cookbook/base",
"@context": {
"myschema": "http://schema.org/",
"myexample": "http://example.org/",
"myrdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
},
"where": {
"@id": "?s",
"myschema:name": "Freddy"
},
"select": { "?s": ["*"] }
}

Example Response

[
{
"@id": "myexample:freddy",
"@type": "myexample:Yeti",
"http://xmlns.com/foaf/0.1/name": "Freddy",
"myexample:verified": true,
"myschema:age": 4,
"myschema:name": "Freddy"
}
]


Transactions​

info

The following examples demonstrate how to submit transactions that leverage a @context or default context.


Using @context to Specify @type on Properties​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"schema:follows": { "@type": "@id" }
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:freddy",
"schema:follows": ["ex:andrew"]
}
}


Using @base & @vocab​

info

In addition to the @context examples above, we have particular keywords--specifically @base and @vocab--which make it possible to set prefixes for all of our entity IRIs or vocabulary elements, respectively. The following examples demonstrate how to use @base and @vocab.


Without @base or @vocab​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/"
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:nessie",
"@type": "ex:SeaMonster",
"ex:isScary": false
}
}


With @base​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"@base": "http://example.org/"
},
"ledger": "cookbook/base",
"insert": {
"@id": "nessie",
"@type": "ex:SeaMonster",
"ex:isScary": false
}
}


With @base and @vocab​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"@base": "http://example.org/",
"@vocab": "http://example.org/terms/"
},
"ledger": "cookbook/base",
"insert": {
"@id": "nessie",
"@type": "SeaMonster",
"isScary": false
}
}


Using @id​

tip

While vanilla JSON does not have a universal concept for the identity of a particular object/entity, JSON-LD requires such a concept, as part of its adherence to RDF graph serialization. We can use @id to set IRI identifiers for entities, and then leverage those identifiers in queries and transactions. The following examples demonstrate how to do this.


Query by @id​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": {
"ex:freddy": ["*"]
}
}

Example Response

[
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
}
]


Using @id To Specify a String as an IRI (Graph Reference)​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data",
"schema:employee": { "@id": "ex:andrew" }
}
}


Using @id To Specify a String as an IRI (via @context)​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"schema:employee": { "@type": "@id" }
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:fluree",
"@type": "schema:Organization",
"schema:description": "We ❤️ Data",
"schema:employee": "ex:andrew"
}
}


Using @type​

tip

Where SQL data has tables to represent an entity's adherence to a particular data concept, JSON-LD represents this via the notions of classes. Entities can be associated with one or more classes, expressed via @type. Being associated with a class can come with schema restrictions (or not). It makes it easier to query by class, and also makes it possible to infer class relationships, when those classes themselves are related (for example, a class of Yeti might be a sub-class of Humanoid).

The following examples demonstrate how to use @type to work with classes in Fluree.


Class / Subclass Inferencing​

info

Because Fluree is a semantic graph database, we are able to establish relationships not just across data object entities, but across our vocabulary concepts, which themselves are simply data. When those semantic relationships exist, Fluree can make inferences about our data at query-time. The following examples demonstrate how subclass relationships make it possible to query for entities of a parent class and see query results for data that is only explicitly described as an instance of that parent's subclasses.


Add Parent Class​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:Humanoid",
"@type": "rdfs:Class"
},
{
"@id": "ex:Yeti",
"rdfs:subClassOf": { "@id": "ex:Humanoid" }
},
{
"@id": "schema:Person",
"rdfs:subClassOf": { "@id": "ex:Humanoid" }
}
]
}


Parent Class Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"@type": "ex:Humanoid"
},
"select": {
"?s": ["*"]
}
}


Query by @type​

info

When entities are related to a particular class (or to multiple classes), it becomes possible to query for them by leveraging @type. The following example demonstrates how this can be accomplished.

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"@type": "ex:Yeti"
},
"select": {
"?s": ["*"]
}
}

Example Response

[
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
}
]


Time Travel​


Time Travel Queries​

tip

Because each data commit in Fluree is immutable and addressed by the specific moment in time when it took place, it is possible to leverage the concept of time when querying against our data state. In fact, each individual ledger contains an immutable and queryable graph database for every single commit against that ledger.

We can specify any query against a moment in time--and we call this a time-travel query. The following examples demonstrate how to issue time-travel queries in Fluree.


With ISO Strings​

info

Time Travel Queries can be expressed in terms of a particular commit number or in terms of a given dateTime instant. We specifically use ISO 8601 dateTime strings to express the latter. The following examples demonstrate how to issue time-travel queries with ISO strings.


Update Data to Use For Time Travel Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@id": "ex:andrew",
"@type": "ex:Yeti",
"schema:name": "Andy the Yeti"
}
]
}


Query Current Data State​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": {
"ex:andrew": ["*"]
}
}


Query at Previous ISO Time​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": {
"ex:andrew": ["*"]
},
"t": "2023-11-01T18:46:44.823Z"
}


With Commit Numbers​

info

Time Travel Queries can be expressed in terms of a particular commit number or in terms of a given dateTime instant. The following examples demonstrate how to issue time-travel queries with commit numbers.


Update Data to Use For Time Travel Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@id": "ex:andrew",
"@type": "ex:Yeti",
"schema:name": "Andy the Yeti"
}
]
}


Query Current Data State Copy​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": {
"ex:andrew": ["*"]
}
}


Query at Previous Commit Number​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"select": {
"ex:andrew": ["*"]
},
"t": 1
}


History API​

tip

Because each data commit in Fluree is immutable and addressed by the specific moment in time when it took place, it is possible to leverage the concept of time when querying against our data state. In fact, each individual ledger contains an immutable and queryable graph database for every single commit against that ledger.

If we want to audit data state changes over time, for a particular subject or for subjects matching particular data shapes, we can use the History API to accomplish this. The following examples demonstrate how to use the History API to audit our data or our individual commits.


Constraining Results​

info

When using the History API, we can constrain our data audits to particular entities or to entities matching particular data conditions. The following examples demonstrate how to use the History API with particular result constraints.


Update Data to Use For History Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@id": "ex:andrew",
"@type": "ex:Yeti",
"schema:name": "Andy the Yeti"
}
]
}


History of Specific Subject​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "from": 1 }
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
}
],
"f:retract": [],
"f:t": 2
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 8
}
]


History of Specific Subject and Specific Property​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": ["ex:andrew", "schema:name"],
"t": { "from": 1 }
}

Example Response

[
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 8
}
]


History of Specific Property Regardless of Subject​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": [null, "schema:name"],
"t": { "from": 1 }
}

Example Response

[
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andrew Johnson"
},
{
"id": "ex:betty",
"schema:name": "Betty"
},
{
"id": "ex:freddy",
"schema:name": "Freddy"
},
{
"id": "ex:letty",
"schema:name": "Leticia"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 8
}
]


History of Specific Property And Specific Value Regardless of Subject​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": [null, "schema:name", "Andrew Johnson"],
"t": { "from": 1 }
}

Example Response

[
{
"f:assert": [
{
"id": "ex:andrew",
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
}
]


Constraining Time​

info

When using the History API, we can constrain our data audits to moments or to ranges of time. The following examples demonstrate how to use the History API with particular time constraints.


Update Data to Use For History Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@id": "ex:andrew",
"@type": "ex:Yeti",
"schema:name": "Andy the Yeti"
}
]
}


History Until/To Specific Commit​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "to": 2 }
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
}
],
"f:retract": [],
"f:t": 2
}
]


History At Specific Commit​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "at": 1 }
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
}
]


History Since/From Specific Commit​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "from": 1 }
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
}
],
"f:retract": [],
"f:t": 2
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 8
}
]


History Within a Range of DateTime Values​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "from": "2023-11-08T16:24:25.802Z", "to": "2023-11-08T16:29:25.802Z" }
}

Example Response

[
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:retract": [],
"f:t": 8
}
]


History Within a Range of Specific Commits​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "from": 1, "to": 2 }
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
}
],
"f:retract": [],
"f:t": 2
}
]


Including Commit Details​

info

When using the History API, we can optionally include a flag to include the commit details for each commit returned from the History API. The following examples demonstrate how we can use commit-details to flag our interest in this additional metadata.


Update Data to Use For History Query​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"ledger": "cookbook/base",
"insert": [
{
"@id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@id": "ex:andrew",
"@type": "ex:Yeti",
"schema:name": "Andy the Yeti"
}
]
}


History with Commit Details​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"history": "ex:andrew",
"t": { "from": 1 },
"commit-details": true
}

Example Response

[
{
"f:assert": [
{
"@id": "ex:andrew",
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
}
],
"f:commit": {
"@id": "fluree:commit:sha256:bf3w4lfsgjcumhgzalmtoyysyjpp5ns3thrqrqpyzmfd3vmre6m6",
"f:address": "fluree:file://cookbook/base/main/commit/a3e94f6ac348e5eaada827aa896b3c2ecdb62861d7bb91622d6c132654a85ef2.json",
"f:alias": "cookbook/base",
"f:branch": "main",
"f:data": {
"f:address": "fluree:file://cookbook/base/main/commit/bf16b05ca11c84d00c678a5e9fe25466e81bd9689d27eecc62d9a9d95fc48a83.json",
"f:assert": [
{
"@type": "schema:Person",
"id": "ex:andrew",
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:name": "Andrew Johnson"
},
{
"@type": "ex:Yeti",
"id": "ex:betty",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@type": "ex:Yeti",
"ex:nickname": "Letty",
"id": "ex:letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
},
{
"@type": "ex:Yeti",
"ex:verified": true,
"id": "ex:freddy",
"schema:age": 4,
"schema:name": "Freddy"
}
],
"f:flakes": 34,
"f:retract": [],
"f:size": 2251,
"f:t": 1
},
"f:defaultContext": {
"@id": "fluree:context:b16965119d05f836e6a1e221730adf5db0b7212c831a3f83aa52e449fe7ce6ef"
},
"f:time": 1699459809478,
"f:v": 0
},
"f:retract": [],
"f:t": 1
},
{
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
}
],
"f:commit": {
"@id": "fluree:commit:sha256:bf37jqhwcluspckl4umg7icphtnboqr37ygeftzn47alko44lpcc",
"f:address": "fluree:file://cookbook/base/main/commit/f52065581b8cf8b0d7fc824e2c5d5b741ca62d8a383568760e8e86f397153226.json",
"f:alias": "cookbook/base",
"f:branch": "main",
"f:data": {
"f:address": "fluree:file://cookbook/base/main/commit/e8ac97e208e5a788aa81201d9d7240be7d915d4d900e3fe8bccf538aa024f09f.json",
"f:assert": [
{
"id": "ex:andrew",
"schema:givenName": "Andrew"
},
{
"ex:firstName": "Betty",
"id": "ex:betty"
},
{
"ex:firstName": "Leticia",
"id": "ex:letty"
},
{
"foaf:name": "Freddy",
"id": "ex:freddy"
}
],
"f:flakes": 69,
"f:previous": {
"@id": "fluree:db:sha256:bbetc3wntccxlmngw24hjiybrhhqdkabssmfpp3qd6kzttwfo6sdr"
},
"f:retract": [],
"f:size": 5955,
"f:t": 2
},
"f:defaultContext": {
"@id": "fluree:context:b16965119d05f836e6a1e221730adf5db0b7212c831a3f83aa52e449fe7ce6ef"
},
"f:previous": {
"@id": "ex:yeti-mutation-event"
},
"f:time": 1699460631766,
"f:v": 0
},
"f:retract": [],
"f:t": 2
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:commit": {
"@id": "fluree:commit:sha256:bt2ee7btm7wbvlyn6u46f5apc6ls2fsvsltvs7zqe235eqzdlv53",
"f:address": "fluree:file://cookbook/base/main/commit/471ccf7c0556245de7ae0bd2774908a1b677646252d7e36405fb6c70666eeae2.json",
"f:alias": "cookbook/base",
"f:branch": "main",
"f:data": {
"f:address": "fluree:file://cookbook/base/main/commit/a1a37538a92ec5bb76794348f35e07d85b7719bdadaecf81fa067aa64d407596.json",
"f:assert": [
{
"id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:flakes": 175,
"f:previous": {
"@id": "fluree:db:sha256:bqf2ccapdj4bcb3zml7exwhxmfpaohrx7l4w6sdjsqazghlpfdni"
},
"f:retract": [],
"f:size": 15435,
"f:t": 7
},
"f:defaultContext": {
"@id": "fluree:context:b16965119d05f836e6a1e221730adf5db0b7212c831a3f83aa52e449fe7ce6ef"
},
"f:previous": {
"@id": "ex:yeti-mutation-event"
},
"f:time": 1699460884560,
"f:v": 0
},
"f:retract": [],
"f:t": 7
},
{
"f:assert": [
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:commit": {
"@id": "fluree:commit:sha256:bbqa5mugvma5t6vqh7mod3ic5so4socmokp7rmdfjuhfw67jd4vjh",
"f:address": "fluree:file://cookbook/base/main/commit/62a83212d9705f677e90514782756243e837b707a1028f9e1109946961613610.json",
"f:alias": "cookbook/base",
"f:branch": "main",
"f:data": {
"f:address": "fluree:file://cookbook/base/main/commit/9cdc867e47c2dff76a79b921d7fda4690f4334a7bf32e2bce9e88571c8dbd39b.json",
"f:assert": [
{
"id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:flakes": 193,
"f:previous": {
"@id": "fluree:db:sha256:bvswubri4d6tweapnpzi4qgdjipjcbhsyezs25sqnw327m75dmyh"
},
"f:retract": [],
"f:size": 17087,
"f:t": 8
},
"f:defaultContext": {
"@id": "fluree:context:b16965119d05f836e6a1e221730adf5db0b7212c831a3f83aa52e449fe7ce6ef"
},
"f:previous": {
"@id": "ex:yeti-mutation-event"
},
"f:time": 1699460897477,
"f:v": 0
},
"f:retract": [],
"f:t": 8
}
]


Latest Commit Details​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /history
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"from": "cookbook/base",
"t": { "from": "latest" },
"commit-details": true
}

Example Response

[
{
"f:commit": {
"@id": "fluree:commit:sha256:bbqa5mugvma5t6vqh7mod3ic5so4socmokp7rmdfjuhfw67jd4vjh",
"f:address": "fluree:file://cookbook/base/main/commit/62a83212d9705f677e90514782756243e837b707a1028f9e1109946961613610.json",
"f:alias": "cookbook/base",
"f:branch": "main",
"f:data": {
"f:address": "fluree:file://cookbook/base/main/commit/9cdc867e47c2dff76a79b921d7fda4690f4334a7bf32e2bce9e88571c8dbd39b.json",
"f:assert": [
{
"id": "ex:yeti-mutation-event",
"schema:description": "All humans are yetis now!!"
},
{
"@type": "ex:Yeti",
"id": "ex:andrew",
"schema:name": "Andy the Yeti"
}
],
"f:flakes": 193,
"f:previous": {
"@id": "fluree:db:sha256:bvswubri4d6tweapnpzi4qgdjipjcbhsyezs25sqnw327m75dmyh"
},
"f:retract": [],
"f:size": 17087,
"f:t": 8
},
"f:defaultContext": {
"@id": "fluree:context:b16965119d05f836e6a1e221730adf5db0b7212c831a3f83aa52e449fe7ce6ef"
},
"f:previous": {
"@id": "ex:yeti-mutation-event"
},
"f:time": 1699460897477,
"f:v": 0
}
}
]


Policy​


Root Access​

tip

Fluree makes it possible to enforce data-centric security. By that we mean that we can express data access policy directly as data, and co-resident with our data, such that data in Fluree is empowered to evaluate its own access. The concept of data-centric security is crucial in enabling our data to participate with other datasets, to support multiple data-producers and data-consumers, and to enable virtualized queries across databases without risking data leaks whatsoever.

The following examples demonstrate how something like root access could be granted to particular roles or to particular identities.


Adding Root Access Policy​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:rootPolicy",
"@type": ["f:Policy"],
"f:targetNode": { "@id": "f:allNodes" },
"f:allow": [
{
"@id": "ex:rootAccessAllow",
"f:targetRole": { "@id": "ex:rootRole" },
"f:action": [{ "@id": "f:view" }, { "@id": "f:modify" }]
}
]
}
}


Adding Root Identity​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"ledger": "cookbook/base",
"insert": {
"@id": "did:fluree:TfCzWTrXqF16hvKGjcYiLxRoYJ1B8a6UMH6",
"ex:user": { "@id": "ex:andrew" },
"f:role": { "@id": "ex:rootRole" }
}
}


Query as Root Role​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"role": "http://example.org/rootRole"
}
}

Example Response

[
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
}
]


Query as Root Identity​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"did": "did:fluree:TfCzWTrXqF16hvKGjcYiLxRoYJ1B8a6UMH6"
}
}

Example Response

[
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:age": 35,
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:age": 82,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:age": 4,
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:age": 2,
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
}
]


Limiting Access​

tip

Fluree makes it possible to enforce data-centric security. By that we mean that we can express data access policy directly as data, and co-resident with our data, such that data in Fluree is empowered to evaluate its own access. The concept of data-centric security is crucial in enabling our data to participate with other datasets, to support multiple data-producers and data-consumers, and to enable virtualized queries across databases without risking data leaks whatsoever.

The following examples demonstrate how to express limited access to our data--not just to particular roles or identities--but also how we can leverage relationships in the data itself to determine access in powerful, granular ways (NOTE: we refer to this as RelBAC, or Relationship-Based Access Control)


Adding Limited Access Policy​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"ledger": "cookbook/base",
"insert": {
"@id": "ex:yetiPolicy",
"@type": ["f:Policy"],
"f:targetClass": { "@id": "ex:Yeti" },
"f:allow": [
{
"@id": "ex:yetiViewAllow",
"f:targetRole": { "@id": "ex:yetiRole" },
"f:action": [{ "@id": "f:view" }]
}
],
"f:property": [
{
"@id": "ex:yetisViewOnlyOwnAge",
"f:path": { "@id": "schema:age" },
"f:allow": [
{
"@id": "ex:ageViewRule",
"f:targetRole": { "@id": "ex:yetiRole" },
"f:action": [{ "@id": "f:view" }],
"f:equals": {
"@list": [{ "@id": "f:$identity" }, { "@id": "ex:user" }]
}
}
]
}
]
}
}


Adding Identity with Limited Access​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /transact
*/
{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/",
"f": "https://ns.flur.ee/ledger#"
},
"ledger": "cookbook/base",
"insert": {
"@id": "did:fluree:Tf5M4L7SNkziB4Q5gC8Hjuqu9WQKCwKpU1Y",
"ex:user": { "@id": "ex:freddy" },
"f:role": {
"@id": "ex:yetiRole"
}
}
}


Query as Limited Role​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"role": "http://example.org/yetiRole"
}
}

Example Response

[
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
}
]


Query as Limited Role Identity​

Request Headers

  • Content-Type: application/json

Request Body


/*
Action: POST
Endpoint: /query
*/
{
"@context": {
"schema": "http://schema.org/"
},
"from": "cookbook/base",
"where": {
"@id": "?s",
"schema:name": "?name"
},
"select": {
"?s": ["*"]
},
"opts": {
"did": "did:fluree:Tf5M4L7SNkziB4Q5gC8Hjuqu9WQKCwKpU1Y"
}
}

Example Response

[
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:andrew",
"@type": [
"ex:Yeti",
"schema:Person"
],
"schema:follows": [
{
"@id": "ex:freddy"
},
{
"@id": "ex:letty"
},
{
"@id": "ex:betty"
}
],
"schema:givenName": "Andrew",
"schema:name": [
"Andrew Johnson",
"Andy the Yeti"
]
},
{
"@id": "ex:betty",
"@type": "ex:Yeti",
"ex:firstName": "Betty",
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Betty"
},
{
"@id": "ex:freddy",
"@type": "ex:Yeti",
"ex:verified": true,
"foaf:name": "Freddy",
"schema:name": "Freddy"
},
{
"@id": "ex:letty",
"@type": "ex:Yeti",
"ex:firstName": "Leticia",
"ex:nickname": "Letty",
"schema:follows": {
"@id": "ex:freddy"
},
"schema:name": "Leticia"
}
]