Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Insert

Insert operations add new data to Fluree. This is the most common transaction type for creating new entities and relationships.

Basic Insert

Single Entity

Insert a single entity with properties:

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

Result:

{
  "t": 1,
  "timestamp": "2024-01-22T10:30:00.000Z",
  "commit_id": "bafybeig...commitT1",
  "flakes_added": 4,
  "flakes_retracted": 0
}

This creates 4 triples:

ex:alice rdf:type schema:Person
ex:alice schema:name "Alice"
ex:alice schema:email "alice@example.org"
ex:alice schema:age 30

Multiple Entities

Insert multiple entities in one transaction:

{
  "@context": {
    "ex": "http://example.org/ns/",
    "schema": "http://schema.org/"
  },
  "@graph": [
    {
      "@id": "ex:alice",
      "@type": "schema:Person",
      "schema:name": "Alice"
    },
    {
      "@id": "ex:bob",
      "@type": "schema:Person",
      "schema:name": "Bob"
    },
    {
      "@id": "ex:carol",
      "@type": "schema:Person",
      "schema:name": "Carol"
    }
  ]
}

Benefits:

  • Atomic: All entities created or none
  • Efficient: Single commit, single index update
  • Consistent: All entities at same transaction time

Insert with Relationships

Create entities with relationships:

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

This creates:

ex:company-a rdf:type schema:Organization
ex:company-a schema:name "Acme Corp"
ex:alice rdf:type schema:Person
ex:alice schema:name "Alice"
ex:alice schema:worksFor ex:company-a

Nested Objects

Create nested structures:

{
  "@context": {
    "ex": "http://example.org/ns/",
    "schema": "http://schema.org/"
  },
  "@graph": [
    {
      "@id": "ex:alice",
      "@type": "schema:Person",
      "schema:name": "Alice",
      "schema:address": {
        "@id": "ex:alice-address",
        "@type": "schema:PostalAddress",
        "schema:streetAddress": "123 Main St",
        "schema:addressLocality": "Springfield",
        "schema:postalCode": "12345"
      }
    }
  ]
}

This creates two entities (alice and alice-address) linked by schema:address.

Multi-Valued Properties

Add multiple values for a property:

{
  "@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", "alice@work.com"],
      "schema:telephone": ["+1-555-0100", "+1-555-0101"]
    }
  ]
}

Creates separate triples for each value:

ex:alice schema:email "alice@example.org"
ex:alice schema:email "alice@work.com"
ex:alice schema:telephone "+1-555-0100"
ex:alice schema:telephone "+1-555-0101"

Typed Literals

Dates

{
  "@id": "ex:alice",
  "schema:birthDate": {
    "@value": "1994-05-15",
    "@type": "xsd:date"
  }
}

Timestamps

{
  "@id": "ex:event",
  "schema:startDate": {
    "@value": "2024-01-22T10:30:00Z",
    "@type": "xsd:dateTime"
  }
}

Numbers

{
  "@id": "ex:product",
  "schema:price": {
    "@value": "29.99",
    "@type": "xsd:decimal"
  }
}

Booleans

{
  "@id": "ex:alice",
  "schema:active": {
    "@value": "true",
    "@type": "xsd:boolean"
  }
}

Or use native JSON boolean:

{
  "@id": "ex:alice",
  "schema:active": true
}

Language Tags

Add language-tagged strings:

{
  "@id": "ex:alice",
  "schema:name": {
    "@value": "Alice",
    "@language": "en"
  },
  "schema:description": [
    { "@value": "Software engineer", "@language": "en" },
    { "@value": "Ingénieure logicielle", "@language": "fr" },
    { "@value": "Softwareingenieurin", "@language": "de" }
  ]
}

The language tag is part of a value’s identity. Two language-tagged values that share the same string but differ in their @language (for example "animal"@en and "animal"@fr) are distinct facts and are both retained; they are not deduplicated against each other.

Blank Nodes

Create entities without explicit IRIs:

