@xiara/mongo
Xiara/Mongo is a MongoDB ODM written entirely in typescript. It is based on mongo-node driver and aim's to support the latest features available in mongo.
This is an object modeling tool designed to work in asynchronous environments.
WHY?!
All the avilable libraries & frameworks require you to create an Interface and on top of that to create the definition of the model.
Or they are low with features, performance or are not documented properly and out-dated.
I wanted to use a simple api to create my models. The ability to use inheritance & other es6-7 features to define my models.
So i come up with the simplest solution. Define your model as a class with a single @Model
decorator that extends the MongoCollection
then bootsrap it with a MongoAdapter
Quick Start
Install via NPM:
npm install @xiara/mongo
import { MongoAdapter } from "@xiara/mongo";
let mongo = new MongoAdapter({
Hostname: "127.0.0.1",
Port: 27017,
Database: "test",
ReplicaSet: "replsetname",
Replicas: [
{
Hostname: "127.0.0.1",
Port: 27018,
},
{
Hostname: "127.0.0.1",
Port: 27019,
},
{
Hostname: "127.0.0.1",
Port: 27020,
}
]
});
mongo.bootstrap([
TodoModel,
UserModel,
IndexesModel,
TimedCollection,
]);
mongo.OnConnect().subscribe((success) => {
if(success)
{
console.log("MongoDB Connected");
}
});
Define a Collection
Below example shows how to define a simple model:
import { ObjectId, Model, MongoCollection, Property, Hook } from "@xiara/mongo";
@Model("todos")
export class TodoModel extends MongoCollection
{
@Property({ required: true })
task: string;
@Property({ required: true, default: Date, })
updatedAt: Date = new Date();
@Property({ required: true})
createdAt: Date = new Date();
}
Query the Collection
Simple Insert
var todo = new TodoModel();
todo.task = "Read this!";
todo.save().then( ... )
Initialize with the constructor
new TodoModel({
task: "Yup this is awesome!"
}).save().then( ... )
Provide _id to update
let todo = new TodoModel();
todo._id = new ObjectId("5a486940abd9be16c8c83b58");
todo.task = "Hey ho!"
todo.save().then( ... )
Find & Update
TodoModel.findOne<TodoModel>({
_id: new ObjectId("5a486940abd9be16c8c83b58")
}).then(todo => {
todo.task = "This is awesome!";
todo.save().then( ... )
});
Update
TodoModel.updateOne<TodoModel>({
_id: new ObjectId("5a486940abd9be16c8c83b58")
}, {
task: "updated!",
}).then( ... )
Insert
TodoModel.createOne<TodoModel>({
task: "created!",
}).then( ... )
Find Many
TodoModel.find<TodoModel>().then( todos => {
for(let todo of todos)
{
console.log("Task:", todo.task);
}
});
Inheritance & property sharing, relations, interfaces
import { ObjectId, Model, MongoCollection, Property, Hook } from "@xiara/mongo";
interface IPersonalDetails
{
}
export class TimedCollection extends MongoCollection
{
@Property({ required: true, default: Date, })
updatedAt: Date = new Date();
@Property({ required: true})
createdAt: Date = new Date();
}
@Model("users")
export class UserModel extends TimedCollection
{
@Property({ required: true })
username: string;
@Property({ required: true, hidden: true })
password: string;
@Property({ required: true })
personal: IPersonalDetails;
}
@Model("posts")
export class PostModel extends TimedCollection
{
@Property({ required: true, reference: UserModel, autoPopulate: true })
user: UserModel;
@Property({ required: true })
title: string;
@Property({ required: true })
content: string;
@Property({ required: true })
upvotes: number = 0;
@Property({ required: true })
downvotes: number = 0;
}
HOOKS & Instance Functions
Hooks come in all sort of types:
db:connected
Triggered when database connection is estabilished.collection:init
Triggerd when a collection is created (ex: new Model()
)collection:created
Triggerd when the collection is readyindexes:created
Triggered when indexes are addedvalidate
Triggered before field validation (filter)fields:hidden
Triggered when hidden fields are requested (filter)fields:visible
Triggered when visible fields are requested (filter)
import { ObjectId, Model, MongoCollection, Property, Hook } from "@xiara/mongo";
@Model("hooks")
export class HooksExample extends MongoCollection
{
@Property()
field: string;
constructor()
{
super({
field: "Always bored?",
});
}
getCustomData()
{
return this.field + "test";
}
@Hook("db:connected")
static OnConnected()
{
}
@Hook("collection:init")
static OnConnected()
{
}
@Hook("fields:hidden")
static OnHiddenFiledsFiltered(fields)
{
return fields.filter( field => field.name == "field" );
}
}
Indexes, Model Decorator, Others
import { ObjectId, Model, MongoCollection, Property, Hook } from "@xiara/mongo";
@Model("hooks", {
indexes: {
specification: {
username: 1,
anotherindex: -1
},
unique: true
},
inherits: [ TimedCollection ],
})
export class IndexesModel extends MongoCollection
{
@Property()
username: string;
@Property()
anotherindex: string;
@Property({ unique: true, index: 1})
singleindex: string;
}
You can use get / set
accessors to implement hooks (usefull for hashing passwords on insert.)
import { ObjectId, Model, MongoCollection, Property, Hook } from "@xiara/mongo";
@Model("example")
export class ExampleModel extends MongoCollection
{
@Property({ hidden: true })
protected hashedPassword: string = null;
get password(): string
{
return this.hashedPassword;
}
set password(inValue: string)
{
this.hashedPassword = hashingAlgorithm(inValue);
}
toJSON()
{
var data = super.toJSON();
delete data.password;
return data;
}
}
Query Builder & Aggregates
Aggregate
class CustomCollection extends MongoCollection
{
count: number;
}
CustomCollection.aggregate<CustomCollection>([
{
$group: {
}
},
{
$project: {
count: 1,
}
}
]).then( ... )
Query Builder
UserModel.query<UserModel>({
banned: false,
})
.where({status: 'Online'})
.gte({upvotes: 100})
.lte({downvotes: 10})
.populate("friends")
.forEach( user => {
console.log("users online", user);
});
.then((users) => {
})
User Model Example & Password hashing
UserModel.ts:
import { Model, MongoCollection, Property, Hook } from "@xiara/mongo";
import { TimedCollection } from "./Base";
import { compareSync, genSaltSync, hashSync } from "bcrypt";
@Model("users")
export class UserModel extends MongoCollection
{
@Property({ required: true, unique: true })
username: string;
@Property({ required: true, hidden: true })
protected hashedPassword: string;
get password(): string
{
return this.hashedPassword;
}
set password(inValue: string)
{
this.hashedPassword = hashSync(inValue, genSaltSync(10));
}
validatePassword(inPassword: string): boolean
{
return compareSync(inPassword, this.hashedPassword)
}
}
App.ts
import { UserModel } from "./UserModel";
let user = new UserModel();
user.username = "azarus";
user.password = "test";
user.save().catch( error => {
console.log("error:", error);
})
UserModel.findOne<UserModel>({
username: "azarus"
}).then( user => {
console.log("Password valid:", user.validatePassword("test"));
})
Have a question?
License
MIT
Soon
- Plugins
- More Hooks
- More Features