ormyx
v0.1.0
Published
A ORM-Based program for making JSON a relational-like database
Readme
Ormyx (JSON-based Relational Database)
MPOP Reverse II [Ryann Kim M. Sesgundo]
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-decto 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 ormyxHow 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, (numberorint),boolean. - ID Column: By default, an
idcolumn is added as anumberorint(incremental). To use a random string ID, defineid: '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 = valueformat. - 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, options: filterOptions)
Search for data within a table that matches specific criteria using a where clause.
// Example: Basic filtering
const results = db.filter('users', {
where: "age > 20 AND active = true"
});
// Using logical aliases (&, |, &&, ||)
const admins = db.filter('users', {
where: "role = admin & active = true"
});
// Pagination support
const pagedResults = db.filter('users', {
where: "active = true",
start: 0,
limit: 10
});Supported Operators:
- Comparison:
=,!=,>,<,>=,<=,in - Logical:
AND,OR,&&,||,&,|
Rules:
- Logical Operators:
&and&&act asAND.|and||act asOR. - Case Sensitivity: Operator aliases like
AND/ORare case-insensitive. - Values: Strings with spaces should be wrapped in quotes (e.g.,
name = 'John Doe').
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": "[email protected]",
"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
- Initialization:
ormyx()checks for the database file. If missing, it initializes a new encrypted file. - 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. - 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 featuresfix:for bug fixesrebrand:for rebranding changesdocs:ordoc:for documentation changesstyle:for formatting, missing semi colons, etc.refactor:for refactoring production codetest:for adding missing testschore: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
.envfiles 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
.datfiles. Encryption protects data privacy, not data loss.
License
This project is licensed under the MIT License.
