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"
}
]