Quickstart: Write Data
This guide shows you how to write data to Fluree using three main patterns: insert, upsert, and update.
Prerequisites
- Fluree server running (see Run the Server)
- A ledger created (see Create a Ledger)
Understanding Fluree Transactions
Fluree stores data as RDF triples (subject-predicate-object). Transactions are submitted as JSON-LD documents that get converted to triples internally.
Basic Transaction Structure
{
"@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"
Insert: Adding New Data
The simplest operation is inserting new entities.
Insert a Single Entity
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
}
Insert Multiple 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: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"
}
]
}'
Insert with Relationships
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: Idempotent Transactions
Upsert (update/insert) replaces values for the predicates you supply on an entity. If the entity doesn't exist, it's created.
Basic Upsert
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).
Upsert Behavior
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
Use Cases for Upsert
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)
Update: Targeted Changes (WHERE/DELETE/INSERT)
For targeted changes to existing data, use the UPDATE pattern with WHERE/DELETE/INSERT.
Basic Update
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
Update Multiple Properties
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" }
]
}'
Conditional Update
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" } }
]
}'
Adding Properties (Not Replacing)
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.
Data Types
Fluree supports various data types through JSON-LD typing:
Strings (Default)
{
"@id": "ex:alice",
"schema:name": "Alice"
}
Numbers
{
"@id": "ex:alice",
"schema:age": 30,
"schema:height": 1.68
}
Booleans
{
"@id": "ex:alice",
"schema:active": true
}
Dates
{
"@id": "ex:alice",
"schema:birthDate": {
"@value": "1994-05-15",
"@type": "xsd:date"
}
}
Timestamps
{
"@id": "ex:alice",
"schema:lastLogin": {
"@value": "2024-01-22T10:30:00Z",
"@type": "xsd:dateTime"
}
}
References (Links to Other Entities)
{
"@id": "ex:alice",
"schema:worksFor": { "@id": "ex:company-a" }
}
Transaction Receipts
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.
Error Handling
Transaction Errors
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
Validation
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)
Best Practices
1. Use Appropriate Transaction Pattern
- Insert: New entities, no duplication concerns
- Upsert: Idempotent transactions, predicate-level replacement for supplied predicates
- Update: Targeted changes, preserve other properties
2. Choose Meaningful IRIs
Good:
{"@id": "ex:user-12345"}
{"@id": "ex:product-widget-2024"}
Bad:
{"@id": "ex:1"}
{"@id": "ex:thing"}
3. Use Consistent Namespaces
Define a clear namespace strategy:
{
"@context": {
"app": "https://myapp.com/ns/",
"schema": "http://schema.org/",
"xsd": "http://www.w3.org/2001/XMLSchema#"
}
}
4. Batch Related Changes
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}
]
}
5. Use Typed Literals
Be explicit about types for dates, numbers, etc.:
{
"@id": "ex:alice",
"ex:birthDate": {
"@value": "1994-05-15",
"@type": "xsd:date"
}
}
Transaction Size Limits
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.
Next Steps
Now that you can write data:
- Query Data - Learn how to retrieve your data
- Transactions Overview - Detailed transaction documentation
- JSON-LD Context - Understanding @context
Related Documentation
- Insert - Detailed insert documentation
- Upsert - Detailed upsert documentation
- Update - Detailed update documentation
- Data Types - Comprehensive type system guide