Lifecycle States

Content: Lifecycle States

User Intent

"What do content states mean? When should I use each state?"

Operation

  • SDK Method: updateContent() with state parameter

  • GraphQL Field: content.state (EntityState enum)

  • Entity Type: Content

  • Common Use Cases: Content management, soft delete, archival, workflow states

Content States Overview

Content progresses through states during its lifecycle. Understanding states is critical for content management and querying.

TypeScript (Canonical)

import { Graphlit } from 'graphlit-client';
import { EntityState } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

// Check content state
const content = await graphlit.getContent('content-id');
console.log(`State: ${content.content.state}`);

// Update content state
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled  // Hide from queries
});

// Query only enabled content
const activeContent = await graphlit.queryContents({
  filter: {
    states: [EntityState.Enabled]
  }
});

// Query disabled content (hidden)
const hiddenContent = await graphlit.queryContents({
  filter: {
    states: [EntityState.Disabled]
  }
});

// Query all states (including disabled)
const allContent = await graphlit.queryContents({
  filter: {
    states: [
      EntityState.Enabled,
      EntityState.Disabled,
      EntityState.Created
    ]
  }
});

Entity States

enum EntityState {
  CREATED = 'CREATED',       // Just ingested, not yet processed
  ENABLED = 'ENABLED',       // Fully processed, searchable, active
  DISABLED = 'DISABLED',     // Hidden from default queries (soft delete)
  DELETED = 'DELETED',       // Permanently deleted (rarely seen)
  ARCHIVED = 'ARCHIVED'      // Preserved but not actively used
}

CREATED

  • When: Immediately after ingestion

  • Duration: Brief (during workflow processing)

  • Searchable: No (processing not complete)

  • Use Case: Content is being prepared/extracted

Example:

const content = await graphlit.ingestUri(
  'https://example.com/document.pdf',
  undefined,
  undefined,
  undefined,
  false,  // Returns immediately
  { id: 'workflow-id' }
);

console.log(content.ingestUri.state);  // CREATED

// Wait for processing
let isDone = false;
while (!isDone) {
  const status = await graphlit.isContentDone(content.ingestUri.id);
  isDone = status.isContentDone.result;
  await new Promise(resolve => setTimeout(resolve, 1000));
}

// Check state after processing
const processed = await graphlit.getContent(content.ingestUri.id);
console.log(processed.content.state);  // ENABLED

ENABLED

  • When: After successful workflow processing

  • Searchable: Yes (default state for queries)

  • Use Case: Active, searchable content

Example:

// Default queries only return ENABLED content
const results = await graphlit.queryContents({
  search: "graphlit"
});

// All results will have state = ENABLED
results.contents.results.forEach(content => {
  console.log(content.state);  // ENABLED
});

DISABLED

  • When: Manually disabled by user

  • Searchable: No (unless explicitly queried)

  • Use Case: Soft delete, hide temporarily, compliance hold

Example:

// Disable content (soft delete)
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

// Default queries won't return it
const results = await graphlit.queryContents({
  search: "query"
});
// Content with DISABLED state won't appear

// Must explicitly query disabled content
const disabled = await graphlit.queryContents({
  filter: {
    states: [EntityState.Disabled]
  }
});

DELETED

  • When: Permanently deleted via deleteContent()

  • Searchable: No (content is gone)

  • Use Case: Permanent removal

Example:

// Permanent delete
await graphlit.deleteContent('content-id');

// Content no longer exists
// Can't query or retrieve

ARCHIVED

  • When: Manually archived by user

  • Searchable: No (unless explicitly queried)

  • Use Case: Long-term retention, compliance, not actively used

Example:

// Archive content
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Archived
});

// Query archived content
const archived = await graphlit.queryContents({
  filter: {
    states: [EntityState.Archived]
  }
});

State Transitions

Ingest → CREATED

Workflow Processing

ENABLED ←→ DISABLED (soft delete/restore)

ARCHIVED (long-term storage)

DELETED (permanent removal)

Valid Transitions:

  • CREATEDENABLED (automatic after processing)

  • ENABLEDDISABLED (soft delete)

  • DISABLEDENABLED (restore)

  • ENABLEDARCHIVED (archive)

  • ARCHIVEDENABLED (restore from archive)

  • Any → DELETED (permanent delete)

Check state

content = await graphlit.getContent('content-id') print(f"State: {content.content.state}")

Update state (snake_case)

await graphlit.updateContent( id='content-id', state=EntityState.DISABLED )

Query by state

results = await graphlit.queryContents( filter=ContentFilterInput( states=[EntityState.ENABLED] ) )


**C#**:
```csharp
using Graphlit;

var client = new Graphlit();

// Check state
var content = await graphlit.GetContent("content-id");
Console.WriteLine($"State: {content.Content.State}");

// Update state (PascalCase)
await graphlit.UpdateContent(new ContentUpdateInput
{
    Id = "content-id",
    State = EntityState.Disabled
});

// Query by state
var results = await graphlit.QueryContents(new ContentFilter
{
    States = new[] { EntityState.Enabled }
});

Developer Hints

Default Query Behavior

// This query...
const results = await graphlit.queryContents({
  search: "query"
});

// ...is equivalent to:
const results = await graphlit.queryContents({
  search: "query",
  filter: {
    states: [EntityState.Enabled]  // Default!
  }
});

// DISABLED, ARCHIVED content won't appear unless explicitly queried

Soft Delete vs Hard Delete

// Soft delete (reversible)
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

// Can restore later
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Enabled
});

// Hard delete (permanent)
await graphlit.deleteContent('content-id');
// Content is gone forever

Querying Multiple States

// Query both enabled and archived
const results = await graphlit.queryContents({
  filter: {
    states: [
      EntityState.Enabled,
      EntityState.Archived
    ]
  }
});

// Query everything except deleted
const everything = await graphlit.queryContents({
  filter: {
    states: [
      EntityState.Created,
      EntityState.Enabled,
      EntityState.Disabled,
      EntityState.Archived
    ]
  }
});

Variations

1. Soft Delete Content

// Hide from queries but preserve
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

2. Restore Soft-Deleted Content

// Make searchable again
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Enabled
});

3. Archive Old Content

// Archive content older than 1 year
const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);

const oldContent = await graphlit.queryContents({
  filter: {
    creationDateRange: {
      to: oneYearAgo.toISOString()
    },
    states: [EntityState.Enabled]
  }
});

// Archive each
for (const content of oldContent.contents.results) {
  await graphlit.updateContent({
    id: content.id,
    state: EntityState.Archived
  });
  console.log(`Archived: ${content.name}`);
}

4. Query Only Active Content

// Explicit enabled filter (best practice)
const active = await graphlit.queryContents({
  filter: {
    states: [EntityState.Enabled]
  }
});

5. View Disabled Content (Admin View)

// Show hidden content
const hidden = await graphlit.queryContents({
  filter: {
    states: [EntityState.Disabled]
  }
});

console.log(`${hidden.contents.results.length} hidden items`);

6. Bulk State Changes

// Disable multiple content items
const contentIds = ['id1', 'id2', 'id3'];

for (const id of contentIds) {
  await graphlit.updateContent({
    id,
    state: EntityState.Disabled
  });
}

// Or use batch operations
await Promise.all(
  contentIds.map(id =>
    graphlit.updateContent({
      id,
      state: EntityState.Disabled
    })
  )
);

7. Compliance Archival

// Archive but preserve for compliance
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Archived,
  metadata: {
    archivedDate: new Date().toISOString(),
    archivedReason: 'Compliance retention',
    retainUntil: '2030-01-01'
  }
});

Common Issues & Solutions

Issue: Content not appearing in queries Solution: Check if content is disabled or archived

// Check content state
const content = await graphlit.getContent('content-id');
console.log(`State: ${content.content.state}`);

if (content.content.state !== EntityState.Enabled) {
  console.log('Content is not enabled');
}

Issue: Accidentally deleted content Solution: Use soft delete (DISABLED) instead

//  DON'T - Permanent delete
await graphlit.deleteContent('content-id');

//  DO - Soft delete
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

Issue: Archived content still appearing Solution: Default queries only return ENABLED

// This won't return archived content
const results = await graphlit.queryContents({});

// Must explicitly exclude if needed
const onlyActive = await graphlit.queryContents({
  filter: {
    states: [EntityState.Enabled]
  }
});

Issue: Content stuck in CREATED state Solution: Check workflow processing status

const content = await graphlit.getContent('content-id');

if (content.content.state === EntityState.Created) {
  // Check if processing is done
  const status = await graphlit.isContentDone('content-id');
  
  if (!status.isContentDone.result) {
    console.log('Still processing...');
  } else if (content.content.error) {
    console.log(`Error: ${content.content.error}`);
  }
}

Production Example

async function manageContentLifecycle(contentId: string) {
  const content = await graphlit.getContent(contentId);
  
  console.log(`\n=== CONTENT LIFECYCLE ===`);
  console.log(`ID: ${content.content.id}`);
  console.log(`Name: ${content.content.name}`);
  console.log(`Current State: ${content.content.state}`);
  console.log(`Created: ${content.content.creationDate}`);
  
  // State-based actions
  switch (content.content.state) {
    case EntityState.Created:
      console.log('\n⏳ Processing...');
      const isDone = await graphlit.isContentDone(contentId);
      if (isDone.isContentDone.result) {
        console.log('✓ Processing complete');
      } else {
        console.log('Still processing workflow');
      }
      break;
      
    case EntityState.Enabled:
      console.log('\n✓ Active and searchable');
      
      // Check if old enough to archive
      const age = Date.now() - new Date(content.content.creationDate).getTime();
      const daysOld = age / (1000 * 60 * 60 * 24);
      
      if (daysOld > 365) {
        console.log(`📦 Content is ${Math.floor(daysOld)} days old - consider archiving`);
      }
      break;
      
    case EntityState.Disabled:
      console.log('\n🔒 Hidden (soft deleted)');
      console.log('Can be restored with state: ENABLED');
      break;
      
    case EntityState.Archived:
      console.log('\n📦 Archived');
      console.log('Preserved for compliance, not actively used');
      break;
  }
  
  return content.content.state;
}

// Usage
await manageContentLifecycle('content-id');

Last updated

Was this helpful?