graphql

package
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 7 Imported by: 0

README

NornicDB GraphQL API

NornicDB provides a full-featured GraphQL API powered by gqlgen, offering an alternative to the Cypher query interface. The GraphQL API supports:

  • Full CRUD operations on nodes and relationships
  • Advanced search with vector similarity and BM25
  • Cypher query pass-through for complex graph operations
  • Schema introspection
  • Graph traversal queries

Quick Start

Enable GraphQL

GraphQL is enabled by default. The endpoints are:

  • GraphQL Endpoint: POST /graphql
  • GraphQL Playground: GET /playground
Using the Playground

Navigate to http://localhost:7474/playground to access the interactive GraphQL Playground with full schema introspection and auto-complete.

  • NOTE: Must add headers { "Authorization": "Bearer asdfsgagsaga....."} to your http headers in the playground to use authentication!

Schema Overview

Core Types
type Node {
  id: ID!
  internalId: String!
  labels: [String!]!
  properties: JSON!
  createdAt: DateTime
  updatedAt: DateTime
  hasEmbedding: Boolean!
  embeddingDimensions: Int!

  # Traversal
  relationships(
    types: [String!]
    direction: RelationshipDirection
    limit: Int
  ): [Relationship!]!
  outgoing(types: [String!], limit: Int): [Relationship!]!
  incoming(types: [String!], limit: Int): [Relationship!]!
  neighbors(
    direction: RelationshipDirection
    relationshipTypes: [String!]
    labels: [String!]
    limit: Int
  ): [Node!]!
  similar(limit: Int, threshold: Float): [SimilarNode!]!
}

type Relationship {
  id: ID!
  type: String!
  startNode: Node!
  endNode: Node!
  properties: JSON!
  createdAt: DateTime
  confidence: Float
  autoGenerated: Boolean!
}
Custom Scalars
  • JSON - Arbitrary JSON objects for properties
  • DateTime - RFC3339 timestamps
  • FloatArray - Float32 arrays for embeddings

Example Queries

Basic Queries
# Get database statistics
query Stats {
  stats {
    nodeCount
    relationshipCount
    embeddedNodeCount
    uptimeSeconds
    labels {
      label
      count
    }
  }
}

# Get a node by ID
query GetNode($id: ID!) {
  node(id: $id) {
    id
    labels
    properties
    createdAt
  }
}

# List nodes by label
query ListPersons {
  allNodes(labels: ["Person"], limit: 10) {
    id
    labels
    properties
  }
}

# Get all labels
query Labels {
  labels
}
Creating Data
# Create a node
mutation CreatePerson {
  createNode(
    input: {
      labels: ["Person"]
      properties: { name: "Alice Smith", age: 30, email: "alice@example.com" }
    }
  ) {
    id
    labels
    properties
  }
}

# Create a relationship
mutation CreateKnows {
  createRelationship(
    input: {
      startNodeId: "alice-id"
      endNodeId: "bob-id"
      type: "KNOWS"
      properties: { since: "2024-01-01" }
    }
  ) {
    id
    type
    startNode {
      id
    }
    endNode {
      id
    }
  }
}

# Bulk create nodes
mutation BulkCreate {
  bulkCreateNodes(
    input: {
      nodes: [
        { labels: ["Person"], properties: { name: "Alice" } }
        { labels: ["Person"], properties: { name: "Bob" } }
      ]
    }
  ) {
    created
    skipped
    errors
  }
}
Search Queries
# Hybrid search (vector + BM25)
query Search {
  search(
    query: "software engineer"
    options: { limit: 10, labels: ["Person"], method: HYBRID }
  ) {
    results {
      node {
        id
        labels
        properties
      }
      score
      rrfScore
      vectorRank
      bm25Rank
      foundBy
    }
    totalCount
    executionTimeMs
  }
}

# Find similar nodes
query FindSimilar {
  similar(nodeId: "node-id", limit: 5, threshold: 0.7) {
    node {
      id
      labels
      properties
    }
    similarity
  }
}
Cypher Pass-through
# Execute Cypher queries
query CypherQuery {
  cypher(
    input: {
      statement: "MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name LIMIT 10"
    }
  ) {
    columns
    rows
    rowCount
  }
}

# Cypher with parameters
query CypherWithParams {
  cypher(
    input: {
      statement: "MATCH (p:Person) WHERE p.age > $minAge RETURN p"
      parameters: { minAge: 25 }
    }
  ) {
    columns
    rows
  }
}

# Cypher mutations
mutation CypherCreate {
  executeCypher(
    input: {
      statement: "CREATE (n:Person {name: $name}) RETURN n"
      parameters: { name: "Charlie" }
    }
  ) {
    rowCount
  }
}
Graph Traversal
# Get node with relationships
query NodeWithRelationships {
  node(id: "person-id") {
    id
    labels
    outgoing(limit: 10) {
      type
      endNode {
        id
        labels
        properties
      }
    }
    incoming(limit: 10) {
      type
      startNode {
        id
        labels
      }
    }
  }
}

# Find shortest path
query ShortestPath {
  shortestPath(
    startNodeId: "alice-id"
    endNodeId: "charlie-id"
    maxDepth: 5
    relationshipTypes: ["KNOWS"]
  ) {
    id
    labels
    properties
  }
}

