All Projects → maritz → Nohm

maritz / Nohm

Licence: mit
node.js object relations mapper (orm) for redis

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects

Projects that are alternatives of or similar to Nohm

Nymph
Data objects for JavaScript and PHP.
Stars: ✭ 97 (-79%)
Mutual labels:  orm, database, pubsub
Walrus
Lightweight Python utilities for working with Redis
Stars: ✭ 846 (+83.12%)
Mutual labels:  orm, database, redis
Prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB)
Stars: ✭ 16,851 (+3547.4%)
Mutual labels:  orm, database
Awesome Cheatsheets
👩‍💻👨‍💻 Awesome cheatsheets for popular programming languages, frameworks and development tools. They include everything you should know in one single file.
Stars: ✭ 26,007 (+5529.22%)
Mutual labels:  database, redis
Summer
这是一个支持分布式和集群的java游戏服务器框架,可用于开发棋牌、回合制等游戏。基于netty实现高性能通讯,支持tcp、http、websocket等协议。支持消息加解密、攻击拦截、黑白名单机制。封装了redis缓存、mysql数据库的连接与使用。轻量级,便于上手。
Stars: ✭ 336 (-27.27%)
Mutual labels:  orm, redis
Rel
💎 Modern Database Access Layer for Golang - Testable, Extendable and Crafted Into a Clean and Elegant API
Stars: ✭ 317 (-31.39%)
Mutual labels:  orm, database
Crecto
Database wrapper and ORM for Crystal, inspired by Ecto
Stars: ✭ 325 (-29.65%)
Mutual labels:  orm, database
Reladomo
Reladomo is an enterprise grade object-relational mapping framework for Java.
Stars: ✭ 336 (-27.27%)
Mutual labels:  orm, database
Think Orm
Think ORM——the PHP Database&ORM Framework
Stars: ✭ 303 (-34.42%)
Mutual labels:  orm, database
Gnorm
A database-first code generator for any language
Stars: ✭ 415 (-10.17%)
Mutual labels:  orm, database
Sqlboiler
Generate a Go ORM tailored to your database schema.
Stars: ✭ 4,497 (+873.38%)
Mutual labels:  orm, database
Framework Learning
计算机学习资料(Java , Jvm , Linux , Mysql , Netty , Redis , Netty , Spring , SpringBoot , Mybatis , Rabbitmq ,计算机网络 , 数据结构与算法 , 设计模式 )Github网页阅读:https://guang19.github.io/framework-learning , Gitee网页版阅读: https://qsjzwithguang19forever.gitee.io/framework-learning
Stars: ✭ 416 (-9.96%)
Mutual labels:  orm, redis
Architect
A set of tools which enhances ORMs written in Python with more features
Stars: ✭ 320 (-30.74%)
Mutual labels:  orm, database
Bitnami Docker Redis
Bitnami Redis Docker Image
Stars: ✭ 317 (-31.39%)
Mutual labels:  database, redis
Freezer
A simple & fluent Android ORM, how can it be easier ? RxJava2 compatible
Stars: ✭ 326 (-29.44%)
Mutual labels:  orm, database
Gokv
Simple key-value store abstraction and implementations for Go (Redis, Consul, etcd, bbolt, BadgerDB, LevelDB, Memcached, DynamoDB, S3, PostgreSQL, MongoDB, CockroachDB and many more)
Stars: ✭ 314 (-32.03%)
Mutual labels:  database, redis
Redis Ui
📡 P3X Redis UI is a very functional handy database GUI and works in your pocket on the responsive web or as a desktop app
Stars: ✭ 334 (-27.71%)
Mutual labels:  database, redis
Android Orma
An ORM for Android with type-safety and painless smart migrations
Stars: ✭ 442 (-4.33%)
Mutual labels:  orm, database
Graphik
Graphik is a Backend as a Service implemented as an identity-aware document & graph database with support for gRPC and graphQL
Stars: ✭ 277 (-40.04%)
Mutual labels:  database, pubsub
Node Orm2
Object Relational Mapping
Stars: ✭ 3,063 (+562.99%)
Mutual labels:  orm, database

Nohm

Build Status Dependency Status Known Vulnerabilities (Snyk) Coverage Status

Description

Nohm is an object relational mapper (ORM) written for node.js and redis written in Typescript.

Features

  • Standard ORM features (validate, store, search, sort, link/unlink, delete)
  • Share validations with browser.
    Allows using the same code for client validations that is used for backend. Includes filtering which validations are shared.
  • Subscribe to orm events (save, delete, link, unlink)
    With this you can do things like socket connections to get live updates from stored models.
    Since it uses redis PUBSUB you can scale your node app and clients can connect to separate node app instances but will still get the same live updates.
  • Typescript typings
    nohm is written in Typescript and thus provides first-class typings for most things, including the option to type your model properties. This means if you use Typescript you don't have to remember every single property name of each model anymore, your IDE can tell you.
  • Dynamic relations
    This is a double-edged sword. Usually ORMs describe relations statically and you have to do database changes before you can add new relations.
    In nohm all relations are defined and used at run-time, since there are no schemas stored in the database.

Requirements

  • redis >= 2.4

Documentation

v2 documentation

API docs

v1 documentation

v1 to v2 migration guide

Example

The examples/rest-user-server is running as a demo on https://nohm-example.maritz.space. It showcases most features on a basic level, including the shared validation and PubSub.

Example ES6 code (click to expand)
import { Nohm, NohmModel, ValidationError } from 'nohm';
// or if your environment does not support module import
// const NohmModule = require('nohm'); // access NohmModule.Nohm, NohmModule.NohmModel and NohmModule.ValidationError

// This is the parent object where you set redis connection, create your models and some other configuration stuff
const nohm = Nohm;

nohm.setPrefix('example'); // This prefixes all redis keys. By default the prefix is "nohm", you probably want to change it to your applications name or something similar

// This is a class that you can extend to create nohm models. Not needed when using nohm.model()
const Model = NohmModel;

const existingCountries = ['Narnia', 'Gondor', 'Tatooine'];

// Using ES6 classes here, but you could also use the old nohm.model definition
class UserModel extends Model {
  getCountryFlag() {
    return `http://example.com/flag_${this.property('country')}.png`;
  }
}
// Define the required static properties
UserModel.modelName = 'User';
UserModel.definitions = {
  email: {
    type: 'string',
    unique: true,
    validations: ['email'],
  },
  country: {
    type: 'string',
    defaultValue: 'Narnia',
    index: true,
    validations: [
      // the function name will be part of the validation error messages, so for this it would be "custom_checkCountryExists"
      async function checkCountryExists(value) {
        // needs to return a promise that resolves to a bool - async functions take care of the promise part
        return existingCountries.includes(value);
      },
      {
        name: 'length',
        options: { min: 3 },
      },
    ],
  },
  visits: {
    type: function incrVisitsBy(value, key, old) {
      // arguments are always string here since they come from redis.
      // in behaviors (type functions) you are responsible for making sure they return in the type you want them to be.
      return parseInt(old, 10) + parseInt(value, 10);
    },
    defaultValue: 0,
    index: true,
  },
};

// register our model in nohm and returns the resulting Class, do not use the UserModel directly!
const UserModelClass = nohm.register(UserModel);

const redis = require('redis').createClient();
// wait for redis to connect, otherwise we might try to write to a non-existent redis server
redis.on('connect', async () => {
  nohm.setClient(redis);

  // factory returns a promise, resolving to a fresh instance (or a loaded one if id is provided, see below)
  const user = await nohm.factory('User');

  // set some properties
  user.property({
    email: '[email protected]',
    country: 'Gondor',
    visits: 1,
  });

  try {
    await user.save();
  } catch (err) {
    if (err instanceof ValidationError) {
      // validation failed
      for (const key in err.errors) {
        const failures = err.errors[key].join(`', '`);
        console.log(
          `Validation of property '${key}' failed in these validators: '${failures}'.`,
        );

        // in a real app you'd probably do something with the validation errors (like make an object for the client)
        // and then return or rethrow some other error
      }
    }
    // rethrow because we didn't recover from the error.
    throw err;
  }
  console.log(`Saved user with id ${user.id}`);

  const id = user.id;

  // somewhere else we could then load the user again
  const loadedUser = await UserModelClass.load(id); // this will throw an error if the user cannot be found

  // alternatively you can use nohm.factory('User', id)

  console.log(`User loaded. His properties are %j`, loadedUser.allProperties());
  const newVisits = loadedUser.property('visits', 20);
  console.log(`User visits set to ${newVisits}.`); // Spoiler: it's 21

  // or find users by country
  const gondorians = await UserModelClass.findAndLoad({
    country: 'Gondor',
  });
  console.log(
    `Here are all users from Gondor: %j`,
    gondorians.map((u) => u.property('email')),
  );

  await loadedUser.remove();
  console.log(`User deleted from database.`);
});
Example Typescript code (click to expand)
import { Nohm, NohmModel, TTypedDefinitions } from 'nohm';

// We're gonna assume the basics are clear and the connection is set up etc. - look at the ES6 example otherwise.
// This example highlights some of the typing capabilities in nohm.

interface IUserProperties {
  email: string;
  visits: number;
}

class UserModel extends NohmModel<IUserProperties> {
  public static modelName = 'User';

  protected static definitions: TTypedDefinitions<IUserProperties> = {
    // because of the TTypedDefinitions we can only define properties keys here that match our interface keys
    // the structure of the definitions is also typed
    email: {
      type: 'string', // the type value is currently not checked. If you put a wrong type here, no compile error will appear.
      unique: true,
      validations: ['email'],
    },
    visits: {
      defaultValue: 0,
      index: true,
      type: function incrVisitsBy(value, _key, old): number {
        return old + value; // TS Error: arguments are all strings, not assignable to number
      },
    },
  };

  public getVisitsAsString(): string {
    return this.property('visits'); // TS Error: visits is number and thus not assignable to string
  }

  public static async loadTyped(id: string): Promise<UserModel> {
    // see main() below for explanation
    return userModelStatic.load<UserModel>(id);
  }
}

const userModelStatic = nohm.register(UserModel);

async function main() {
  // currently you still have to pass the generic if you want typing for class methods
  const user = await userModelStatic.load<UserModel>('some id');
  // you can use the above defined loadTyped method to work around that.

  const props = user.allProperties();
  props.email; // string
  props.id; // any
  props.visits; // number
  props.foo; // TS Error: Property foo does not exist
  user.getVisitsAsString(); // string
}

main();

More detailed examples

Do you have code that should/could be listed here? Message me!

Add it to your project

npm install --save nohm

Debug

Nohm uses the debug module under the namespace "nohm". To see detailed debug logging set the environment variable DEBUG accordingly:

DEBUG="nohm:*" node yourApp.js

Available submodule debug namespaces are nohm:index, nohm:model, nohm:middleware, nohm:pubSub and nohm:idGenerator.

Developing nohm

If you want to make changes to nohm, you can fork or clone it. Then install the dependencies:

npm install

and run the development scripts (compile & watch & tests):

npm run dev

When submitting PRs, please make sure that you run the linter and that everything still builds fine. The easiest way to do that is to run the prepublishOnly script:

npm run prepublishOnly

Running tests

Build the javascript files:

npm run build

Then run the tests:

npm run test
# or
npm run test:watch

This requires a running redis server. (you can configure host/port with the command line arguments --redis-host 1.1.1.1 --redis-port 1234)

WARNING: The tests also create a lot of temporary keys in your database that look something like this:

nohmtestsuniques:something:something

After the tests have run all keys that match the pattern nohmtests* are deleted!

You can change the prefix ("nohmtests") part doing something like

node test/tests.js --nohm-prefix YourNewPrefix

Now the keys will look like this:

YourNewPrefixuniques:something:something
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].