Skip to main content

Core Concepts

Before diving deep into Fluree, it helps to understand a few key concepts that make it different from traditional databases.

Ledgers

A ledger is Fluree's term for a database. Unlike traditional databases that only store current state, a ledger maintains a complete, immutable history of all changes.


Ledger = Current State + Complete History

Every transaction creates a new commit that records what changed. You can query any point in time or see exactly how data evolved.

Naming Convention: Ledgers use a namespace/name format, like myapp/users or production/inventory.


Entities and Triples

Fluree stores data as triples — three-part statements in the form:


Subject → Predicate → Object

For example:

  • ex:aliceex:name"Alice"
  • ex:aliceex:age30
  • ex:aliceex:knowsex:bob

An entity is a subject with all its predicates and objects:


{
"@id": "ex:alice",
"ex:name": "Alice",
"ex:age": 30,
"ex:knows": { "@id": "ex:bob" }
}

This triple-based model enables:

  • Flexible schemas — Add new properties anytime
  • Graph relationships — Entities link to other entities naturally
  • Semantic meaning — Properties have universal definitions

JSON-LD

Fluree uses JSON-LD (JSON for Linked Data) as its data format. JSON-LD is regular JSON with a few special keys:

KeyPurposeExample
@contextDefine namespace prefixes{"ex": "http://example.org/"}
@idUnique identifier for an entity"ex:alice"
@typeThe type/class of an entity"ex:Person"

Why JSON-LD?

JSON-LD gives your data universal meaning. The property ex:name expands to http://example.org/name, making it unambiguous across systems.


{
"@context": {
"ex": "http://example.org/",
"schema": "http://schema.org/"
},
"@id": "ex:alice",
"@type": "schema:Person",
"schema:name": "Alice"
}

This enables:

  • Data portability — Share data with anyone who understands the vocabulary
  • Integration — Combine data from multiple sources that use the same terms
  • Semantic web compatibility — Works with RDF, SPARQL, and linked data standards

Queries with FlureeQL

FlureeQL is Fluree's query language. Queries have three main parts:


{
"from": "my-ledger",
"@context": { "ex": "http://example.org/" },
"select": { "?s": ["*"] },
"where": { "@id": "?s", "@type": "ex:Person" }
}

Logic Variables

Variables start with ? and bind to matching values:


{
"from": "my-ledger",
"@context": { "ex": "http://example.org/" },
"select": ["?name", "?age"],
"where": {
"@id": "?person",
"ex:name": "?name",
"ex:age": "?age"
}
}

This finds all entities with ex:name and ex:age properties.

Graph Traversal

Navigate relationships by nesting patterns:


{
"from": "my-ledger",
"@context": { "ex": "http://example.org/" },
"select": { "?person": ["ex:name", {"ex:knows": ["ex:name"]}] },
"where": { "@id": "?person", "@type": "ex:Person" }
}

This returns each person's name AND the names of everyone they know.


Transactions

Transactions modify data using three operations:

OperationPurpose
insertAdd new data
deleteRemove existing data
whereFind data to delete/update

Insert


{
"ledger": "my-ledger",
"@context": { "ex": "http://example.org/" },
"insert": {
"@id": "ex:alice",
"ex:name": "Alice"
}
}

Update (Delete + Insert)

To update, delete the old value and insert the new one:


{
"ledger": "my-ledger",
"@context": { "ex": "http://example.org/" },
"where": { "@id": "ex:alice", "ex:age": "?oldAge" },
"delete": { "@id": "ex:alice", "ex:age": "?oldAge" },
"insert": { "@id": "ex:alice", "ex:age": 31 }
}

Upsert

The /fluree/upsert endpoint simplifies updates — it inserts if the entity doesn't exist, or updates if it does:


{
"@id": "ex:alice",
"ex:age": 31
}


Time Travel

Every transaction creates a new point in time, identified by t (transaction number). You can query any historical state:


{
"from": "my-ledger",
"@context": { "ex": "http://example.org/" },
"select": { "ex:alice": ["*"] },
"t": 5
}

Or query a range of changes:


{
"from": "my-ledger",
"@context": { "ex": "http://example.org/" },
"history": "ex:alice",
"t": { "from": 1, "to": 10 }
}


Policies (Access Control)

Fluree implements data-centric security — access rules are defined in the data itself, not in application code.

A policy is a query that determines if access is allowed:


{
"@id": "ex:readOwnData",
"f:action": { "@id": "f:view" },
"f:query": {
"@type": "@json",
"@value": {
"where": ["filter", "(= ?$this ?$identity)"]
}
}
}

This policy allows users to view only their own data. The ?$this variable is the entity being accessed, and ?$identity is the requesting user.


Key Differences from SQL Databases

SQL DatabaseFluree
Tables with fixed columnsFlexible entities with any properties
Rows identified by primary keysEntities identified by IRIs
Foreign keys for relationshipsDirect entity references
Current state onlyComplete history built-in
Application-level securityData-level security policies
Proprietary data formatStandard JSON-LD format

Next Steps

Now that you understand the core concepts: