Skip to content

jakesower/spectragraph

Repository files navigation

SpectraGraph

A unified, expressive query language for multiple data sources. Query databases, APIs, and in-memory data with the same syntax, automatic relationship linking, and powerful expressions.

Quick Example

// Same query works across different data sources
const result = await store.query({
  type: "patrons",
  select: [
    "name",
    "email",
    {
      loans: [
        "dueDate",
        "renewalCount",
        {
          book: [
            "title",
            "isbn",
            {
              author: ["name"],
            },
          ],
        },
      ],
    },
  ],
  where: { membershipActive: true },
  limit: 10,
});

// Works with: PostgreSQL, SQLite, in-memory data, REST APIs
// Handles relationships automatically with request optimization
// Full toolkit for creating custom stores to meet exact needs

Start Building Today, Deploy Anywhere Tomorrow

SpectraGraph lets you build with real data relationships from day one then deploy to multiple infrastructures (including your own custom stores) without changing your queries.

Development to Production in 3 Steps

1. Start app development immediately with fixtures

import { createMemoryStore } from "@spectragraph/memory-store";

// Begin building features with realistic data
const store = createMemoryStore(schema, {
  initialData: {
    patrons: [{ id: "1", name: "Kenji Nakamura", libraryCard: "LIB001" }],
    books: [
      {
        id: "b1",
        title: "The Golden Compass",
        isbn: "0-590-54178-1",
      },
    ],
  },
});

2. Build up your store in parallel

import { createMultiApiStore } from "@spectragraph/postgres-store";

// Zero code changes in your application as you build out your config
const store = createMultiApiStore(schema, config);
// Your team configures endpoints, debugs transport concerns, works out data
// formats, etc.

3. Combine the two

import { createMultiApiStore } from "@spectragraph/multi-api-store";

// Switch the store over seamlessly, no application changes required
const store = createMultiApiStore(schema, {
  resources: {
    patrons: {
      handlers: { query: { fetch: () => membershipAPI.getPatrons() } },
    },
    books: {
      handlers: { query: { fetch: () => catalogAPI.getBooks() } },
    },
  },
});

Your queries and application layer stay the same during the entire process, no matter the data layer.

Why This Matters

  • Unblock parallel development - App and transport/database teams work independently
  • Validate architecture early - Prove data model before infrastructure complexity
  • Deploy flexibly - Move your application over when ready with zero query changes
  • Eliminate data layer rewrites - Investment compounds across environments

Performance by Design

Each store optimizes queries using its native capabilities:

  • PostgreSQL store generates efficient SQL with proper indexes
  • Multi-API store coordinates requests and caches relationships
  • Memory store uses JavaScript's native performance for prototyping

Installation

npm install @spectragraph/memory-store
# or choose your store: postgres-store, sqlite-store, etc.

Basic Usage

import { createMemoryStore } from "@spectragraph/memory-store";

// Define your data schema
const schema = {
  resources: {
    patrons: {
      attributes: {
        name: { type: "string" },
        email: { type: "string" },
        libraryCard: { type: "string" },
        membershipActive: { type: "boolean" },
        readingLevel: {
          type: "string",
          enum: ["beginner", "intermediate", "advanced"],
        },
      },
      relationships: {
        loans: {
          type: "loans",
          cardinality: "many",
          inverse: "patron",
        },
      },
    },
    books: {
      attributes: {
        title: { type: "string" },
        isbn: { type: "string" },
        publishedYear: { type: "number" },
      },
      relationships: {
        author: {
          type: "authors",
          cardinality: "one",
          inverse: "books",
        },
        loans: {
          type: "loans",
          cardinality: "many",
          inverse: "book",
        },
      },
    },
    authors: {
      attributes: {
        name: { type: "string" },
        biography: { type: "string" },
      },
      relationships: {
        books: {
          type: "books",
          cardinality: "many",
          inverse: "author",
        },
      },
    },
    loans: {
      attributes: {
        dueDate: { type: "string" },
        renewalCount: { type: "number" },
        returned: { type: "boolean" },
      },
      relationships: {
        patron: {
          type: "patrons",
          cardinality: "one",
          inverse: "loans",
        },
        book: {
          type: "books",
          cardinality: "one",
          inverse: "loans",
        },
      },
    },
  },
};

// Create store with initial data
const store = createMemoryStore(schema, {
  initialData: {
    patrons: {
      1: {
        id: "1",
        type: "patrons",
        attributes: {
          name: "Amara Okafor",
          email: "amara.okafor@email.com",
          libraryCard: "LIB001",
        },
        relationships: { loans: [{ type: "loans", id: "1" }] },
      },
    },
    authors: {
      1: {
        id: "1",
        type: "authors",
        attributes: {
          name: "Philip Pullman",
          biography: "Award-winning novelist",
        },
        relationships: { books: [{ type: "books", id: "1" }] },
      },
    },
    books: {
      1: {
        id: "1",
        type: "books",
        attributes: {
          title: "The Golden Compass",
          isbn: "978-1234567890",
          publishedYear: 1995,
        },
        relationships: {
          author: { type: "authors", id: "1" },
          loans: [{ type: "loans", id: "1" }],
        },
      },
    },
    loans: {
      1: {
        id: "1",
        type: "loans",
        attributes: {
          dueDate: "2024-02-15",
          renewalCount: 1,
          returned: false,
        },
        relationships: {
          patron: { type: "patrons", id: "1" },
          book: { type: "books", id: "1" },
        },
      },
    },
  },
});