{
  "@context": {
    "ex": "http://example.org/ns/",
    "schema": "http://schema.org/"
  },
  "@graph": [
    {
      "@id": "ex:alice",
      "schema:address": {
        "@type": "schema:PostalAddress",
        "schema:streetAddress": "123 Main St"
      }
    }
  ]
}

Fluree generates a unique IRI for the blank node address.

Adding to Existing Entities

Add properties to existing entities:

Initial Insert (t=1):

{
  "@graph": [
    {
      "@id": "ex:alice",
      "schema:name": "Alice"
    }
  ]
}

Add Email (t=2):

{
  "@graph": [
    {
      "@id": "ex:alice",
      "schema:email": "alice@example.org"
    }
  ]
}

After t=2, ex:alice has both name and email.

Edge Annotations

Attach metadata to a specific edge via an @annotation block on the object node-map. The annotation reifies the (subject, predicate, object) triple it sits under:

{
  "@id": "ex:alice",
  "ex:worksFor": {
    "@id": "ex:acme",
    "@annotation": {
      "ex:role": "Engineer",
      "ex:since": { "@value": "2024-01-01", "@type": "xsd:date" }
    }
  }
}

@edge is an alias for @annotation; the two are interchangeable.

The annotation subject can be left anonymous (a blank node is minted) or pinned to an explicit IRI for stable identity:

"@annotation": {
  "@id": "ex:emp/alice-acme-2024",
  "ex:role": "Engineer"
}

Multiple parallel annotations on the same (s, p, o) edge are supported — assert two @annotation blocks on separate copies of the edge in the same transaction (each with its own @id to keep them distinct):

{
  "@graph": [
    { "@id": "ex:alice", "ex:worksFor": {
        "@id": "ex:acme",
        "@annotation": { "@id": "ex:emp/2020", "ex:role": "Engineer" }
    }},
    { "@id": "ex:alice", "ex:worksFor": {
        "@id": "ex:acme",
        "@annotation": { "@id": "ex:emp/2024", "ex:role": "Manager" }
    }}
  ]
}

Inline @annotation queries return one row per occurrence.

Annotated literal-valued edges are supported — write the object as a JSON-LD value object so the annotation has a sibling key to attach to:

{
  "@id": "ex:alice",
  "ex:name": {
    "@value": "Alice",
    "@annotation": { "ex:source": "ex:hr-system" }
  }
}

Deferred shapes error with explicit messages:

  • Annotations on list-occurrence triples (@list membership).
  • Reifiers for unasserted triples (@reifies must point at an asserted edge).
  • Multi-triple @reifies (more than one predicate-object pair under @reifies).
  • Annotation-of-annotation (nested @annotation inside an annotation body).
  • @reifies on the insert side (use the inline @annotation form instead).
  • Hand-authored mention of the reserved system predicates that back annotations (compact or full IRI form).

For the full surface — including SPARQL 1.2 / RDF 1.2 annotation tails ({| |}), the named reifier (~), the cardinality / multiplicity contract, anonymous vs explicit-IRI lifecycle, and named-graph behavior — see the Edge annotations concept doc. For cascade semantics when a base edge or annotation metadata is removed, see Retractions.

Insert Semantics

Additive by Default

Inserts are additive—they don’t remove existing data:

t=1: INSERT { ex:alice schema:name "Alice" }
     Result: ex:alice has name "Alice"

t=2: INSERT { ex:alice schema:age 30 }
     Result: ex:alice has name "Alice" AND age 30

Duplicate Prevention

Inserting the same triple again is a no-op:

t=1: INSERT { ex:alice schema:name "Alice" }
t=2: INSERT { ex:alice schema:name "Alice" }
     (No change—triple already exists)

A triple’s identity includes its datatype and language tag, so values that differ only by language tag are not duplicates:

INSERT { ex:alice schema:name "Alice"@en, "Alice"@fr }
     Result: ex:alice has TWO name values (one per language tag)

Multi-Value Handling

Multiple values create multiple triples:

