Skip to content

RyannKim327/ormyx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

109 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ormyx (JSON-based Relational Database)

MPOP Reverse II [Ryann Kim M. Sesgundo]

wakatime npm version

Ormyx is a TypeScript-based ORM-like library designed to manage JSON data as a relational-like database with built-in encryption. It simplifies data persistence by providing a structured way to handle tables, rows, and automatic ID generation while keeping your data secure.

Note

I don't recommend using this for large-scale projects; it's best suited for small projects, prototypes, or hobbies. This was created for fun and as a way to expand my ideas and knowledge.

Table of Contents

Features

  • Relational Structure: Organize your JSON data into tables and records.
  • Built-in Encryption: Uses json-enc-dec to keep your database files secure.
  • Data Filtering & Type Safety: Automatically sanitizes input and enforces column types to prevent unwanted data injection.
  • Auto ID Generation: Support for both random 12-character strings and incremental numeric IDs.
  • Flexible Data Insertion: Support for both JSON objects and easy-to-use string formats.
  • TypeScript Support: Fully typed for an enhanced developer experience.

Installation

npm install ormyx

How To

1. Initialization

ormyx(key: string, filename?: string)

To get started, you need to initialize Ormyx with a secret encryption key. If the database file does not exist, it will be created automatically.

Option A: Providing a Custom Filename You can specify a name for your database. Filenames are automatically sanitized (only the base name is kept) and the .dat extension is added if missing.

import { ormyx } from 'ormyx';

// This creates/loads 'my-database.dat'
const db = ormyx('your-secret-key', 'my-database');

// 'my.db.json' will be sanitized to 'my.dat'
const db2 = ormyx('your-secret-key', 'my.db.json');

Option B: Using the Default Filename (Optional) If you omit the second parameter, the database will default to data.dat.

import { ormyx } from 'ormyx';

// This creates/loads 'data.dat'
const db = ormyx('your-secret-key');

Warning: The encryption key is the only way to access your data. If you lose the key or use a different one later, you will not be able to read the existing database. Always store your keys in environment variables!

2. Creating Tables (Required)

db.create(tableName: string, columns: object | string, autoincrement?: boolean)

You must create a table before you can insert any data. This defines the structure, column types, and ID generation strategy.

Option A: Using an Object (Recommended) Define columns and their types using a JSON object.

// Example: db.create('users', { username: 'string', email: 'string', active: 'boolean' })
db.create('users', {
    username: 'string',
    email: 'string',
    role: 'string',
    active: 'boolean'
});

Option B: Using a String Format A convenient shorthand for defining columns and types.

// Example: db.create('users', "username = string, email = string, age = int")
db.create('users', "username = string, email = string, role = string, active = boolean");

Column Type Rules:

  • Supported Types: string, (number or int), boolean.
  • ID Column: By default, an id column is added as a number or int (incremental). To use a random string ID, define id: 'string'.
  • Autoincrement: The third parameter (default: true) controls if numeric IDs should automatically increment.

3. Inserting Data

db.insert(tableName: string, data: object | string, options?: insertOptions)

You can insert data into your tables by providing either a JSON object or a formatted string.

Using JSON Object (Recommended)

This method is recommended for structured data and better type safety.

// Example: db.insert('users', { username: 'ryannkim', active: true })
try {
    const result = db.insert('users', {
        username: 'ryannkim',
        role: 'developer',
        active: true
    });
    console.log('Inserted record:', result);
} catch (error) {
    console.error('Failed to insert:', error.message);
}

Using String Format

A convenient shorthand for simple data entry.

// Example: db.insert('users', "username = mpop, role = admin")
db.insert('users', "username = mpop, role = admin, active = true");

String Format Rules:

  • Pairs: Use key = value format.
  • Types: Numbers and booleans (true/false) are automatically parsed.
  • Quotes: Wrap values in ' or " if they contain commas or special characters.
  • Separators: Separate fields with a comma (,).

ID Generation Strategies

Control how IDs are generated via the options parameter or table structure.

// Incremental ID: 1, 2, 3... (Default if 'id' type is 'number')
db.insert('logs', { event: 'startup' });

// Random 12-character alphanumeric ID (Default if 'id' type is 'string')
db.insert('sessions', { token: 'xyz123' });

// Custom ID length for string-based IDs
db.insert('tokens', { value: 'abc' }, { idLength: 16 });

4. Reading Data

db.read(tableName: string, id: string | number)

Retrieve a specific record by its ID.

// Example: db.read('users', 1)
const user = db.read('users', 1);

if (user) {
    console.log('User data:', user);
} else {
    console.error('Record not found');
}

5. Filtering Data

db.filter(tableName: string, query: object | string)

Search for data within a table that matches specific criteria.

// Example: db.filter('users', { role: 'admin' })
const admins = db.filter('users', { role: 'admin' });
console.log('Admins found:', admins);

// Using string format
const activeUsers = db.filter('users', "active = true");

6. Updating Data

db.update(tableName: string, id: string | number, data: object | string)

Modify an existing record. New data is merged with the existing record.

// Example: db.update('users', 1, { active: false })
db.update('users', 1, { active: false });

// Using string format
db.update('users', 1, "role = administrator, active = true");

7. Deleting Data

db.remove(tableName: string, id: string | number)

Remove a specific record from a table.

// Example: db.remove('users', 1)
const status = db.remove('users', 1);
console.log(status.message); // "Deleted successfully"

8. Altering Tables

db.alter(tableName: string, newColumns?: object | string, deleteColumns?: string[])

Modify the structure of an existing table. You can add new columns with types or remove existing ones.

// Add new columns to the 'users' table
db.alter('users', { age: 'number', gender: 'string' });

// Using string format
db.alter('users', "address = string");

// Remove columns from the 'users' table
db.alter('users', undefined, ['role']);

9. Renaming Tables

db.rename(oldTableName: string, newTableName: string)

Rename an existing table. This will preserve both the table structure and its data.

// Example: db.rename('users', 'members')
const result = db.rename('users', 'members');
console.log(result.message); // "Table users is now renamed to members"

Data Structure

The internal structure of the database follows a hierarchical JSON format:

{
  "table_struct": {
    "users": {
        "username": "string",
        "email": "string",
        "role": "string",
        "active": "boolean",
        "id": "number"
    }
  },
  "users": {
    "1": {
      "username": "ryannkim",
      "email": "ryann@example.com",
      "role": "developer",
      "active": true,
      "id": 1
    }
  }
}

TypeScript Interfaces

type data_structure = Record<string, string | number | boolean | undefined | null>
type json_data = Record<string | number, data_structure>
type main_structure = Record<string, json_data>

interface insertOptions {
    increment?: boolean
    idLength?: number
}

How it Works

  1. Initialization: ormyx() checks for the database file. If missing, it initializes a new encrypted file.
  2. Operations: Every write operation (insert, update, remove) synchronizes with the encrypted file, performs the operation in-memory, and then flushes the re-encrypted data back to disk.
  3. Encryption: Powered by json-enc-dec, ensuring data at rest is secure.

Contributing

Contributions are welcome! To maintain a clean and manageable history, please use Semantic Commit Messages:

  • feat: for new features

  • fix: for bug fixes

  • rebrand: for rebranding changes

  • docs: or doc: for documentation changes

  • style: for formatting, missing semi colons, etc.

  • refactor: for refactoring production code

  • test: for adding missing tests

  • chore: for updating build tasks, package manager configs, etc.

Example: feat: add support for custom primary keys

Security Best Practices

  • Environment Variables: Never hardcode your encryption key. Use .env files or secrets managers.
  • Key Consistency: Using a different key on an existing database will result in decryption failure and potential data corruption if forced.
  • Backups: Regularly backup your .dat files. Encryption protects data privacy, not data loss.

License

This project is licensed under the MIT License.


View Changelog

About

A ORM-Based program for making JSON a relational-like database (This is just to practice programming)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors