@nuvix/db
v0.1.30
Published
Modular and performant open source database library for Nuvix, with internal complexity abstracted from developers.
Maintainers
Readme
@nuvix/db
A modular and performant database library for Nuvix, with internal complexity abstracted from developers. Built with TypeScript, this library provides a high-level interface for PostgreSQL databases with support for relationships, validation, caching, and more.
Features
🚀 High Performance - Optimized queries and connection pooling
🔒 Type Safe - Full TypeScript support with generated types
📊 Relationships - OneToOne, OneToMany, ManyToOne, and ManyToMany relationships
🛡️ Security - Built-in authorization and document-level permissions
✅ Validation - Comprehensive data validation and structure checking
🎯 Query Builder - Fluent query interface with filters, sorting, and pagination
📇 Indexing - Support for key, unique, and fulltext indexes
🔄 Transactions - ACID transaction support
💾 Caching - Integrated caching layer for improved performance
🏢 Multi-tenancy - Built-in support for shared tables and tenant isolation
📝 Migrations - Schema migration support
Installation
npm install @nuvix/db
# or
yarn add @nuvix/db
# or
bun add @nuvix/dbQuick Start
import {
Database,
Adapter,
Doc,
AttributeEnum,
Permission,
Role,
} from "@nuvix/db";
import { Memory } from "@nuvix/cache";
// Create database adapter
const adapter = new Adapter({
connectionString: "postgres://user:pass@localhost:5432/mydb",
});
// Initialize database
const db = new Database(adapter, new Memory());
// Create database schema
await db.create();
// Create a collection
await db.createCollection({
id: "users",
attributes: [
new Doc({
$id: "name",
key: "name",
type: AttributeEnum.String,
size: 100,
required: true,
}),
new Doc({
$id: "email",
key: "email",
type: AttributeEnum.String,
size: 255,
required: true,
}),
new Doc({
$id: "age",
key: "age",
type: AttributeEnum.Integer,
size: 4,
}),
],
permissions: [Permission.create(Role.any())],
});
// Create a document
const user = await db.createDocument(
"users",
new Doc({
name: "John Doe",
email: "[email protected]",
age: 30,
$permissions: [Permission.read(Role.any()).toString()],
}),
);
// Read a document
const retrieved = await db.getDocument("users", user.getId());
// Query documents
const users = await db.find("users", (qb) => qb.equal("age", 30).limit(10));
// Update a document
await db.updateDocument(
"users",
user.getId(),
new Doc({
age: 31,
}),
);
// Delete a document
await db.deleteDocument("users", user.getId());Core Concepts
Collections and Attributes
Collections are like tables in traditional databases. Each collection has attributes that define the structure of documents:
// Define collection attributes
const attributes = [
new Doc({
$id: "title",
key: "title",
type: AttributeEnum.String,
size: 200,
required: true,
}),
new Doc({
$id: "content",
key: "content",
type: AttributeEnum.String,
size: 5000,
}),
new Doc({
$id: "published",
key: "published",
type: AttributeEnum.Boolean,
default: false,
}),
new Doc({
$id: "tags",
key: "tags",
type: AttributeEnum.String,
size: 50,
array: true, // Array of strings
}),
];Supported Attribute Types
AttributeEnum.String- Text with specified size limitAttributeEnum.Integer- Integer numbers with optional sizeAttributeEnum.Float- Floating point numbersAttributeEnum.Boolean- True/false valuesAttributeEnum.Json- JSON objectsAttributeEnum.Uuid- UUID stringsAttributeEnum.Timestamptz- Timestamps with timezoneAttributeEnum.Relationship- References to other documents
Relationships
Create relationships between collections:
// One-to-Many: User has many Posts
await db.createRelationship({
collectionId: "users",
relatedCollectionId: "posts",
type: RelationEnum.OneToMany,
id: "posts",
twoWay: true,
twoWayKey: "author",
});
// Many-to-Many: Posts have many Tags
await db.createRelationship({
collectionId: "posts",
relatedCollectionId: "tags",
type: RelationEnum.ManyToMany,
id: "tags",
twoWay: true,
twoWayKey: "posts",
});Querying
Use the fluent query builder for complex queries:
// Simple queries
const users = await db.find("users", (qb) =>
qb
.equal("status", "active")
.greaterThan("age", 18)
.limit(50)
.offset(0)
.orderBy("name", "ASC"),
);
// Complex queries with multiple conditions
const posts = await db.find("posts", (qb) =>
qb
.equal("published", true)
.search("title", "typescript")
.between("created_at", "2024-01-01", "2024-12-31")
.contains("tags", ["tutorial", "guide"])
.populate(["author", "comments"])
.select(["title", "content", "author.name"]),
);
// Using Query objects directly
const results = await db.find("users", [
Query.equal("status", ["active"]),
Query.greaterThan("age", 18),
Query.limit(25),
Query.orderBy("name"),
]);Indexing
Create indexes for better query performance:
// Key index for faster lookups
await db.createIndex("users", "idx_email", IndexEnum.Key, ["email"]);
// Unique index to enforce uniqueness
await db.createIndex("users", "idx_username", IndexEnum.Unique, ["username"]);
// Fulltext index for search
await db.createIndex("posts", "idx_content", IndexEnum.FullText, [
"title",
"content",
]);
// Composite index
await db.createIndex("posts", "idx_author_date", IndexEnum.Key, [
"author",
"created_at",
]);Permissions and Security
Control access with role-based permissions:
// Collection-level permissions
await db.createCollection({
id: 'posts',
attributes: [...],
permissions: [
Permission.create(Role.user()),
Permission.read(Role.any()),
Permission.update(Role.user()),
Permission.delete(Role.user())
],
documentSecurity: true // Enable document-level permissions
});
// Document-level permissions
await db.createDocument('posts', new Doc({
title: 'My Post',
content: 'Content here...',
$permissions: [
Permission.read(Role.any()).toString(),
Permission.update(Role.user('user123')).toString(),
Permission.delete(Role.user('user123')).toString()
]
}));Advanced Usage
Transactions
Ensure data consistency with transactions:
await db.withTransaction(async () => {
const user = await db.createDocument("users", userData);
const profile = await db.createDocument("profiles", {
userId: user.getId(),
...profileData,
});
// Both operations succeed or both fail
});Multi-tenancy
Support multiple tenants in shared infrastructure:
// Configure adapter for shared tables
adapter.setMeta({
sharedTables: true,
tenantId: 123,
namespace: "tenant_app",
});
// All operations will be scoped to the tenant
const users = await db.find("users"); // Only returns tenant 123's usersCaching
Leverage built-in caching for better performance:
// Cache is automatically managed
const user = await db.getDocument("users", "user123"); // Fetches from DB
const userAgain = await db.getDocument("users", "user123"); // Returns from cache
// Manual cache control
await db.purgeCachedDocument("users", "user123");
await db.purgeCachedCollection("users");Event Handling
Listen to database events:
db.on(EventsEnum.DocumentCreate, (document) => {
console.log("Document created:", document.getId());
});
db.on(EventsEnum.CollectionCreate, (collection) => {
console.log("Collection created:", collection.getId());
});Project Structure
src/
├── adapters/ # Database adapters (PostgreSQL)
│ ├── adapter.ts # Main adapter implementation
│ ├── base.ts # Base adapter class
│ ├── postgres.ts # PostgreSQL client wrapper
│ └── types.ts # Adapter types
├── core/ # Core database functionality
│ ├── database.ts # Main Database class
│ ├── doc.ts # Document class
│ ├── query.ts # Query building
│ ├── cache.ts # Caching layer
│ └── enums.ts # Type enums
├── errors/ # Custom error classes
├── utils/ # Utility functions
│ ├── authorization.ts # Permission handling
│ ├── id.ts # ID generation
│ ├── permission.ts # Permission utilities
│ └── query-builder.ts # Query builder
└── validators/ # Data validation
├── schema.ts # Schema validation
├── queries/ # Query validation
└── permissions.ts # Permission validationDevelopment
Prerequisites
- Node.js 18.17 or later
- PostgreSQL 12 or later
- Bun (recommended) or npm/yarn
Setup
# Clone the repository
git clone https://github.com/Nuvix-Tech/database.git
cd database
# Install dependencies
bun install
# Set up environment variables
cp .env.example .env
# Edit .env with your database configuration
# Run tests
bun test
# Build the library
bun run build
# Type checking
bun run typecheck
# Lint code
bun run lintRunning Tests
# Run all tests
bun test
# Watch mode
bun run test:watch
# Test specific file
bun test tests/database.basic.test.tsEnvironment Variables
# PostgreSQL connection string
PG_URL=postgres://postgres:postgres@localhost:5432/test_dbConfiguration
Database Adapter Options
const adapter = new Adapter({
// PostgreSQL connection config
host: "localhost",
port: 5432,
database: "myapp",
user: "username",
password: "password",
// Or use connection string
connectionString: "postgres://user:pass@host:port/db",
// Connection pool settings
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Set metadata for multi-tenancy
adapter.setMeta({
database: "myapp",
schema: "public",
sharedTables: false,
tenantId: undefined,
tenantPerDocument: false,
namespace: "default",
});Cache Configuration
import { Memory, Redis } from "@nuvix/cache";
// In-memory cache (development)
const cache = new Memory();
// Redis cache (production)
const cache = new Redis({
host: "localhost",
port: 6379,
password: "redis-password",
});API Reference
Database Class Methods
Collection Management
create(database?)- Create database schemacreateCollection(options)- Create a new collectiongetCollection(id)- Get collection metadatalistCollections(limit?, offset?)- List all collectionsupdateCollection(options)- Update collection permissionsdeleteCollection(id)- Delete a collection
Document Operations
createDocument(collectionId, document)- Create a documentcreateDocuments(collectionId, documents)- Create multiple documentsgetDocument(collectionId, id, query?)- Get document by IDupdateDocument(collectionId, id, updates)- Update a documentupdateDocuments(collectionId, updates, query?)- Update multiple documentsdeleteDocument(collectionId, id)- Delete a documentdeleteDocuments(collectionId, query?)- Delete multiple documentsfind(collectionId, query?)- Query documents
Attribute Management
createAttribute(collectionId, attribute)- Add attribute to collectionupdateAttribute(collectionId, id, options)- Update attribute propertiesdeleteAttribute(collectionId, id)- Remove attribute from collectionrenameAttribute(collectionId, oldName, newName)- Rename an attribute
Relationship Management
createRelationship(options)- Create relationship between collectionsupdateRelationship(options)- Update existing relationshipdeleteRelationship(collectionId, id)- Delete a relationship
Index Management
createIndex(collectionId, id, type, attributes)- Create an indexdeleteIndex(collectionId, id)- Delete an indexrenameIndex(collectionId, oldName, newName)- Rename an index
Contributing
We welcome contributions! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
bun test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Code Style
- Use TypeScript with strict type checking
- Follow existing code formatting (Prettier)
- Write comprehensive tests for new features
- Update documentation for API changes
Testing
All contributions must include tests. We use Vitest for testing:
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { createTestDb } from "./helpers.js";
describe("Feature", () => {
const db = createTestDb();
beforeAll(async () => {
await db.create();
});
afterAll(async () => {
await db.getAdapter().$client.disconnect();
});
it("should work correctly", async () => {
// Test implementation
expect(result).toBe(expected);
});
});License
This project is licensed under the Proprietary License - see the LICENSE file for details.
Support
Built with ❤️ by the Nuvix team
