@kang-bakso-development/fast-orm
v1.0.0
Published
A Fast and TypeScript-based MySQL ORM with CLI model generation and migration capabilities.
Maintainers
Readme
FastOrm
FastOrm is a lightweight and fast Object-Relational Mapper (ORM) for Node.js, designed specifically for MySQL databases. Built with TypeScript, FastOrm provides a fluent and structured way to interact with your database, offering features like query building, transaction management, relations, and a powerful command-line interface (CLI) for model scaffolding and migrations.
Key Features
- Fluent Query Builder: Construct complex database queries with a readable syntax, supporting
SELECT,WHERE(with nestedAND/OR,IN,BETWEEN,NULL),JOIN,GROUP BY,HAVING,ORDER BY,LIMIT, andOFFSET. - Model-Based Interactions: Define your TypeScript models to represent database tables, enabling intuitive CRUD operations.
- Transactional Support: Execute a series of database operations within a safe transaction, ensuring data integrity.
- Database Introspection: Automatically detect database schema (tables and columns) to aid in model generation.
- CLI Tools:
fastorm generate:from-db: Generate TypeScript models directly from an existing MySQL database schema.fastorm generate:from-schema: Generate models from custom schema definition files (JSON/TS/JS).fastorm make:migration: Create new migration files to manage database schema changes.fastorm migrate:up/fastorm migrate:down: Run or rollback database migrations.
- Strongly Typed: Leverages the power of TypeScript for strict type validation and autocompletion, reducing runtime errors.
Installation
To get started with FastOrm in your Node.js project, install it via npm along with its peer dependencies:
npm install fastorm mysql2 dotenv
npm install -D typescript @types/node @types/mysql2 ts-nodeConfiguration
Database Connection (.env) file
FastOrm relies on environment variables for database connection details. Create a .env file in the root of your project:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=your_database_nameRemember to replace your_password and your_database_name with your actual MySQL credentials.
Usage
1. Initialize FastOrm in Your Application
First, initialize FastOrm in your application's entry point (e.g., src/index.ts or app.ts):
// src/index.ts or app.ts
import { FastOrm } from 'fastorm';
import { PoolOptions } from 'mysql2/promise';
import * as dotenv from 'dotenv';
dotenv.config(); // Load environment variables from .env
const dbConfig: PoolOptions = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
};
const orm = new FastOrm(dbConfig);
// Export the ORM instance and its Model constructor for use throughout your application
export const connection = orm.connection;
export const Model = orm.Model;
export default orm;Example Usage (JavaScript)
Here’s how you can use FastOrm in a JavaScript project:
// db.js
const { FastOrm } = require("fastorm");
const orm = new FastOrm({
host: "localhost",
user: "root",
password: "your_password",
database: "your_database",
});
module.exports = orm;// usage.js
const orm = require("./db");
async function getUsers() {
const users = await orm
.table("users")
.select("*")
.where("status", "active")
.orderBy("created_at", "desc")
.limit(10)
.get();
console.log(users);
}
getUsers();FastOrm works seamlessly in JavaScript projects without the need for TypeScript, while still offering type-safety benefits when using TypeScript.
2. Defining Your Models
You can define your models manually, or use the FastOrm CLI to generate them from your database or a schema file. Models typically reside in a directory like src/models/.
// src/models/User.ts (Example: can be generated by CLI)
import { Model, Identifiable, BaseData } from 'fastorm';
// Define the interface for your model's data structure
export interface IUser extends BaseData {
id: number;
name: string;
email: string;
age?: number;
status: 'active' | 'inactive';
created_at: Date;
updated_at: Date;
}
// Extend FastOrm's Model class, passing the table name
export class User extends Model<IUser> {
constructor() {
super('users'); // 'users' is the actual table name in your database
// You can add hooks for lifecycle events (optional)
this.addHook('beforeInsert', (data) => {
console.log('Before inserting user:', data.name);
return data;
});
}
}3. Performing Database Queries
Once your models are defined and FastOrm is initialized, you can start interacting with your database.
// Example usage in an application file (e.g., src/app.ts)
import orm, { Model } from "./index"; // Assuming you exported orm and Model from index.ts
import { IUser, User } from "./models/User"; // Import your User model and interface
async function demonstrateFastOrm() {
// You can instantiate your specific model
const userModel = new User(); // The constructor already sets the table name
try {
// --- Basic CRUD Operations ---
// Create a new user
const newUser = await userModel.create({
name: "John Doe",
email: "[email protected]",
age: 30,
status: "active",
});
console.log("Created User:", newUser);
// Find a user by ID
const user = await userModel.find(newUser.id);
console.log("Found User:", user);
// Update a user
const updatedUser = await userModel.update(newUser.id, {
age: 31,
status: "inactive",
});
console.log("Updated User:", updatedUser);
// --- Query Builder Examples ---
// Get all users
const allUsers = await userModel.query().get();
console.log("All Users:", allUsers);
// Complex query: Active users over 25, ordered by name, limit 10
const activeUsersOver25 = await userModel
.query()
.select("id", "name", "email")
.where("age", ">", 25)
.where("status", "=", "active")
.orderBy("name", "ASC")
.limit(10)
.get();
console.log("Active users over 25:", activeUsersOver25);
// Query with nested OR conditions
const vipOrNewUsers = await userModel
.query()
.where("is_vip", "=", true)
.orWhere((q) => {
// Use a callback for nested OR
q.where("created_at", ">", "2023-01-01").where("status", "=", "active");
})
.get();
console.log("VIP or New Active Users:", vipOrNewUsers);
// Using JOINs (assuming a 'posts' table with 'user_id' column)
const usersWithTheirPosts = await userModel
.query()
.select("users.name", "posts.title as postTitle")
.innerJoin("posts", "users.id", "=", "posts.user_id")
.get();
console.log("Users and their posts:", usersWithTheirPosts);
// Aggregations
const totalUsers = await userModel.query().count();
console.log("Total Users:", totalUsers);
// Delete a user
const deleteResult = await userModel.delete(newUser.id);
console.log("Deleted User:", deleteResult);
} catch (error) {
console.error("Database Operation Error:", error);
} finally {
// Always disconnect the ORM when your application shuts down
await orm.disconnect();
}
}
demonstrateFastOrm();4. Running Transactions
FastOrm provides a robust way to perform a series of database operations atomically using transactions.
// Example transaction in src/app.ts
import orm, { Model } from "./index"; // Assuming you exported orm and Model from index.ts
import { IUser } from "./models/User"; // Import your User interface
async function runTransactionExample() {
try {
const transactionResult = await orm.transaction(
async (trxConnection, TrxModel) => {
// Inside the transaction callback, use the provided `TrxModel`
// and `trxConnection` to ensure all operations are within the same transaction.
const trxUserModel = new TrxModel<IUser>("users"); // Create model instances using TrxModel
// const trxProductModel = new TrxModel<IProduct>('products'); // If you had a Product model
// Operation 1: Update user status
const updatedUser = await trxUserModel.update(1, {
status: "inactive",
});
console.log("User status updated in transaction:", updatedUser);
// Operation 2: Simulate another operation, e.g., decrease product stock
// For demonstration, let's assume 'products' table exists.
// const updatedProduct = await trxProductModel.update(101, { stock: 99 });
// console.log('Product stock updated in transaction:', updatedProduct);
// Uncomment the line below to simulate an error and trigger a rollback
// throw new Error("Simulating a transaction error for rollback!");
return { user: updatedUser /*, product: updatedProduct */ }; // Return data to be committed
}
);
console.log("Transaction committed successfully:", transactionResult);
} catch (error) {
console.error(
"Transaction rolled back due to error:",
(error as Error).message
);
} finally {
await orm.disconnect();
}
}
// runTransactionExample();]Command Line Interface (CLI)
FastOrm comes with a powerful CLI tool to help you manage your database schema and models.
To make the fastorm command available, you should add it to your package.json scripts:
// package.json
{
"name": "your-project-name",
"version": "1.0.0",
"description": "My FastOrm application",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"fastorm": "ts-node src/cli.ts" # Make sure this path points to your cli.ts file
},
"dependencies": {
"fastorm": "^1.0.0",
"mysql2": "^3.0.0",
"dotenv": "^16.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"@types/mysql2": "^1.0.0",
"ts-node": "^10.0.0"
}
}Now you can run CLI commands using npm run fastorm <command>.
fastorm generate:from-db <outputDir>
Generates TypeScript models by introspecting an existing MySQL database schema.
npm run fastorm generate:from-db src/models --db-name your_database_name --user root --password your_password
# You can also use a JSON configuration file (e.g., config/db.json)
npm run fastorm generate:from-db src/models -c config/db.jsonfastorm generate:from-schema <schemaFile> <outputDir>
Generates TypeScript models from a custom schema definition file (JSON/TS/JS). This is useful for code-first approaches.
npm run fastorm generate:from-schema src/schemas/user.schema.json src/modelsExample src/schemas/user.schema.json:
{
"tableName": "users",
"columns": [
{
"name": "id",
"type": "number",
"primaryKey": true,
"autoIncrement": true
},
{ "name": "name", "type": "string", "maxLength": 255 },
{ "name": "email", "type": "string", "unique": true },
{ "name": "age", "type": "number", "nullable": true },
{
"name": "status",
"type": "string",
"enumValues": ["active", "inactive"],
"default": "active"
}
]
}fastorm make:migration <name>
Creates a new migration file with a timestamp, ready for your UP and DOWN schema changes.
npm run fastorm make:migration create_users_table
# Specify a custom directory for migration files
npm run fastorm make:migration add_posts_table --dir database/migrationsfastorm migrate:up
Runs all pending (unapplied) migrations.
npm run fastorm migrate:up --db-name your_database_name
# Use a custom migrations directory and config file
npm run fastorm migrate:up -c config/db.json --dir database/migrationsfastorm migrate:down
Rolls back the last N migrations (default: 1 step).
Contribution
Contributions are highly appreciated! If you find a bug or have an idea for a new feature, please open an issue or submit a pull request.
License FastOrm is MIT Licensed.
