unimapperjs
TypeScript icon, indicating that this package has built-in type declarations

1.1.10 • Public • Published

UniMapperJS

NPM version License: MIT Gitter chat

UniMapperJS is universal Node.js (native ES6/ES7) LINQ-like object mapper (ORM/ODM) which can map whatever you create adapter for.

!! Under DEVELOPMENT !!

See wiki

Main Features

  • Adapters - if your database isn't supported, you can write own adapter,
  • migrations - migration scripts - you can check migration script before applying,
  • seeding - seed default data into storage,
  • more connections across adapters (eg. take data from MongoDB or two MongoDBs and insert it into MySQL),
  • unit of work - tracking changes in transactions,
  • updating just changed properties - saving resources,
  • quering like C# LINQ to Entities (with JS function names)

TODO

  • implement Entity.addUnique() which allow UNIQUE key over more fields
  • implement Entity.addPrimary() which allow PRIMARY key over more fields
  • implement bulk operations
  • implement JOINs
  • implement nested operations (eg. saving Enterprise should save all Employees in Enterprise.Employees collection too. Same rollbacking etc.)
  • add beforeSave() method into entity class - allow some operation before saving
  • add baseQuery property into entity class - it'll be used in ALL select queries (can be used for soft-delete)
  • implement client-side execution (query from client will send AJAJ request to server which will handle it as normal server query; something like automatic GraphQL)
  • add whereOr() and whereOrIf() into Query

Example

You can clone and play with the example in this repo.

Directory structure of this example

  • entities
  • migrations
  • domain.js
  • create-migration.js
  • run-migration.js

Create domain

domain.js

const $um = require("unimapperjs");
const MySqlAdapter = require("unimapperjs/adapters/MySqlAdapter");
 
// Domain creation - connect to database via MySQL adapter
const domain = $um.createDomain(MySqlAdapter, { // connection string or object with options - specific to adapter
    host: '127.0.0.1',
    user: 'test',
    password: 'test',
    database: "test"
});
exports.domain = domain;

Create entities

Then create entities in given domain. In TypeScript you can declare entity like this one.

etities/Student.ts

import {type} from "unimapperjs";
import {Entity} from "unimapperjs/src/Entity";
import {domain} from "../domain";
import {Teacher} from "./Teacher";
 
@domain.entity()
export class Student extends Entity<Student>
{
    /**
     * Student Id
     */
    id: number;
 
    /**
     * Student name
     */
    name: string;
 
    /**
     * Student's teacher id
     */
    teacherId: number;
 
    /**
     * Navigation property to Teacher
     */
    teacher: Promise<Teacher>;
 
    static map(map: Student) {
        const {Teacher} = require("./Teacher");
 
        map.id = <any>type.uuid;
        map.name = <any>type.string.length(100);
        map.teacherId = <any>type.number;
        map.teacher = <any>type.foreign(Teacher.name)
            .withForeign<Student>(s => s.teacherId);
    }
}

entities/Teacher.ts

import {type} from "unimapperjs";
import {Entity} from "unimapperjs/src/Entity";
import {domain} from "../domain";
import {Student} from "./Student";
 
/**
 * Teacher entity
 */
@domain.entity()
export class Teacher extends Entity<Teacher>
{
    /**
     * Teacher ID
     */
    id: number;
 
    /**
     * First name
     */
    firstName: string;
 
    /**
     * Last name
     */
    lastName: string;
 
    /**
     * Navigations property to assigned students
     */
    students: Promise<Array<Student>>;
 
    /**
     * Mapping
     */
    static map(map: Teacher) {
        const {Student} = require("./Student");
 
        map.id = <any>type.number.primary().autoIncrement();
        map.firstName = <any>type.string.length(50);
        map.lastName = <any>type.string.length(50);
        map.students = <any>type.foreign(Student.name)
            .hasMany<Student>(s => s.teacherId);
    }
}

Migrate

Now you can run migraion.

create-migration.js

const $umjs = require("unimapperjs");
const $path = require("path");
const {domain} = require("./domain");
 
// Discove all entities from given path
$umjs.initEntitiesFrom($path.resolve(__dirname, "entities"));
 
// Run it in next event loop iteration
setImmediate(async () => {
    await domain.createMigration($path.resolve(__dirname, "migrations"));
    await domain.dispose();
});

New migration script is gonna be generated in folder ./migrations like this one.

/**
 * Migration script
 */
 
module.exports = {
    up: async function up(adapter) {
        await adapter.createEntity("Student", {
              "id": {
                  "type": "String"
                , "length": 37
                , "primary": true
            }
            , "name": {
                  "type": "String"
                , "length": 100
            }
            , "teacherId": {
                  "type": "Number"
                , "length": 11
            }
        });
 
        await adapter.createEntity("Teacher", {
              "id": {
                  "type": "Number"
                , "length": 11
                , "primary": true
                , "autoIncrement": true
            }
            , "firstName": {
                  "type": "String"
                , "length": 50
            }
            , "lastName": {
                  "type": "String"
                , "length": 50
            }
        });
 
        await adapter.addForeignKey("Student", "teacherId", "Teacher", "fk_Student_teacherId_Teacher_id");
    }
};

You can run that migration script with

run-migration.js

const $path = require("path");
const {domain} = require("./domain");
 
setImmediate(async () => {
    await domain.runMigration($path.resolve(__dirname, "migrations"));
    await domain.dispose();
});

Insert some data

let teacher = new Teacher({
    firstName: "John",
    lastName: "Wick"
});
await Teacher.insert(teacher);
 
let teacher2 = new Teacher();
teacher2.firstName = "John";
teacher2.lastName = "Smith";
await Teacher.insert(teacher2);
 
let student = new Student({
    name: "Bart Simpson",
    teacherId: teacher.id
});
await Student.insert(student);
 
student.teacher = teacher2;
await student.save();

Make some queries

// Teachers their first name contains "u"
let teachers = await Teacher.getAll()
    .filter(t => t.firstName.includes("u"))
    .sort(t => t.firstName)
    .exec();
// Students their name starts with 'P' or ends with 's'
let startsWith = "P";
let students = await Student.getAll()
    .filter(s => s.name.startsWith($) || s.name.endsWith("s"), startsWith)
    .sortDesc(s => s.name)
    .slice(3, 8) // limit 5, skip 3
    .exec();
let onlyActive = true;
let activeSubjectsCount = await Subject.getAll()
    .filterIf(s => s.active === true, onlyActive) // if (onlyActive) { .filter(s => s.active === true) }
    .count()
    .exec();
let subjectNames = await Subject.getAll()
    .sort(s => s.name)
    .map(s => s.name) // select only names ; in SQL SELECT name FROM Subject
    .exec();
let subjectMap = await Subject.getAll()
    .sort(s => s.name)
    .map(s => ({
        id: s.id,
        name: s.name
    }))
    .exec();

Package Sidebar

Install

npm i unimapperjs

Weekly Downloads

0

Version

1.1.10

License

MIT

Unpacked Size

110 kB

Total Files

23

Last publish

Collaborators

  • hookyns