UNPKG

@manic.code/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

317 lines (245 loc) 7.95 kB
# 🧠 @manic.code/schema [![npm version](https://img.shields.io/npm/v/@manic.codes/schema.svg)](https://www.npmjs.com/package/@manic.codes/schema) [![Build Status](https://img.shields.io/github/workflow/status/maniccodes/schema/test)](https://github.com/maniccodes/schema) [![Coverage](https://img.shields.io/codecov/c/github/maniccodes/schema)](https://codecov.io/gh/maniccodes/schema) [![License](https://img.shields.io/npm/l/@manic.codes/schema)](https://github.com/maniccodes/schema/blob/main/LICENSE) A powerful, flexible schema definition and validation system for TypeScript with seamless multi-database support. <p align="center"> <img src="banner.svg" alt="SAGE Schema" width="100%"> </p> ## 🔥 Overview MANIC Schema is a revolutionary TypeScript-first ORM/ODM that lets you define your data model once and seamlessly use it across multiple databases — MongoDB, Neo4j, PostgreSQL, and more. Built from the ground up with modern best practices: ## ✨ Key Features - **Define Once, Use Anywhere**: Define schema in TypeScript, deploy to any supported database - **Multi-Database Architecture**: Mix and match databases for optimal performance - **Cross-Database Relationships**: Maintain relationships between entities in different databases - **GraphQL Ready**: Auto-generate GraphQL schema from your entities - **Database-Specific Optimizations**: Leverage native database features when needed - **Type-Safe**: Full TypeScript support with proper types throughout ## 🏗️ Core Architecture - **Clean Design**: Explicit dependencies, no global state, no magic - **Adapter Pattern**: Extensible adapter system for database integrations - **Decorator-Based**: Intuitive TypeScript decorators for schema definition - **100% Test Coverage**: Comprehensive test suite with property-based testing ## 🗄️ Supported Databases | Database | Status | Features | | ---------- | -------------- | ----------------------------------------------- | | MongoDB | ✅ Stable | Collections, indexes, embedding | | Neo4j | ✅ Stable | Node labels, relationship types, Cypher queries | | PostgreSQL | ✅ Stable | Tables, columns, indexes, joins | | SQLite | 🚧 In Progress | Basic functionality | | Redis | 🚧 In Progress | Basic functionality | | DynamoDB | 🗓️ Planned | - | | Firestore | 🗓️ Planned | - | ## 🧩 Key Components ### Decorators Define your entities with TypeScript decorators: ```typescript @Entity() class Post { @Id() id: string; @Property({ required: true }) title: string; @Property() content: string; @Property() createdAt: Date; @ManyToOne({ target: () => User, inverse: "posts", name: "AUTHORED", }) author: User; @ManyToMany({ target: () => Tag, inverse: "posts", name: "HAS_TAG", }) tags: Tag[] = []; } ``` ### Database-Specific Decorators Optimize for specific databases when needed: ```typescript // MongoDB specific @Collection("blog_posts") @MongoIndex({ title: "text", content: "text" }) class Post { /* ... */ } // Neo4j specific @Labels(["Content", "BlogPost"]) @RelationshipType("TAGGED_WITH") class Post { /* ... */ } // PostgreSQL specific @Table("blog_posts") @Column({ name: "post_content", type: "text" }) @PgIndex({ name: "idx_post_title", columns: ["title"] }) class Post { /* ... */ } ``` ### SchemaBuilder & Registry Explicit dependencies, no global state: ```typescript // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Register your entities builder.registerEntities([User, Post, Comment, Tag]); ``` ### Multi-Database Adapters Use different databases for different entity types: ```typescript // Create adapters for different databases const mongoAdapter = new MongoDBAdapter( registry, "mongodb://localhost:27017/blog" ); const neo4jAdapter = new Neo4jAdapter(registry, "bolt://localhost:7687"); const pgAdapter = new PostgreSQLAdapter( registry, "postgres://localhost:5432/blog" ); // Create repositories with appropriate adapters const userRepo = new Repository<User>(User, mongoAdapter); const postRepo = new Repository<Post>(Post, neo4jAdapter); const tagRepo = new Repository<Tag>(Tag, pgAdapter); ``` ### Repositories Consistent interface across all databases: ```typescript // Find by ID const user = await userRepo.findById("user123"); // Query by criteria const posts = await postRepo.find({ completed: true }); // Create and save const tag = new Tag(); tag.name = "TypeScript"; await tagRepo.save(tag); // Delete await userRepo.delete("user123"); ``` ## 📊 GraphQL Integration Automatic GraphQL schema generation: ```typescript // Generate GraphQL schema from entities import { generateGraphQLSchema } from "@manic.codes/schema/graphql"; const typeDefs = generateGraphQLSchema(registry); const resolvers = generateResolvers(registry, { User: userRepo, Post: postRepo, Tag: tagRepo, }); // Create Apollo Server const server = new ApolloServer({ typeDefs, resolvers, }); ``` ## 🚀 Complete Multi-Database Example ```typescript import { Entity, Id, Property, ManyToOne, ManyToMany, MetadataRegistry, SchemaBuilder, MongoDBAdapter, Neo4jAdapter, PostgreSQLAdapter, Repository, } from "@manic.code/schema"; // Define entities @Entity() class User { @Id() id: string; @Property({ required: true }) name: string; @Property({ required: true, unique: true }) email: string; @OneToMany({ target: () => Post, inverse: "author" }) posts: Post[] = []; } @Entity() class Post { @Id() id: string; @Property({ required: true }) title: string; @Property() content: string; @Property() createdAt: Date = new Date(); @ManyToOne({ target: () => User, inverse: "posts" }) author: User; @ManyToMany({ target: () => Tag, inverse: "posts" }) tags: Tag[] = []; } @Entity() class Tag { @Id() id: string; @Property({ required: true, unique: true }) name: string; @ManyToMany({ target: () => Post, inverse: "tags" }) posts: Post[] = []; } // Set up the registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); builder.registerEntities([User, Post, Tag]); // Create database adapters const mongoAdapter = new MongoDBAdapter( registry, "mongodb://localhost:27017/blog" ); const neo4jAdapter = new Neo4jAdapter(registry, "bolt://localhost:7687"); const pgAdapter = new PostgreSQLAdapter( registry, "postgres://localhost:5432/blog" ); // Create repositories with different adapters const userRepo = new Repository<User>(User, mongoAdapter); const postRepo = new Repository<Post>(Post, neo4jAdapter); const tagRepo = new Repository<Tag>(Tag, pgAdapter); // Create entities with relationships across databases async function createBlogPost() { // Create user in MongoDB const user = new User(); user.id = "user1"; user.name = "John Doe"; user.email = "john@example.com"; await userRepo.save(user); // Create tags in PostgreSQL const tag1 = new Tag(); tag1.id = "tag1"; tag1.name = "TypeScript"; await tagRepo.save(tag1); const tag2 = new Tag(); tag2.id = "tag2"; tag2.name = "ORM"; await tagRepo.save(tag2); // Create post in Neo4j with relationships to MongoDB and PostgreSQL const post = new Post(); post.id = "post1"; post.title = "Multi-Database ORM with TypeScript"; post.content = "This is amazing..."; post.author = user; post.tags.push(tag1, tag2); await postRepo.save(post); return post; } ``` ## 🔧 Installation ```bash npm install @manic.code/schema ``` ## 📄 License MIT © [Zach Winter](https://github.com/zachwinter)