GraphQL API Guide¶
NornicDB provides a full-featured GraphQL API powered by gqlgen, offering a flexible alternative to the Cypher query interface. The GraphQL API supports queries, mutations, and real-time subscriptions for live updates.
Quick Start¶
Endpoints¶
- GraphQL Endpoint:
POST /graphql - GraphQL Playground:
GET /graphql/playground
Using the Playground¶
Navigate to http://localhost:7474/graphql/playground to access the interactive GraphQL Playground with full schema introspection and auto-complete.
Authentication: Add headers { "Authorization": "Bearer <your-token>" } in the playground settings to use authentication.
Core Features¶
- ✅ Full CRUD operations on nodes and relationships
- ✅ Advanced search with vector similarity and BM25
- ✅ Cypher query pass-through for complex graph operations
- ✅ Real-time subscriptions for live data updates
- ✅ Schema introspection for auto-complete
- ✅ Graph traversal queries with relationship filtering
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 propertiesDateTime- RFC3339 timestampsFloatArray- Float32 arrays for embeddings
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
}
}
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
}
totalCount
executionTimeMs
}
}
# Find similar nodes
query FindSimilar {
similar(nodeId: "node-id", limit: 5, threshold: 0.7) {
node {
id
labels
properties
}
similarity
}
}
Graph Traversal¶
# Get node with relationships
query NodeWithRelationships {
node(id: "person-id") {
id
labels
outgoing(limit: 10) {
type
endNode {
id
labels
properties
}
}
}
}
# Find shortest path
query ShortestPath {
shortestPath(
startNodeId: "alice-id"
endNodeId: "charlie-id"
maxDepth: 5
relationshipTypes: ["KNOWS"]
) {
id
labels
properties
}
}
Mutations¶
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
}
}
Updating Data¶
# 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")
}
Real-Time Subscriptions¶
GraphQL subscriptions enable real-time updates when nodes and relationships are created, updated, or deleted. This is perfect for building reactive applications, dashboards, and live collaboration features.
Available Subscriptions¶
nodeCreated(labels: [String!])- Subscribe to new nodes (optionally filtered by labels)nodeUpdated(id: ID, labels: [String!])- Subscribe to node updates (optionally filtered by ID or labels)nodeDeleted(labels: [String!])- Subscribe to node deletions (optionally filtered by labels)relationshipCreated(types: [String!])- Subscribe to new relationships (optionally filtered by types)relationshipUpdated(id: ID, types: [String!])- Subscribe to relationship updatesrelationshipDeleted(types: [String!])- Subscribe to relationship deletions
Subscription Examples¶
Subscribe to All New Nodes¶
Subscribe to Specific Node Labels¶
Subscribe to Node Updates¶
# All node updates
subscription AllNodeUpdates {
nodeUpdated {
id
labels
properties
updatedAt
}
}
# Updates for a specific node
subscription NodeUpdate($nodeId: ID!) {
nodeUpdated(id: $nodeId) {
id
properties
updatedAt
}
}
# Updates for nodes with specific labels
subscription PersonUpdates {
nodeUpdated(labels: ["Person"]) {
id
properties
updatedAt
}
}
Subscribe to Node Deletions¶
Subscribe to Relationship Events¶
# All new relationships
subscription NewRelationships {
relationshipCreated {
id
type
startNode {
id
labels
}
endNode {
id
labels
}
properties
}
}
# Specific relationship types
subscription NewKnowsRelationships {
relationshipCreated(types: ["KNOWS"]) {
id
type
startNode {
id
}
endNode {
id
}
}
}
# Relationship updates
subscription RelationshipUpdates {
relationshipUpdated {
id
type
properties
updatedAt
}
}
# Relationship deletions
subscription DeletedRelationships {
relationshipDeleted(types: ["KNOWS"])
}
Using Subscriptions in JavaScript/TypeScript¶
import { createClient } from 'graphql-ws';
// Create WebSocket client
const client = createClient({
url: 'ws://localhost:7474/graphql',
connectionParams: {
Authorization: 'Bearer <your-token>',
},
});
// Subscribe to new Person nodes
const unsubscribe = client.subscribe(
{
query: `
subscription NewPersons {
nodeCreated(labels: ["Person"]) {
id
labels
properties
}
}
`,
},
{
next: (data) => {
console.log('New person created:', data.data.nodeCreated);
},
error: (err) => {
console.error('Subscription error:', err);
},
complete: () => {
console.log('Subscription completed');
},
}
);
// Later, unsubscribe
unsubscribe();
Using Subscriptions with Apollo Client¶
import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
const httpLink = new HttpLink({
uri: 'http://localhost:7474/graphql',
headers: {
Authorization: 'Bearer <your-token>',
},
});
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:7474/graphql',
connectionParams: {
Authorization: 'Bearer <your-token>',
},
})
);
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
// Use subscription in React component
import { useSubscription } from '@apollo/client';
const NEW_PERSONS_SUBSCRIPTION = gql`
subscription NewPersons {
nodeCreated(labels: ["Person"]) {
id
labels
properties
}
}
`;
function PersonList() {
const { data, loading } = useSubscription(NEW_PERSONS_SUBSCRIPTION);
if (loading) return <p>Loading...</p>;
return (
<div>
<h2>New Person: {data?.nodeCreated?.properties?.name}</h2>
</div>
);
}
Using Subscriptions with cURL (Server-Sent Events)¶
GraphQL subscriptions over HTTP use Server-Sent Events (SSE):
curl -N -H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"query": "subscription { nodeCreated(labels: [\"Person\"]) { id labels properties } }"
}' \
http://localhost:7474/graphql
Filtering Subscriptions¶
Subscriptions support flexible filtering:
- Label filtering: Only receive events for nodes with specific labels
- Type filtering: Only receive events for relationships of specific types
- ID filtering: Only receive events for a specific node or relationship
Examples:
# Only Person nodes
subscription Persons {
nodeCreated(labels: ["Person"]) { id }
}
# Multiple labels (OR logic - matches any)
subscription PeopleOrCompanies {
nodeCreated(labels: ["Person", "Company"]) { id }
}
# Specific relationship types
subscription KnowsOrLikes {
relationshipCreated(types: ["KNOWS", "LIKES"]) { id type }
}
# Specific node updates
subscription AliceUpdates {
nodeUpdated(id: "alice-id") { id properties }
}
Subscription Best Practices¶
- Use filters to reduce unnecessary events and bandwidth
- Handle errors gracefully - subscriptions may disconnect
- Reconnect logic - implement automatic reconnection for production
- Rate limiting - be aware of event volume for high-traffic applications
- Cleanup - properly unsubscribe when components unmount
Performance Considerations¶
- Subscriptions use buffered channels (size: 10) to prevent blocking
- Events are dropped if the channel is full (prevents memory issues)
- Filtering happens server-side for efficiency
- Multiple subscriptions can run concurrently
Cypher Pass-through¶
For complex queries, you can execute Cypher directly:
# 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
}
}
Integration Examples¶
HTTP API¶
# Query
curl -X POST http://localhost:7474/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"query": "{ stats { nodeCount } }"}'
# Mutation
curl -X POST http://localhost:7474/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"query": "mutation { createNode(input: { labels: [\"Test\"], properties: {} }) { id } }"
}'
JavaScript/TypeScript¶
const response = await fetch("/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <token>",
},
body: JSON.stringify({
query: `
query GetNode($id: ID!) {
node(id: $id) {
id
labels
properties
}
}
`,
variables: { id: "node-123" },
}),
});
const { data, errors } = await response.json();
Python¶
import requests
response = requests.post(
"http://localhost:7474/graphql",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer <token>",
},
json={
"query": """
query GetNode($id: ID!) {
node(id: $id) {
id
labels
properties
}
}
""",
"variables": {"id": "node-123"},
},
)
data = response.json()
Performance Tips¶
- Batching: Use bulk operations for creating/deleting multiple nodes
- Pagination: Always use
limitandoffsetfor large result sets - Field Selection: Only request fields you need to minimize data transfer
- Cypher Fallback: For complex queries, use the Cypher pass-through for optimal performance
- Subscription Filters: Use filters to reduce event volume
Related Documentation¶
- API Reference - Complete function documentation
- Cypher Queries - Cypher language guide
- Vector Search - Semantic search guide
- Complete Examples - Full application examples
Ready to start? → GraphQL Playground