t=1: INSERT { ex:alice schema:email "alice@example.org" }
t=2: INSERT { ex:alice schema:email "alice@work.com" }
     Result: ex:alice has TWO email values

IRI Generation

Explicit IRIs

Specify IRIs explicitly:

{
  "@id": "ex:user-12345",
  "schema:name": "Alice"
}

UUID-Based IRIs

Generate UUIDs for unique IRIs:

const uuid = crypto.randomUUID();
const entity = {
  "@id": `ex:user-${uuid}`,
  "schema:name": "Alice"
};

Content-Addressable IRIs

Use content hashing for deterministic IRIs:

const hash = sha256(JSON.stringify(data));
const entity = {
  "@id": `ex:entity-${hash}`,
  ...data
};

Batch Inserts

{
  "@graph": [
    { "@id": "ex:user-1", "schema:name": "Alice" },
    { "@id": "ex:user-2", "schema:name": "Bob" },
    { "@id": "ex:user-3", "schema:name": "Carol" }
    // ... 100-1000 entities
  ]
}

Large Imports

For very large imports:

const batchSize = 1000;
for (let i = 0; i < entities.length; i += batchSize) {
  const batch = entities.slice(i, i + batchSize);
  await transact({ "@graph": batch });
  
  // Optional: wait for indexing
  await sleep(1000);
}

Error Handling

Common Insert Errors

Invalid IRI:

{
  "error": "ValidationError",
  "message": "Invalid IRI format",
  "code": "INVALID_IRI"
}

Type Mismatch:

{
  "error": "TypeError",
  "message": "Expected number, got string",
  "code": "TYPE_ERROR"
}

Constraint Violation:

{
  "error": "ConstraintViolation",
  "message": "Unique constraint violated",
  "code": "CONSTRAINT_VIOLATION"
}

Validation Before Insert

Validate data before inserting:

function validateEntity(entity) {
  if (!entity['@id']) {
    throw new Error('Entity must have @id');
  }
  if (!isValidIRI(entity['@id'])) {
    throw new Error('Invalid IRI format');
  }
  // Additional validation...
}

Best Practices

1. Use Meaningful IRIs

Good:

{ "@id": "ex:user-alice-12345" }

Bad:

{ "@id": "ex:1" }

2. Always Include Type

{
  "@id": "ex:alice",
  "@type": "schema:Person"
}

3. Use Appropriate Datatypes

{
  "schema:age": 30,
  "schema:price": 29.99,
  "schema:active": true,
  "schema:birthDate": { "@value": "1994-05-15", "@type": "xsd:date" }
}

Insert related entities in same transaction:

{
  "@graph": [
    { "@id": "ex:order-123", ... },
    { "@id": "ex:order-item-1", ... },
    { "@id": "ex:order-item-2", ... }
  ]
}

5. Use Consistent Namespaces

Define and use consistent namespace prefixes:

{
  "@context": {
    "app": "https://myapp.com/ns/",
    "schema": "http://schema.org/"
  }
}

6. Include Metadata

Add creation metadata:

{
  "@id": "ex:alice",
  "schema:name": "Alice",
  "app:createdAt": "2024-01-22T10:00:00Z",
  "app:createdBy": "user-admin"
}

7. Validate Before Insert

Always validate:

  • JSON-LD syntax
  • IRI formats
  • Required fields
  • Type constraints

Performance Tips

1. Batch Appropriately

  • Recommended: 100-1000 entities per batch
  • Too small: Many commits, slow
  • Too large: Memory pressure, long commits

2. Monitor Indexing

Track indexing lag after large inserts:

curl http://localhost:8090/v1/fluree/info/mydb:main
# Check: t - index.t

3. Use Efficient IRIs

Short IRIs are more efficient:

Good: ex:user-123 Less efficient: https://example.org/very/long/path/user-123

4. Minimize Context Size

Use compact contexts:

{
  "@context": {
    "ex": "http://example.org/ns/",
    "schema": "http://schema.org/"
  }
}