// Query with expressions
const analytics = await store.query({
  type: "patrons",
  select: {
    name: "name",
    loanCount: { $count: "loans" },
    avgRenewals: { $avg: "loans.$.renewalCount" },
  },
});

console.log(analytics);
// [{ name: 'Amara Okafor', loanCount: 1, avgRenewals: 1 }]

Learning Path (5 minutes)

If you know JSON, you know the basics:

// This is just asking for patron data
{ type: "patrons", select: ["name", "email"] }

If you know GraphQL, relationships work the same way:

// Nested data, just like GraphQL
{ type: "patrons", select: ["name", { loans: [{ book: ["title"] }] }] }

If you know SQL, filtering is familiar:

// WHERE clauses, just in JSON
{ type: "patrons", where: { membershipActive: true }, limit: 10 }

If you know JavaScript, expressions are just functions:

// Count and average, like Array methods
{ type: "patrons", select: { loanCount: { $count: "loans" } } }

Use Cases

Backend Development

  • Database Abstraction - Switch between PostgreSQL, SQLite, or in-memory stores without changing queries
  • API Integration - Combine data from multiple services with unified query syntax
  • Microservice Data - Query across service boundaries with automatic relationship resolution

Frontend Development

  • Multi-API Coordination - Fetch related data from multiple endpoints in one query
  • Request Optimization - Minimize API calls with intelligent batching and caching
  • Data Transformation - Apply computations and aggregations client-side

Data Analysis

  • Expression-Powered Queries - Built-in aggregations, computations, and custom expressions
  • Cross-Store Analytics - Same analytical queries work on any data source
  • Dynamic Querying - Build queries programmatically for dashboards and reports

Custom Store Development

  • Store Builder Toolkit - Create stores for any data source using @spectragraph/core
  • Unified Interface - Implement once, compatible with entire SpectraGraph ecosystem
  • Query Processing - Built-in validation, normalization, and relationship handling

Available Stores

Store Use Case Status
@spectragraph/memory-store In-memory data, testing, prototyping Stable
@spectragraph/multi-api-store Multiple REST APIs, frontend BFF Stable
@spectragraph/postgres-store PostgreSQL databases Stable
@spectragraph/sqlite-store SQLite databases Stable

Advanced Query Examples

Filtering and Sorting

{
  type: "books",
  select: ["title", "publishedYear"],
  where: {
    active: true, // implicit $eq
    readingLevel: ["intermediate", "advanced"], // implicit $in
    publishedYear: { $gte: 2020 }, // expression
  },
  order: [{ publishedYear: "desc" }],
  limit: 5
}

Cross-Store Data Composition

// Same query works across different store types
{
  type: "patrons",
  select: ["name", {
    loans: ["dueDate", "renewalCount", {
      book: {
        select: ["title", "isbn"],
        limit: 3
      }
    }]
  }],
  where: { membershipActive: true }
}

Documentation

Why SpectraGraph?

Instead of store-specific query languages:

-- PostgreSQL
SELECT p.name, b.title, a.name as author_name
FROM patrons p
LEFT JOIN loans l ON p.id = l.patron_id
LEFT JOIN books b ON l.book_id = b.id
LEFT JOIN authors a ON b.author_id = a.id
WHERE p.membership_active = true;

Write once, run anywhere:

// Same query works on PostgreSQL, SQLite, in-memory, or API stores
const result = await store.query({
  type: "patrons",
  select: [
    "name",
    {
      loans: [
        "dueDate",
        {
          book: [
            "title",
            {
              author: ["name"],
            },
          ],
        },
      ],
    },
  ],
  where: { membershipActive: true },
});

License

AGPL-3.0 © Jake Sower

Using SpectraGraph in Commercial/Proprietary Software

SpectraGraph is designed for maximum extensibility without modification. The AGPL-3.0 copyleft is triggered only if you modify and distribute the core/store packages themselves.

These DO NOT trigger copyleft:

  • Writing custom stores that import @spectragraph/core
  • Using SpectraGraph as a dependency in proprietary applications
  • Wrapping SpectraGraph in internal company APIs
  • Building commercial SaaS products on top of SpectraGraph

These DO trigger copyleft:

  • Forking, modifying, and distributing @spectragraph/core internals
  • Patching store implementations and distributing them
  • Bundling modified SpectraGraph code without source disclosure

For organizations requiring alternative licensing (indemnification, warranty, legal CYA), commercial licenses are available. Contact: jake@jakesower.com

See LICENSING.md for detailed scenarios and FAQs.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors