0.5.133 • Published 3 years ago

@nodeswork/sbase v0.5.133

Weekly downloads
94
License
Apache-2.0
Repository
github
Last release
3 years ago

@nodeswork/sbase

SBase is a user-friendly Typescript wrapper on top of Mongoose and Koa2.

Key Features

1. Model Definition

It maps the db model to a Typescript class, the db schema to the class properties, and the methods to the class methods so that all the model configuration code is well organized in a centralized place and the models have distinct property types. It also supports model inheritance and mixin so that common logic can be abstracted out. It has some useful pre-defined model extensions, such as auto-generate timestamp, KOA2 middlewares, etc.

Below listed how SBase mapping the Mongoose definitions.

Mongoose FeaturesSBase Implementation
Collection configurationClass deocorator @Config
SchemaProperties combines with decorators like @Field, @Enum, @Unique, etc.
MethodsClass methods
Static methodsClass static methods
Model registrationPre-defined class static methoed Model.$register() & Model.$registerNModel().

2. Web Context Validation

It provides numerious built-in validators and sanitizers that can validate any fields under ctx.request, usually query param and body.

import { params, isByteLength, isEmail, ltrim, rtrim } from '@nodeswork/sbase/koa';

router
  .post('/user', params({
    '!body.email': isEmail,
    '!body.description': [ isByteLength(6, 1400), ltrim(), rtrim() ],
  }))
  // ... other middleware chain
;

3. Model-direct Koa Middlewares

It provides customizable Koa middlewares which are generated from model and allows to chain them with other middlewares.

router

  .post('/user', models.User.createMiddleware())

  .post('/user/:userId', models.User.updateMiddleware({
    field: 'userId',
  }))
;

Installation

$ npm install @nodeswork/sbase

Quick Start

Define a Model

Models are defined by extending NModel or Model class.

// models/users.ts

import { Config, Field, NModel } from '@nodeswork/sbase/mongoose';

// Pass-in model configuration
@Config({
  collections: 'users',
})
export class User extends NModel {

  @Unique() email: string;

  @Field() firstName: string;

  @Field() lastName: string

  // get property maps to virtual get function
  get fullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }

  // set property maps to virtual set function
  set fullName(fullName: string) {
    let [firstName, lastName] = fullName.split(' ');
    this.firstName = firstName;
    this.lastName = lastName;
  }

  // method maps to document method
  isEmailFromDomain(domain: string): boolean {
    return this.email.endsWith(`@${domain}`);
  }

  // static method maps to model method
  static async findByName(firstName: string, lastName: string): Promise<User> {
    let self = this.cast<UserType>();
    return self.findOne({ firstName, lastName });
  }
}

Register the Model

Register models and expose model types.

// models/index.ts

import * as users from './users';

export const User = users.User.$registerNModel<User, typeof User>();
export type  User = users.User;

Use the Model

Use the model with normal Mongoose interfaces.

import { User } from './models';

(async function usage() {

  // Create a new user.
  const user = await User.create({
    email: 'test@abc.com',
    firstName: 'Alice',
    lastName: 'North',
  });

  // Getter
  user.fullName === 'Alice North';  // returns true

  // Modify the user's first name.
  user.firstName = 'Bob';
  // or
  user.fullName = 'Bob North';

  // Save the modified model.
  await user.save();

  // Find by user attributes.
  const user = await User.findOne({
    email: 'test@abc.com',
  });

  // Instance method
  user.isEmailFromDomain('abc.com');  // returns true

  // Class method
  await User.findByName('Bob', 'North');  // returns the instance

})();

Advanced Features

Field Definition

Field

The common decorator is @Field, which accepts the schema and passes down to the Mongoose schema object.

For example, to define a field phone number and specifies the regex validation:

import { Config, Field, NModel } from '@nodeswork/sbase/mongoose';

@Config({})
export class User extends NModel {

  @Field({
    type: String,
    validate: {
      validator: function(v) {
        return /\d{3}-\d{3}-\d{4}/.test(v);
      },
      message: props => `${props.value} is not a valid phone number!`,
    },
    required: [true, 'User phone number required'],
  }) phone: string;
}

There are many other decorators to make definitions easiler.

ArrayField

@ArrayField allows to define an array of any other type.

import { ArrayField, NModel } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @ArrayField(String) phones: string[];
}

DBRef

@DBRef allows to reference to other models.

import { DBRef, NModel } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
}

export class Post extends NModel {
  @DBRef('User') author: mongoose.Types.ObjectId | models.User;
}

DBRefArray

@DBRefArray allows to reference to other model in a list.

import { DBRefArray, NModel } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
}

export class Post extends NModel {
  @DBRef('User') authors: Array<mongoose.Types.ObjectId | models.User>;
}

Virtual Reference

@Virtual allows to reference to other model in a single object or a list.

import { DBRefArray, NModel } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @Virtual({ ref: 'Post', localField: '_id', foreignField: 'author' })
  posts: models.Post[];
}

export class Post extends NModel {
  @DBRef('User') author: mongoose.Types.ObjectId | models.User;
}

Default

@Default provides default value for the field.

import { Default, NModel } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @Default(false) active: boolean;
}

Enum

import { Enum, NModel } from '@nodeswork/sbase/mongoose';

export enum UserAuthMethod {
  EMAIL = 'EMAIL',
  FACEBOOK = 'FACEBOOK',
  GOOGLE = 'GOOGLE',
}

export class User extends NModel {
  @Enum(UserAuthMethod) authMethod: UserAuthMethod;
}

IndexField

@IndexField builds index on the target field.

import { NModel, Required } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @IndexField() email: string;
}

Required

@Required marks the target as a required field.

import { NModel, Required } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @Required() email: string;
}

Unique

@Unique marks the target as a unique field.

import { NModel, Unique } from '@nodeswork/sbase/mongoose';

export class User extends NModel {
  @Unique() email: string;
}

Nested Reference

Nested schema can be defined separately and shared or referenced by other models.

import { Config, Field, Model, NModel } from '@nodeswork/sbase/mongoose';

@Config({
  _id: false,
})
export class UserProfile extends Model {
  @Field() phone: string;
  @Field() address: string;
}

export class User extends NModel {
  @Field() profile: UserProfile;
}

Indexes

Indexes can be defined either through @Unique decorator on properties or @Index decorator on model class.

import { Config, Field, Index, NModel, Unique } from '@nodeswork/sbase/mongoose';

@Config({
  collections: 'users',
})
@Index({
  fields: {
    firstName: 1,
    lastName: 1,
  },
  options: {
    unique: false,
  },
})
export class User extends NModel {
  @Unique() email: string;

  @Field() firstName: string;
  @Field() lastName: string;
}

Hooks

@Pre, @Post, @Pres, and @Posts allow to add hooks to the model.

import { Config, Default, Pre, Pres, NModel } from '@nodeswork/sbase/mongoose';

@Config({
  collections: 'users',
})
@Pre({
  name: 'save',
  fn: () => this.saveVersion++,
})
@Pres(['save', 'update'], {
  fn: () => this.allUpdateVersion++,
})
export class User extends NModel {
  @Default(0) saveVersion: number;
  @Default(0) allUpdateVersion: number;
}

Plugins

@Plugin allows to extend the model with normal Mongoose plugins.

import { Config, NModel, Plugin } from '@nodeswork/sbase/mongoose';

@Config({
  collections: 'users',
})
@Plugin({
  fn: () => {}, // plugin logic
})
export class User extends NModel {
}

Data Levels

Data fields can be grouped by pre-defined levels, and the level can be used as a short-cut for projecting, while either retrieving the model instances or calling instance toJSON() method.

For example, suppose there are three levels of data for User model: 1) Basic info; 2) Detail info; 3) Credentials. Below is the model definitions and sample usages.

import { Config, Field, Level, NModel } from '@nodeswork/sbase/mongoose';

export enum UserDataLevel {
  BASIC = 'BASIC',
  DETAIL = 'DETAIL',
  CREDENTIAL = 'CREDENTIAL',
}

@Config({
  collections: 'users',
  dataLevel: {
    levels: UserDataLevel,
    default: UserDataLevel.BASIC,
  },
})
export class User extends NModel {

  @Field() email: string;

  @Level(UserDataLevel.BASIC) username: string;

  @Level(UserDataLevel.DETAIL) bio: string;

  @Level(UserDataLevel.CREDENTIAL) password:   string
}
await User.find({} /* query */, null /* field projection */, {
  level: UserDataLevel.BASIC,
});  // returns email and username

await User.find({} /* query */, null /* field projection */, {
  level: UserDataLevel.DETAIL,
});  // returns email, username, and bio

const user = await User.find({} /* query */, null /* field projection */, {
  level: UserDataLevel.CREDENTIAL,
});  // returns email, username, bio, and password

const userJson = user.toJSON({
  level: UserDataLevel.DETAIL,
}); // returns email, username, and bio.

Koa Middlewares

Api Level

0.5.133

3 years ago

0.5.132

3 years ago

0.5.130

4 years ago

0.5.131

4 years ago

0.5.129

4 years ago

0.5.128

4 years ago

0.5.127

4 years ago

0.5.126

4 years ago

0.5.125

4 years ago

0.5.124

4 years ago

0.5.123

4 years ago

0.5.122

4 years ago

0.5.121

4 years ago

0.5.120

4 years ago

0.5.119

4 years ago

0.5.118

4 years ago

0.5.117

4 years ago

0.5.116

4 years ago

0.5.115

4 years ago

0.5.114

4 years ago

0.5.113

4 years ago

0.5.112

4 years ago

0.5.111

4 years ago

0.5.108

4 years ago

0.5.107

4 years ago

0.5.106

4 years ago

0.5.105

4 years ago

0.5.104

4 years ago

0.5.102

4 years ago

0.5.101

4 years ago

0.5.99

4 years ago

0.5.100

4 years ago

0.5.98

4 years ago

0.5.96

4 years ago

0.5.97

4 years ago

0.5.95

4 years ago

0.5.92

4 years ago

0.5.93

4 years ago

0.5.91

4 years ago

0.5.90

4 years ago

0.5.88

4 years ago

0.5.85

4 years ago

0.5.84

4 years ago

0.5.83

4 years ago

0.5.82

4 years ago

0.5.81

4 years ago

0.5.80

4 years ago

0.5.79

4 years ago

0.5.78

4 years ago

0.5.76

4 years ago

0.5.77

4 years ago

0.5.75

4 years ago

0.5.74

4 years ago

0.5.73

4 years ago

0.5.72

4 years ago

0.5.70

4 years ago

0.5.71

4 years ago

0.5.69

4 years ago

0.5.68

4 years ago

0.5.66

4 years ago

0.5.67

4 years ago

0.5.65

4 years ago

0.5.63

4 years ago

0.5.61

4 years ago

0.5.62

4 years ago

0.5.60

4 years ago

0.5.59

4 years ago

0.5.58

4 years ago

0.5.57

4 years ago

0.5.56

4 years ago

0.5.55

4 years ago

0.5.54

4 years ago

0.5.52

4 years ago

0.5.51

4 years ago

0.5.50

4 years ago

0.5.49

4 years ago

0.5.48

4 years ago

0.5.47

4 years ago

0.5.46

4 years ago

0.5.44

4 years ago

0.5.45

4 years ago

0.5.43

4 years ago

0.5.42

4 years ago

0.5.40

4 years ago

0.5.39

4 years ago

0.5.38

4 years ago

0.5.37

4 years ago

0.5.36

4 years ago

0.5.35

4 years ago

0.5.34

4 years ago

0.5.33

4 years ago

0.5.32

5 years ago

0.5.31

5 years ago

0.5.30

5 years ago

0.5.29

5 years ago

0.5.28

5 years ago

0.5.27

5 years ago

0.5.26

5 years ago

0.5.25

5 years ago

0.5.24

5 years ago

0.5.23

5 years ago

0.5.22

5 years ago

0.5.21

5 years ago

0.5.20

5 years ago

0.5.19

5 years ago

0.5.18

5 years ago

0.5.17

5 years ago

0.5.16

5 years ago

0.5.15

5 years ago

0.5.14

5 years ago

0.5.13

5 years ago

0.5.12

5 years ago

0.5.11

5 years ago

0.5.10

5 years ago

0.5.9

5 years ago

0.5.8

5 years ago

0.5.7

5 years ago

0.5.6

5 years ago

0.5.5

6 years ago

0.5.4

6 years ago

0.5.3

6 years ago

0.5.2

6 years ago

0.5.1

6 years ago

0.5.0

6 years ago

0.3.8

6 years ago

0.3.7

6 years ago

0.3.6

6 years ago

0.3.5

6 years ago

0.3.4

6 years ago

0.3.3

6 years ago

0.3.2

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.24

6 years ago

0.2.23

6 years ago

0.2.22

6 years ago

0.2.21

6 years ago

0.2.20

6 years ago

0.2.19

6 years ago

0.2.18

6 years ago

0.2.17

6 years ago

0.2.16

6 years ago

0.2.15

6 years ago

0.2.12

6 years ago

0.2.11

6 years ago

0.2.10

6 years ago

0.2.9

6 years ago

0.2.7

6 years ago

0.2.6

7 years ago

0.2.5

7 years ago

0.2.4

7 years ago

0.2.3

7 years ago

0.2.2

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.46

7 years ago

0.1.45

7 years ago

0.1.44

7 years ago

0.1.43

7 years ago

0.1.42

7 years ago

0.1.41

7 years ago

0.1.40

7 years ago

0.1.39

7 years ago

0.1.38

7 years ago

0.1.37

7 years ago

0.1.35

7 years ago

0.1.32

7 years ago

0.1.31

7 years ago

0.1.30

7 years ago

0.1.29

7 years ago

0.1.28

7 years ago

0.1.27

7 years ago

0.1.26

7 years ago

0.1.25

7 years ago

0.1.24

7 years ago

0.1.23

7 years ago

0.1.22

7 years ago

0.1.21

7 years ago

0.1.20

7 years ago

0.1.19

7 years ago

0.1.18

7 years ago

0.1.17

7 years ago

0.1.16

7 years ago

0.1.15

7 years ago

0.1.14

7 years ago

0.1.12

7 years ago

0.1.11

7 years ago

0.1.9

7 years ago

0.1.8

7 years ago

0.1.7

7 years ago

0.1.6

7 years ago

0.1.5

7 years ago

0.1.4

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago

0.0.6

7 years ago

0.0.5

7 years ago

0.0.4

7 years ago

0.0.3

7 years ago

0.0.2

7 years ago

0.0.1

7 years ago