# Get neighborhood subgraph
query Neighborhood {
  neighborhood(
    nodeId: "center-node-id"
    depth: 2
    labels: ["Person", "Company"]
  ) {
    nodes {
      id
      labels
    }
    relationships {
      id
      type
      startNode {
        id
      }
      endNode {
        id
      }
    }
  }
}
Update Operations
# Update node properties
mutation UpdatePerson {
  updateNode(
    input: { id: "node-id", properties: { age: 31, title: "Senior Engineer" } }
  ) {
    id
    properties
  }
}

# Merge (upsert) node
mutation MergePerson {
  mergeNode(
    labels: ["Person"]
    matchProperties: { email: "alice@example.com" }
    setProperties: { lastLogin: "2024-12-16" }
  ) {
    id
    properties
  }
}

# Delete node
mutation DeleteNode {
  deleteNode(id: "node-id")
}
Admin Operations
# Trigger embedding generation
mutation TriggerEmbedding {
  triggerEmbedding(regenerate: false) {
    pending
    embedded
    total
    workerRunning
  }
}

# Rebuild search index
mutation RebuildIndex {
  rebuildSearchIndex
}

# Get schema information
query Schema {
  schema {
    nodeLabels
    relationshipTypes
    nodePropertyKeys
  }
}

# Clear all data (dangerous!)
mutation ClearAll {
  clearAll(confirmPhrase: "DELETE ALL DATA")
}

Integration

Go Client
import "github.com/orneryd/nornicdb/pkg/graphql"

// Create handler
handler := graphql.NewHandler(db)

// Register with HTTP server
http.Handle("/graphql", handler)
http.Handle("/playground", handler.Playground())
HTTP API
# Query
curl -X POST http://localhost:7474/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ stats { nodeCount } }"}'

# Mutation
curl -X POST http://localhost:7474/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { createNode(input: { labels: [\"Test\"], properties: {} }) { id } }"}'
JavaScript/TypeScript
const response = await fetch("/graphql", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    query: `
      query GetNode($id: ID!) {
        node(id: $id) {
          id
          labels
          properties
        }
      }
    `,
    variables: { id: "node-123" },
  }),
});

const { data, errors } = await response.json();

Architecture

pkg/graphql/
├── schema/
│   └── schema.graphql    # GraphQL schema definition
├── generated/
│   └── generated.go      # gqlgen generated code
├── models/
│   └── models.go         # Custom model definitions
├── resolvers/
│   ├── resolver.go       # Root resolver
│   ├── schema.resolvers.go # Generated resolver stubs
│   ├── query_impl.go     # Query implementations
│   ├── mutation_impl.go  # Mutation implementations
│   ├── node_impl.go      # Node field resolvers
│   ├── relationship_impl.go # Relationship field resolvers
│   └── helpers.go        # Conversion helpers
├── handler.go            # HTTP handler
└── gqlgen.yml            # gqlgen configuration

Regenerating Schema

After modifying schema/schema.graphql:

cd pkg/graphql
go generate ./...

This regenerates generated/generated.go and updates resolver stubs.

Testing

# Run all GraphQL tests
go test ./pkg/graphql/... -v

# Run only resolver tests
go test ./pkg/graphql/resolvers/... -v

# Run with coverage
go test ./pkg/graphql/... -coverprofile=coverage.out
go tool cover -html=coverage.out

Performance Considerations

  1. Batching: Use bulk operations for creating/deleting multiple nodes
  2. Pagination: Always use limit and offset for large result sets
  3. Field Selection: Only request fields you need to minimize data transfer
  4. Cypher Fallback: For complex queries, use the Cypher pass-through for optimal performance

Subscriptions

Real-time subscriptions are now fully implemented!

Subscribe to live updates when nodes and relationships are created, updated, or deleted:

type Subscription {
  nodeCreated(labels: [String!]): Node!
  nodeUpdated(id: ID, labels: [String!]): Node!
  nodeDeleted(labels: [String!]): ID!
  relationshipCreated(types: [String!]): Relationship!
  relationshipUpdated(id: ID, types: [String!]): Relationship!
  relationshipDeleted(types: [String!]): ID!
}

See the GraphQL User Guide for complete subscription documentation and examples.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler represents the GraphQL HTTP handler.

func NewHandler

func NewHandler(db *nornicdb.DB, dbManager *multidb.DatabaseManager) *Handler

NewHandler creates a new GraphQL handler that uses the default database's namespaced storage engine. This ensures all nodes created via GraphQL are properly namespaced and visible in the default database.

The handler uses the default database from the database manager, ensuring proper multi-database isolation and preventing orphaned nodes.

Parameters:

  • db: Base database instance (used for admin operations like embeddings)
  • dbManager: Database manager that provides namespaced storage engines

All GraphQL operations (create, update, delete, query) use namespaced storage to ensure proper database isolation.

func (*Handler) Playground

func (h *Handler) Playground() http.Handler

Playground returns the GraphQL playground handler with full introspection.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles GraphQL requests using gqlgen.

Directories

Path Synopsis
Package models provides GraphQL model types for NornicDB.
Package models provides GraphQL model types for NornicDB.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL