This guide shows you how to write data to Fluree using three main patterns: insert, upsert, and update.
- Fluree server running (see Run the Server)
- A ledger created (see Create a Ledger)
Fluree stores data as RDF triples (subject-predicate-object). Transactions are submitted as JSON-LD documents that get converted to triples internally.
This guide uses JSON-LD. The same writes can also be expressed in SPARQL UPDATE or openCypher (CREATE/MERGE/SET/DELETE) — all three land in the same ledger. See the Cypher reference for the property-graph write surface.
{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"@graph": [
{
"@id": "ex:alice",
"@type": "schema:Person",
"schema:name": "Alice"
}
]
}This creates triples like:
ex:alice rdf:type schema:Person
ex:alice schema:name "Alice"
The simplest operation is inserting new entities.
curl -X POST http://localhost:8090/v1/fluree/insert?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"@graph": [
{
"@id": "ex:alice",
"@type": "schema:Person",
"schema:name": "Alice",
"schema:email": "alice@example.org",
"schema:age": 30
}
]
}'Response:
{
"t": 1,
"timestamp": "2024-01-22T10:30:00.000Z",
"commit_id": "bafybeig...commitT1",
"flakes_added": 4,
"flakes_retracted": 0
}curl -X POST http://localhost:8090/v1/fluree/insert?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"@graph": [
{
"@id": "ex:bob",
"@type": "schema:Person",
"schema:name": "Bob",
"schema:email": "bob@example.org"
},
{
"@id": "ex:carol",
"@type": "schema:Person",
"schema:name": "Carol",
"schema:email": "carol@example.org"
}
]
}'curl -X POST http://localhost:8090/v1/fluree/insert?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"@graph": [
{
"@id": "ex:company-a",
"@type": "schema:Organization",
"schema:name": "Acme Corp"
},
{
"@id": "ex:alice",
"@type": "schema:Person",
"schema:name": "Alice",
"schema:worksFor": {"@id": "ex:company-a"}
}
]
}'Upsert (update/insert) replaces values for the predicates you supply on an entity. If the entity doesn't exist, it's created.
Use the dedicated /upsert endpoint:
curl -X POST "http://localhost:8090/v1/fluree/upsert?ledger=mydb:main" \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"@graph": [
{
"@id": "ex:alice",
"@type": "schema:Person",
"schema:name": "Alice Smith",
"schema:email": "alice.smith@example.org",
"schema:age": 31
}
]
}'This replaces existing values for the predicates included in the payload (for ex:alice, those are @type, schema:name, schema:email, schema:age).
First transaction (entity doesn't exist):
- Creates the entity with all specified properties
Subsequent transactions (entity exists):
- Retracts existing values for the supplied predicates
- Asserts new values for those predicates
- Leaves other predicates unchanged
Good for:
- Idempotent transactions (can retry safely)
- Syncing from external systems
- Replacing values for the predicates you supply
- Avoiding duplicate checks
Not good for:
- Conditional/targeted changes (use UPDATE instead)
For targeted changes to existing data, use the UPDATE pattern with WHERE/DELETE/INSERT.
curl -X POST http://localhost:8090/v1/fluree/update?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"where": [
{ "@id": "ex:alice", "schema:age": "?oldAge" }
],
"delete": [
{ "@id": "ex:alice", "schema:age": "?oldAge" }
],
"insert": [
{ "@id": "ex:alice", "schema:age": 32 }
]
}'This pattern:
- WHERE: Finds matching data
- DELETE: Retracts specific triples
- INSERT: Asserts new triples
curl -X POST http://localhost:8090/v1/fluree/update?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"where": [
{ "@id": "ex:alice", "schema:name": "?name", "schema:email": "?email" }
],
"delete": [
{ "@id": "ex:alice", "schema:name": "?name", "schema:email": "?email" }
],
"insert": [
{ "@id": "ex:alice", "schema:name": "Alice Johnson", "schema:email": "alice.j@example.org" }
]
}'Only update if condition is met:
curl -X POST http://localhost:8090/v1/fluree/update?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"where": [
{ "@id": "ex:alice", "schema:age": "?age" },
{ "@id": "?age", "@type": "xsd:integer" }
],
"delete": [
{ "@id": "ex:alice", "schema:age": "?age" }
],
"insert": [
{ "@id": "ex:alice", "schema:age": { "@value": "32", "@type": "xsd:integer" } }
]
}'To add a property without removing existing ones, use INSERT only:
curl -X POST http://localhost:8090/v1/fluree/update?ledger=mydb:main \
-H "Content-Type: application/json" \
-d '{
"@context": {
"ex": "http://example.org/ns/",
"schema": "http://schema.org/"
},
"insert": [
{ "@id": "ex:alice", "schema:telephone": "+1-555-0100" }
]
}'This adds the telephone property without affecting other properties.
Fluree supports various data types through JSON-LD typing:
{
"@id": "ex:alice",
"schema:name": "Alice"
}{
"@id": "ex:alice",
"schema:age": 30,
"schema:height": 1.68
}{
"@id": "ex:alice",
"schema:active": true
}{
"@id": "ex:alice",
"schema:birthDate": {
"@value": "1994-05-15",
"@type": "xsd:date"
}
}{
"@id": "ex:alice",
"schema:lastLogin": {
"@value": "2024-01-22T10:30:00Z",
"@type": "xsd:dateTime"
}
}{
"@id": "ex:alice",
"schema:worksFor": { "@id": "ex:company-a" }
}Every successful transaction returns a receipt with metadata:
{
"t": 5,
"timestamp": "2024-01-22T10:30:00.000Z",
"commit_id": "bafybeig...commitT5",
"flakes_added": 3,
"flakes_retracted": 2,
"previous_commit_id": "bafybeig...commitT4"
}Key fields:
- t: Transaction time (monotonically increasing)
- timestamp: ISO 8601 timestamp
- commit_id: Content-addressed identifier (CID) for the commit
- flakes_added: Number of triples added
- flakes_retracted: Number of triples removed
- previous_commit_id: ContentId of the previous commit (present when t > 1)
See Commit Receipts for details.
If a transaction fails, you'll receive an error response:
{
"error": "TransactionError",
"message": "Invalid IRI: not a valid URI",
"code": "INVALID_IRI"
}Common errors:
- INVALID_IRI: Malformed IRIs
- PARSE_ERROR: Invalid JSON-LD syntax
- TYPE_ERROR: Type mismatch
- CONSTRAINT_VIOLATION: Data constraint violated
Transactions are validated before being applied:
- JSON-LD syntax must be valid
- IRIs must be well-formed
- Types must be compatible
- References must resolve (optional)
- Insert: New entities, no duplication concerns
- Upsert: Idempotent transactions, predicate-level replacement for supplied predicates
- Update: Targeted changes, preserve other properties
Good:
{"@id": "ex:user-12345"}
{"@id": "ex:product-widget-2024"}Bad:
{"@id": "ex:1"}
{"@id": "ex:thing"}Define a clear namespace strategy:
{
"@context": {
"app": "https://myapp.com/ns/",
"schema": "http://schema.org/",
"xsd": "http://www.w3.org/2001/XMLSchema#"
}
}Include related entities in a single transaction:
{
"@graph": [
{"@id": "ex:order-123", "ex:customer": {"@id": "ex:alice"}},
{"@id": "ex:order-123", "ex:product": {"@id": "ex:widget"}},
{"@id": "ex:order-123", "ex:quantity": 5}
]
}Be explicit about types for dates, numbers, etc.:
{
"@id": "ex:alice",
"ex:birthDate": {
"@value": "1994-05-15",
"@type": "xsd:date"
}
}Be aware of transaction size constraints:
- Recommended: < 1000 triples per transaction
- Maximum: Configurable (default: 10,000 triples)
- Large imports: Use batch processing
See Indexing Side-Effects for performance considerations.
Now that you can write data:
- Query Data - Learn how to retrieve your data
- Transactions Overview - Detailed transaction documentation
- JSON-LD Context - Understanding @context
- Insert - Detailed insert documentation
- Upsert - Detailed upsert documentation
- Update - Detailed update documentation
- Cypher - openCypher reads and writes
- Data Types - Comprehensive type system guide