FuseDB is an ORM with traditional ActiveRecord approach, that provides a simple yet powerful API. FuseDB stores data in the filesystem (nedb) or MongoDB. You write your own adapter and implement a different database suppport.
It's perfectly suitable for medium scale databases or Electron apps and takes 5 minutes to dive in.
Checkout this example
Everything works by default, and the files will be stored in your home folder e.g /home/user/.fusedb
on linux or /Users/user/.fusedb
on mac. In order to customise it, do the following
import { FuseDB, FileAdapter } from "fusedb"
FuseDB.setup({ adapter :
FileAdapter({ path: "/path/to/folder/", database: "test" }) });
Install mongo module first
npm install mongodb --save
import { FuseDB, MongoAdapter } from "fusedb";
FuseDB.setup({
adapter: MongoAdapter({
url : "mongodb://localhost:27017/fusedb",
dbName : "myProject"
})
});
Models contain essential methods to talk to the database, methods like save
, find
, remove
are reserved. Therefore we don't need any "repositories" and connection pools as everything is handled internally
import { Field, Model } from "fusedb";
class Author extends Model<Author> {
@Field()
public name: string;
@Field()
public books: Book[];
}
class Book extends Model<Book> {
@Field()
public name: string;
@Field()
public author: Author;
}
Field
decorator tells fusedb to serialize the field. There are a few reserved methods:
const john = new Author({ name: "John" });
await john.save();
const book1 = new Book({ name: "book1", author: john });
const book2 = new Book({ name: "book2", author: john });
await book1.save();
await book2.save();
john.books = [book1, book2];
await john.save();
FuseDB will save references as ids
and it won't store the entire model
Field decorator has a few properties you might find useful
hidden
Hiding your field when sending to view
@Field({ hidden : true })
public password: string;
Define custom serialiser when sending to view
@Field({
toJSON : value => moment(value).format("LLLL")
})
public date: Date;
First record:
const author = await Author.find<Author>({ name: "john" }).first();
All records:
const authors = await Author.find<Author>({
name : {$in : ["a", "b"]}
}).all();
Count:
const num = await Author.find<Author>().count();
You don't need to convert strings to ObjectID When you using MongoAdapter. FuseDB does it for you.
for example:
const record = await Foo.findById<Foo>("5b290c188e9f69ab51c3bd41");
It will be applied for find
method recursively for example
await Foo.find<Foo>({
_id : $in : ["5b290c188e9f69ab51c3bd41", "5b290c188e9f69ab51c3bd42"]
}).first()
Instead of extracting IDs you can pass a real FuseDB Model to the query. For example
const group = await Group.find({name : "admin"}).first();
const adminUsers = await Users.find({group : group}).all(); // will fetch all users that belong to admin group
const authors
= await Author.find<Author>({active : true})
.sort("name", "desc")
.limit(4)
.skip(2)
.all()
FuseDB can automatically join referenced fields by making optimised requests (collecting all ids and making additional queries to the database) e.g
const books = await Book.find<Book>().with("author", Author).all();
const author = new Author();
author.name = "john"
await autor.save()
const author = await Author.find({name : "john"});
await author.remove()
Defining the following hooks will allow you to intercept model events
export class Foo extends Model<Foo> {
@Field()
public name: string;
// before updating or creating a record
async onBeforeSave() {}
// after creating or updating a record
async onAfterSave() {}
// before updating a record
async onBeforeUpdate() {}
// after creating a record
async onBeforeCreate() {}
}
Validators in FuseDb are quite easy to use and implement. The framework offers a few default validators, in order to enable them call a function in your entry point (before you start importing your models)
import { enableDefaultDecorators } from "fusedb"
enableDefaultDecorators();
Now you can start using them in your models, like that:
class FooBarMax extends Model<FooBarMin> {
@Field() @Validate({max : 3})
public name: string;
}
Default validators can assert a custom message
@Validate({nameOftheValidator : { message :"Failed", value : 3}})
class FooBarMin extends Model<FooBarMin> {
@Field() @Validate({min : 3})
public name: string;
}
class FooBarMax extends Model<FooBarMax> {
@Field() @Validate({max : 3})
public name: string;
}
class FooBarEmail extends Model<FooBarEmail> {
@Field() @Validate({email : true})
public name: string;
}
class FooBarRegExp extends Model<FooBarRegExp> {
@Field() @Validate({regExp : /\d{2}/})
public name: string;
}
const seasons = {
SUMMER: 'summer',
WINTER: 'winter',
SPRING: 'spring'
}
class FooBarEnum extends Model<FooBarEnum> {
@Field() @Validate({enum : seasons})
public season: string;
}
class FooBarCustom extends Model<FooBarCustom> {
@Field()
@Validate({fn : value => {
if( value !== "foo") throw new Error("Value should be foo only")
}})
public name: string;
}
Define a class with Validator
import { Validator, FieldValidator } from "fusedb";
@Validator()
export class OopsValidator implements FieldValidator {
validate(field: string, props: any, value: any) {
throw "Somethign wentWrong"
}
}
A validator with name oops
has be registered, how you can use it in your models
class Hello extends Model<Hello> {
@Field() @Validate({oops : true})
public name: string;
}