MasqueradeORM is a lightweight ORM for Node.js that works seamlessly with both TypeScript and JavaScript.
Its goal is to hide SQL complexity while letting you work naturally in JS/TS syntax. Instead of forcing you into ORM-specific models, metadata systems, or decorators, MasqueradeORM lets you use your own classes directly, exactly as you normally would.
MasqueradeORM improves readability, maintainability, and workflow simplicity through a unified coding approach and extremely minimal setup. No ORM offers a simpler start. There’s no need to manage heavy configuration layers, maintain secondary schema systems, or even plan your database structure separately. Your schema and tables are generated automatically from a single source of truth: Your class definitions.
MasqueradeORM currently supports the following SQL clients:
- SQLite
- PostgreSQL
npm install masquerade-orm- Effortless setup - No ORM-specific structures; just use your classes.
- Zero schema planning - Tables and schema are generated automatically.
- Powerful IntelliSense - Confidently build complex queries with real-time IDE guidance and warnings when something’s wrong.
- Minimal memory usage - Automatically prevents duplicate entity instances by maintaining a single in-memory representation per database row using an Entity Map.
- Batched implicit writes - Minimizes database round-trips by batching operations into optimized transactions while preserving consistency and state integrity.
- Cross-column & cross-table conditions made effortless - Use relational and non-relational data in WHERE and ORDER BY clauses with clean, IntelliSense-supported syntax that automatically handles the all the necessary joins for you. Focus on your business logic while the ORM manages the tedious work.
- Expressive template-literal WHERE clauses - Write complex, readable conditions such as LIKE, ≥, nested property access, array element matching and more, by using IntelliSense-guided tagged template literals. Any valid SQL WHERE logic is possible, with safe parameter interpolation and full relational nesting support.
- Advanced, flexible sorting with aggregates, relations & custom expressions - Go far beyond basic ASC/DESC: support multi-column tie-breakers, ORDER BY with aggregates (COUNT, AVG, etc.), and fully custom computed expressions via template literals. Perfect for leaderboards, recommendations, or relevance scoring: all IntelliSense-friendly.
- Powerful relation capabilities - Full support for eager & lazy loading, unidirectional / bidirectional / self-referencing relationships, and modifying associations even when they are not loaded.
- SQL injection protection - All queries are parameterized.
- Minimal data transfer size - Improves performance in client-server setups (not applicable for embedded databases like SQLite).
- Soft deletion and hard deletion support
- Abstract and non-abstract inheritance - Enables the use of abstract classes, even in JavaScript.
- Strong typing even in JavaScript - Powered by JSDoc, no compile step required.
- Smart runtime schema cleanup - Identifies unused tables and columns at runtime and provides actionable cleanup through a built-in class with static methods, reducing database bloat and maintaining optimal performance.
- Lightweight - Requires just two dependencies, keeping the library lean and easy to integrate.
- Combines the convenience of embedded SQLite with the strict typing of RDBMS
import { Entity } from 'masquerade'
type UserSettings = {
theme: 'light' | 'dark' | 'system'
twoStepVerification: boolean
locale: 'en' | 'es' | 'fr' | 'de'
}
export class User extends Entity {
username: string
email: string
password: string
createdAt: Date = new Date()
friendList: User[] = []
settings: UserSettings & object = {
locale: "en",
theme: "system",
twoStepVerification: false
}
constructor(username: string, email: string, password: string) {
super()
this.username = username
this.email = email
this.password = password
}
}// finds any User instance with email === lookupEmail
async function findUserByEmail(lookupEmail: string): Promise<User | undefined> {
const resultArray = await User.find({
where: { email: lookupEmail }
})
// the static 'find' method above is inherited from 'Entity'
return resultArray[0]
}// Creating a new table row in the User table
const newUser = new User('JohnDoe57', 'johnDoe@yahoo.com', 'passwordHash')
// newUser will be saved to the database automatically, no explicit save call is required.
// Finding a user by email
const user = await findUserByEmail('johnDoe@yahoo.com') // The user's 'friendList' is a LazyPromise (not yet loaded - will load once awaited)
console.log(user.username === 'JohnDoe57') // trueAlternatively, it is possible to explicitly save an instance's changes:
const user = await User.find({where: id: 123)})
user.isAdmin = true
try {
await user.save()
}
catch (e) {
console.log(e)
}All mutations are persisted implicitly and automatically, meaning that simply changing a value is enough for it to be reflected in the database.
Mutating non-Relational Values
user.settings.theme = 'dark' Mutating Relational Values
// ** Assuming the user's 'friendList' is still a LazyPromise **
// add a new relation (without having to load 'friendList')
user.friendList.push(new User('JaneDoe33', 'janeDoe@yahoo.com', 'passwordHash2'))
// load 'friendList'
await user.friendList
// remove a relation (requires 'friendList' to be loaded)
user.friendList.pop() - Getting Started - Javascript
- Getting Started - Typescript
- Defining Classes: In-Depth (important read)
- Find Method
- Saving to Database
- Deleting Instances from the Database
- Managing Database Tables
- JSDoc – UX Tips