GithubHelp home page GithubHelp logo

szokodiakos / typegoose Goto Github PK

View Code? Open in Web Editor NEW
1.2K 23.0 138.0 1.17 MB

Typegoose - Define Mongoose models using TypeScript classes.

License: MIT License

TypeScript 100.00%
mongoose typescript ts mongodb odm

typegoose's Introduction

This Repository got moved

Please use hasezoey's fork to be up-to-date Please dont create new issues & pull request anymore, thanks









Typegoose

Build Status Coverage Status npm

Define Mongoose models using TypeScript classes.

Basic usage

import { prop, Typegoose, ModelType, InstanceType } from 'typegoose';
import * as mongoose from 'mongoose';

mongoose.connect('mongodb://localhost:27017/test');

class User extends Typegoose {
  @prop()
  name?: string;
}

const UserModel = new User().getModelForClass(User);

// UserModel is a regular Mongoose Model with correct types
(async () => {
  const u = await UserModel.create({ name: 'JohnDoe' });
  const user = await UserModel.findOne();

  // prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
  console.log(user);
})();

Motivation

A common problem when using Mongoose with TypeScript is that you have to define both the Mongoose model and the TypeScript interface. If the model changes, you also have to keep the TypeScript interface file in sync or the TypeScript interface would not represent the real data structure of the model.

Typegoose aims to solve this problem by defining only a TypeScript interface (class) which need to be enhanced with special Typegoose decorators.

Under the hood it uses the reflect-metadata API to retrieve the types of the properties, so redundancy can be significantly reduced.

Instead of:

interface Car {
  model?: string;
}

interface Job {
  title?: string;
  position?: string;
}

interface User {
  name?: string;
  age: number;
  job?: Job;
  car: Car | string;
}

mongoose.model('User', {
  name: String,
  age: { type: Number, required: true },
  job: {
    title: String;
    position: String;
  },
  car: { type: Schema.Types.ObjectId, ref: 'Car' }
});

mongoose.model('Car', {
  model: string,
});

You can just:

class Job {
  @prop()
  title?: string;

  @prop()
  position?: string;
}

class Car extends Typegoose {
  @prop()
  model?: string;
}

class User extends Typegoose {
  @prop()
  name?: string;

  @prop({ required: true })
  age!: number;

  @prop()
  job?: Job;

  @prop({ ref: Car })
  car?: Ref<Car>;
}

Please note that sub documents do not have to extend Typegoose. You can still give them default value in prop decorator, but you can't create static or instance methods on them.

Requirements

  • TypeScript 3.2+
  • Node 8+
  • mongoose 5+
  • emitDecoratorMetadata and experimentalDecorators must be enabled in tsconfig.json
  • reflect-metadata must be installed

Install

npm install typegoose -S

You also need to install mongoose and reflect-metadata, in versions < 5.0, these packages were listed as dependencies in package.json, starting with version 5.0 these packages are listed as peer dependencies.

npm install mongoose reflect-metadata -S

Testing

npm test

Versioning

Major.Minor.Fix (or how npm expresses it Major.Minor.Patch)

  • 0.0.x is for minor fixes, like hot-fixes
  • 0.x.0 is for Minor things like adding features, that are non-breaking (or at least should not be breaking anything)
  • x.0.0 is for Major things like adding features that are breaking or refactoring which is a breaking change
  • 0.0.0-x is for a Pre-Release, that are not yet ready to be published

API Documentation

Typegoose class

This is the class which your schema defining classes must extend.

Methods:

getModelForClass<T>(t: T, options?: GetModelForClassOptions)

This method returns the corresponding Mongoose Model for the class (T). If no Mongoose model exists for this class yet, one will be created automatically (by calling the method setModelForClass).

setModelForClass<T>(t: T, options?: GetModelForClassOptions)

This method assembles the Mongoose Schema from the decorated schema defining class, creates the Mongoose Model and returns it. For typing reasons, the schema defining class must be passed down to it.

Hint: If a Mongoose Model already exists for this class, it will be overwritten.

The GetModelForClassOptions provides multiple optional configurations:

  • existingMongoose: mongoose: An existing Mongoose instance can also be passed down. If given, Typegoose uses this Mongoose instance's model methods.
  • schemaOptions: mongoose.SchemaOptions: Additional schema options can be passed down to the schema-to-be-created.
  • existingConnection: mongoose.Connection: An existing Mongoose connection can also be passed down. If given, Typegoose uses this Mongoose instance's model methods.

Property decorators

Typegoose comes with TypeScript decorators, which responsibility is to connect the Mongoose schema behind the TypeScript class.

prop(options)

The prop decorator adds the target class property to the Mongoose schema as a property. Typegoose checks the decorated property's type and sets the schema property accordingly. If another Typegoose extending class is given as the type, Typegoose will recognize this property as a sub document.

The options object accepts multiple config properties:

  • required: Just like the Mongoose required it accepts a handful of parameters. Please note that it's the developer's responsibility to make sure that if required is set to false then the class property should be optional.

    Note: for coding style (and type completion) you should use ! when it is marked as required

    // this is now required in the schema
    @prop({ required: true })
    firstName!: string;
    
    // by default, a property is not required
    @prop()
    lastName?: string; // using the ? optional property
  • index: Tells Mongoose whether to define an index for the property.

    @prop({ index: true })
    indexedField?: string;
  • unique: Just like the Mongoose unique, tells Mongoose to ensure a unique index is created for this path.

    // this field is now unique across the collection
    @prop({ unique: true })
    uniqueId?: string;
  • enum: The enum option accepts a string array. The class property which gets this decorator should have an enum-like type which values are from the provided string array. The way how the enum is created is delegated to the developer, Typegoose needs a string array which hold the enum values, and a TypeScript type which tells the possible values of the enum. However, if you use TS 2.4+, you can use string enum as well.

    enum Gender {
      MALE = 'male',
      FEMALE = 'female',
    }
    
    @prop({ enum: Gender })
    gender?: Gender;
  • lowercase: for strings only; whether to always call .toLowerCase() on the value.

    @prop({ lowercase: true })
    nickName?: string;
  • uppercase: for strings only; whether to always call .toUpperCase() on the value.

    @prop({ uppercase: true })
    nickName?: string;
  • trim: for strings only; whether to always call .trim() on the value.

    @prop({ trim: true })
    nickName?: string;
  • default: The provided value will be the default for that Mongoose property.

    @prop({ default: 'Nick' })
    nickName?: string;
  • _id: When false, no _id is added to the subdocument

    class Car extends Typegoose {}
    
    @prop({ _id: false })
    car?: Car;
  • ref: By adding the ref option with another Typegoose class as value, a Mongoose reference property will be created. The type of the property on the Typegoose extending class should be Ref<T> (see Types section).

    class Car extends Typegoose {}
    
    @prop({ ref: Car })
    car?: Ref<Car>;
  • refPath: Is the same as ref, only that it looks at the path specified, and this path decides which model to use

    class Car extends Typegoose {}
    class Shop extends Typegoose {}
    
    // in another class
    class Another extends Typegoose {
      @prop({ required: true, enum: 'Car' | 'Shop' })
      which!: string;
    
      @prop({ refPath: 'which' })
      kind?: Ref<Car | Shop>;
    }
  • min / max (numeric validators): Same as Mongoose numberic validators.

    @prop({ min: 10, max: 21 })
    age?: number;
  • minlength / maxlength / match (string validators): Same as Mongoose string validators.

    @prop({ minlength: 5, maxlength: 10, match: /[0-9a-f]*/ })
    favouriteHexNumber?: string;
  • validate (custom validators): You can define your own validator function/regex using this. The function has to return a boolean or a Promise (async validation).

    // you have to get your own `isEmail` function, this is a placeholder
    
    @prop({ validate: (value) => isEmail(value)})
    email?: string;
    
    // or
    
    @prop({ validate: (value) => { return new Promise(res => { res(isEmail(value)) }) })
    email?: string;
    
    // or
    
    @prop({ validate: {
        validator: val => isEmail(val),
        message: `{VALUE} is not a valid email`
    }})
    email?: string;
    
    // or
    
    @prop({ validate: /\S+@\S+\.\S+/ })
    email?: string;
    
    // you can also use multiple validators in an array.
    
    @prop({ validate:
      [
        {
            validator: val => isEmail(val),
            message: `{VALUE} is not a valid email`
        },
        {
            validator: val => isBlacklisted(val),
            message: `{VALUE} is blacklisted`
        }
      ]
    })
    email?: string;
  • alias (alias): Same as Mongoose Alias, only difference is the extra property for type completion

    class Dummy extends Typegoose {
      @prop({ alias: "helloWorld" })
      public hello: string; // will be included in the DB
      public helloWorld: string; // will NOT be included in the DB, just for type completion (gets passed as hello in the DB)
    }

Mongoose gives developers the option to create virtual properties. This means that actual database read/write will not occur these are just 'calculated properties'. A virtual property can have a setter and a getter. TypeScript also has a similar feature which Typegoose uses for virtual property definitions (using the prop decorator).

@prop()
firstName?: string;

@prop()
lastName?: string;

@prop() // this will create a virtual property called 'fullName'
get fullName() {
  return `${this.firstName} ${this.lastName}`;
}
set fullName(full) {
  const [firstName, lastName] = full.split(' ');
  this.firstName = firstName;
  this.lastName = lastName;
}

TODO: add documentation for virtual population

arrayProp(options)

The arrayProp is a prop decorator which makes it possible to create array schema properties.

The options object accepts required, enum and default, just like the prop decorator. In addition to these the following properties exactly one should be given:

  • items: This will tell Typegoose that this is an array which consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it's extending the Typegoose class).

    @arrayProp({ items: String })
    languages?: string[];

Note that unfortunately the reflect-metadata API does not let us determine the type of the array, it only returns Array when the type of the property is queried. This is why redundancy is required here.

  • itemsRef: In mutual exclusion with items, this tells Typegoose that instead of a subdocument array, this is an array with references in it. On the Mongoose side this means that an array of Object IDs will be stored under this property. Just like with ref in the prop decorator, the type of this property should be Ref<T>[].

    class Car extends Typegoose {}
    
    // in another class
    @arrayProp({ itemsRef: Car })
    previousCars?: Ref<Car>[];
  • itemsRefPath(IRP): Is the same as itemsRef only that it looks at the specified path of the class which specifies which model to use

    class Car extends Typegoose {}
    class Shop extends Typegoose {}
    
    // in another class
    class Another extends Typegoose {
      @prop({ required: true, enum: 'Car' | 'Shop' })
      which!: string;
    
      @arrayProp({ itemsRefPath: 'which' })
      items?: Ref<Car | Shop>[];
    }

mapProp(options)

The mapProp is a prop decorator which makes it possible to create map schema properties.

The options object accepts enum and default, just like prop decorator. In addition to these the following properties are accepted:

  • of : This will tell Typegoose that the Map value consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it's extending the Typegoose class).

    class Car extends Typegoose {
      @mapProp({ of: Car })
      public keys?: Map<string, Car>;
    }
  • mapDefault : This will set the default value for the map.

    enum ProjectState {
        WORKING = 'working',
        BROKEN = 'broken',
        MAINTAINANCE = 'maintainance',
    }
    
    class Car extends Typegoose {
      @mapProp({ of: String, enum: ProjectState,mapDefault: { 'MainProject' : ProjectState.WORKING }})
      public projects?: Map<string, ProjectState>;
    }

Method decorators

In Mongoose we can attach two types of methods for our schemas: static (model) methods and instance methods. Both of them are supported by Typegoose.

staticMethod

Static Mongoose methods must be declared with static keyword on the Typegoose extending class. This will ensure, that these methods are callable on the Mongoose model (TypeScript won't throw development-time error for unexisting method on model object).

If we want to use another static method of the model (built-in or created by us) we have to override the this in the method using the type specifying of this for functions. If we don't do this, TypeScript will throw development-time error on missing methods.

@staticMethod
static findByAge(this: ModelType<User> & typeof User, age: number) {
  return this.findOne({ age });
}

Note that the & typeof T is only mandatory if we want to use the developer defined static methods inside this static method. If not then the ModelType<T> is sufficient, which will be explained in the Types section.

instanceMethod

Instance methods are on the Mongoose document instances, thus they must be defined as non-static methods. Again if we want to call other instance methods the type of this must be redefined to InstanceType<T> (see Types).

@instanceMethod
incrementAge(this: InstanceType<User>) {
  const age = this.age || 1;
  this.age = age + 1;
  return this.save();
}

Class decorators

Mongoose allows the developer to add pre and post hooks / middlewares to the schema. With this it is possible to add document transformations and observations before or after validation, save and more.

Typegoose provides this functionality through TypeScript's class decorators.

pre

We can simply attach a @pre decorator to the Typegoose class and define the hook function like you normally would in Mongoose. (Method supports REGEXP)

@pre<Car>('save', function(next) { // or @pre(this: Car, 'save', ...
  if (this.model === 'Tesla') {
    this.isFast = true;
  }
  next();
})
class Car extends Typegoose {
  @prop({ required: true })
  model!: string;

  @prop()
  isFast?: boolean;
}

This will execute the pre-save hook each time a Car document is saved. Inside the pre-hook Mongoose binds the actual document to this.

Note that additional typing information is required either by passing the class itself as a type parameter <Car> or explicity telling TypeScript that this is a Car (this: Car). This will grant typing informations inside the hook function.

post

Same as pre, the post hook is also implemented as a class decorator. Usage is equivalent with the one Mongoose provides. (Method supports REGEXP)

@post<Car>('save', (car) => {
  if (car.topSpeedInKmH > 300) {
    console.log(car.model, 'is fast!');
  }
})
class Car extends Typegoose {
  @prop({ required: true })
  model!: string;

  @prop({ required: true })
  topSpeedInKmH!: number;
}

Of course this is not the document in a post hook (see Mongoose docs). Again typing information is required either by explicit parameter typing or by providing a template type.

plugin

Using the plugin decorator enables the developer to attach various Mongoose plugins to the schema. Just like the regular schema.plugin() call, the decorator accepts 1 or 2 parameters: the plugin itself, and an optional configuration object. Multiple plugin decorator can be used for a single Typegoose class.

If the plugin enhances the schema with additional properties or instance / static methods this typing information should be added manually to the Typegoose class as well.

import * as findOrCreate from 'mongoose-findorcreate';

@plugin(findOrCreate)
class User extends Typegoose {
  // this isn't the complete method signature, just an example
  static findOrCreate(condition: InstanceType<User>):
    Promise<{ doc: InstanceType<User>, created: boolean }>;
}

const UserModel = new User().getModelForClass(User);
UserModel.findOrCreate({ ... }).then(findOrCreateResult => {
  ...
});

index

The @index decorator can be used to define advanced index types and index options not available via the index option of the @prop property decorator, such as compound indices, GeoJSON index types, partial indices, expiring documents, etc. Any values supported by MongoDB's createIndex() are also valid for @index. For more info refer to interface IndexOptions

@index({ article: 1, user: 1 }, { unique: true })
@index({ location: '2dsphere' })
@index({ article: 1 }, { partialFilterExpression: { stars: { $gte: 4.5 } } })
export class Location extends Typegoose {
 @prop()
 article?: number;

 @prop()
 user?: number;

 @prop()
 stars?: number;

 @arrayProp({ items: Array })
 location?: [[Number]]
}

Types

Some additional types were added to make Typegoose more user friendly.

InstanceType

This is basically the logical 'and' of the T and the mongoose.Document, so that both the Mongoose instance properties/functions and the user defined properties/instance methods are available on the instance.

Note: TypeScript has its own InstanceType, you should import it from Typegoose

ModelType

This is the logical 'and' of mongoose.Model<InstanceType<T>> and T, so that the Mongoose model creates InstanceType<T> typed instances and all user defined static methods are available on the model.

Ref

For reference properties: Ref<T> - T if populated and ObjectID if unpopulated.

Improvements

  • Add frequently used (currently not present) features if needed
  • Create more tests (break down current huge one into multiple unit tests)
  • Add Tests for:
    • Hooks: add hook test for pre & post with error
    • test for the errors (if invalid arguments are given)
    • improve baseProp required handeling ()

Notes

  • mongoose is a peer-dependency, and a dev dependency to install it for dev purposes
  • Please dont add comments with +1 or something like that, use the Reactions
  • Typegoose cannot be used with classes of the same name, it will always return the first build class with that name
  • All Models in Typegoose are set to strict by default, and cant be changed!

typegoose's People

Contributors

19majkel94 avatar afgthecat avatar aljazerzen avatar ben305 avatar bitjson avatar cjanietz avatar daanrutten avatar gae123 avatar hasezoey avatar hpoul avatar jinxcifer avatar joseluisgarciaott avatar kaihaase-wd avatar khashayar avatar luxcium avatar marcelh-gh avatar megahertz avatar michallytek avatar mpsijm avatar mschnee avatar nodkz avatar pabloszx avatar richarddd avatar rjmunro avatar senci avatar simonb12 avatar szokodiakos avatar ukoester avatar vinczedani avatar vnenkpet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typegoose's Issues

.save() never returns

Hi. I'm trying to follow your example and having difficulties.
my project structure is:
app.ts
server.ts
models
-->user.model.ts
routes
-->users.ts

in app.ts I'm defining the connection to the database:

constructor() {
    this.mongoose = new mongoose.Mongoose();
    this.express = express();
    this.express.use("/users", usersRouter);
    this.mongoose.connect("mongodb:\/\/localhost:27017/example", (err) => {
        if (err) {
            console.log('[-] Error connecting  to database mongodb:\/\/localhost:27017/example" - ${err.message}');
            throw err;
        } else {
            console.log('[*] Connected to database mongodb:\/\/localhost:27017/example');
        }
    });
}

server.ts is only there to start the server itself:

let app = new App();
app.express.listen(3000, () => {
    console.log('[*] server started on port 3000');
});

I've created a user model with Typegoose and added an API call via express to add a user to the system:
routes/users.ts (where i call 'addUser' from the model):

private static readonly UserModel = new User().getModelForClass(User);
usersRouter.post("/register", (req, res, next) => {
    let newUser = new Models.UserModel({
        email: req.body.email,
        name: req.body.name,
        password: req.body.password,
        username: req.body.username,
    });
    UserModel.addUser(newUser, (err, user) => {
        if (err) {
            res.json({
                msg: "Failed regisgter user",
                success: false,
            });
        } else {
            res.json({
                msg: "registered user",
                success: true,
            });
        }
    });
}

models/user.model.ts

export class User extends Typegoose {
    @staticMethod
    public static async addUser(newUser: InstanceType<User>, callback: (err: any, product: InstanceType<User>) => void)) {
        await newUser.save(callback);
    }
    @prop({required: true})
    public email: string;
    @prop()
    public name?: string;
    @prop({required: true})
    public password: string;
    @prop({required: true})
    public username: string;
}

everything's going fine until the line newUser.save();
the callback never get called... I also don't see any traffic in mongo.

The project work on JavaScript, now I'm trying to move to TS. Is there something I missed during the transmission or is it an issue?

Populate across different DBs

I have two DB with a Mongoose instance for each one, which I use as an existingMongoose property when I create a new getModelForClass instance.
In a model associated to the first Mongoose instance I have a ref to a model in the second Mongoose instance.
And of course, when I try to populate the ref of the first model (Which refers to the second Mongoose instance) I get a MissingSchemaError: Schema hasn't been registered for model because it is not registered in the first Mongoose instance.
This is supported by Mongoose (http://mongoosejs.com/docs/populate.html#cross-db-populate) but Typegoose expects a fully Mongoose instance, not a connection created with createConnection to associate a model to it.

Is there any workaround for this??

(I'm open to implement this if you give me some guidance :) )

Stuck on findOne

I probably made a mistake implementing Typegoose somewhere (btw, awesome project!) but on the first User.findOne() my request is freezing.

This is my (simplified) model:

export class UserClass extends Typegoose {
  @prop()
  _id: string

  @prop({
    required: true
  })
  name: string

  @prop({
    required: true
  })
  email: string

  @prop({
    required: true
  })
  password: string
}

Exposed like this:

export const User = new UserClass().getModelForClass(UserClass)

And called like this:

import { UserClass, User } from 'server/models/user/model'

  const user = await User.findOne({
    email
  })

But it gets stuck on the User.findOne line and never returns from it.

Last thing the console prints before that is DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated even though I'm using (mongoose as any).Promise = Promise.
Before Typegoose I didn't get the deprecation warning and the Promise line did work.

Typegoose 3.6.0
Node v8.5.0

Any ideas?

Support for compound indexes

I have not found any reference about how to define compound indexes using Typegoose.
Is there any way to do it?

Thanks.

Schema methods should not be undefined

If a schema has no instance methods defined by the user, mongoose will still try to access its structure when timestamps are enabled for it.

The following error will occur:

TypeError: Cannot set property 'initializeTimestamps' of undefined

The cause is that schema.methods is undefined and mongoose tries to add a new method by:
this.methods.initializeTimestamps = function { ... }

A solution to this is simple: typegoose.ts:37:

sch.methods = instanceMethods || {};

Data not saving correctly

I want to save a mongoose document with a typegoose sub-document but data isn't saving correctly.

Car Model does not extend Typegoose
import * as mongoose from 'mongoose' const Model = mongoose.Schema const CarModel = new Model ({ color: String, driver: { type: Schema.Types.ObjectId, ref: 'UserModel' }, })

UserModel extends Typegoose
`export class User extends Typegoose {
@prop()
public name: string;
}

export const UserModel = new User().getModelForClass(User)`

Different file to create Data to save
const autoMobile = {};
autoMobile.color = 'black';
autoMobile.driver = new UserModel({name: 'Bob'});
Car.create(autoMobile, (createdCar: any) => { return createdCar; })

Everything is saved correctly except the name attribute on the userModel. Mogoose even creates the ID correctly. Any help is much appreciated

How to use InstanceType?

I have my class like this:

export class User extends Typegoose {
}

How can I add mongoose.Document or InstanceType to it? Because now I have to do this in all my functions:

export async function addUser(args: IAddUserArgs): Promise<User & mongoose.Document> {
}

Which is a bit annoying because I have to import mongoose in every file that imports User.

I'm not sure how to do this with my limited TS knowledge :)

Using Mongoose Plugins ?

Is there any way to use the very useful Plugins from mongoose ? Or anything allowing me to re-use a bunch of properties in several models ?

Re-using the a sub schema could be a solution but it does not allow custom pre-validation on on the added properties. I've also tried using typescript mixins, but this method needs to redeclare the variables in the implementing class.

Any ideas about this ?

Thanks in advance.

Unnecessary package.json dependencies

Why mongoose is a real dependency of typegoose? It should be a peerDependency and users should install it by themselves, as they still need to explicitly use it by themselves:

import * as mongoose from 'mongoose';
mongoose.connect('mongodb://localhost:27017/test');

So when they use different version than the typegoose one, there will be problems with no errors marked by npm - two copies of mongoose in one project.

Also, you distribute on npm the compiled version and "main" points to "lib/typegoose.js" which is correct. So why you have typescript listed in dependecies? Lib consumers doesn't need this, they can use the lib even with flow+babel, as long as they produce api-compatible js transpiled code.

import 'reflect-metadata'; is also not needed in typegoose source - it's only a runtime shim for Reflect.metadata to make TS decorator class type reflection works. Again, users should import it in their entry file as some of them might want to use core-js polyfill instead.

Pre and Pos methods

First of all, The idea is awesome!

I just wanted to know about how to implement 'pre' and 'pos' save methods in typegoose way.

Thanks!

@instanceMethod not working

I can't make @instancemethod work at all. Even with this simple example:

// on the User model class:
@instanceMethod
    public changeName(this: InstanceType<User>) {
        this.name = "blabla";
    }

In some other file where I create an User:

const user: User = await UserService.getOneBy...(..); //this succesfully creates an user based on db
user.doSomething();

I get this error:

error TS2684: The 'this' context of type 'User' is not assignable to method's 'this' of type 'InstanceType<User>'.
  Type 'User' is not assignable to type 'Document'.
    Property 'increment' is missing in type 'User'.

Any clue?

Thanks in advance!
Josรฉ

'plugin' documentation in README.md file improving

Hi.
I'm using typegoose increasingly in a production project; it works and keep clean the source code. Thank you!

Maybe, in the README.md file you can specify the following tips about 'plugin':

1- plugin source file written in typescript: check the export instructions

If the plugin is developed with Typescript language and it is not a 3rd part software, check the first line of the transpilled file.
From Typescript 2.2 the first transpilled js line after the 'use strict' line, is 'Object.defineProperty(exports, "__esModule", { value: true });' and a mongoose error is generated because a function is needed at start of plugin.
To avoid this error you can start the plugin in the Typescript source file with the line:

module.exports = exports = function findOrCreatePlugin (schema, options?) {
...
}

2- plugin static methods in typegoose class

staticMethod decorator and an empy body {} are mandatory for a correct and complete method signature also when you add manually the static methods for typing information about a schema.statics function in a plugin.

@staticMethod public static findOrCreate(condition: InstanceType<User>): Promise<{ doc: InstanceType<User>, created: boolean }> {};

Whitout these two warnings in the README.md it's possible to lose a lot of work time.

findById read empty arrays when the items are defined with a "_id" field

Hi.
I'm using yet typegoose 3.5.2 because 3.6.0 don't works for me (see #68).

I defined a model with the "_id" field to simplify the configuration of data on db. This is not frequently but it works and it is admitted with mongoose.

When I load in memory a doc defined with an array of items wich are defined with an "_id" field, the array is empty.

Below the source file.

import * as tg from 'typegoose';

export class perm extends tg.Typegoose {
    @tg.prop() routerFunction : string;
    @tg.prop() permit: boolean;
}

export const permModel = new perm().getModelForClass(perm);


export class group extends tg.Typegoose {
    @tg.prop( { required: true, index: true, unique: true } ) _id: string;
    @tg.prop( { required: true, index: true, unique: true } ) name : string;
    @tg.arrayProp( { itemsRef: perm } ) perms ?: tg.Ref<perm>[];
}

export const groupModel = new group().getModelForClass(group);

export class admin extends tg.Typegoose {
    @tg.prop() name : string;
    @tg.arrayProp( { itemsRef: group } ) groups ?: tg.Ref<group>[];
    @tg.arrayProp( { itemsRef: perm } ) perms ?: tg.Ref<perm>[];
}

export const adminModel = new admin().getModelForClass(admin);

adminModel.findById('an id').exec((err, data) => {
   console.log(data); // prints correctly the perms array data but an empty groups array
}

feathers-mongoose

Hi,

I am trying to use Typegoose with feathers-mongoose and I guess I am making some beginnerยดs mistake, because I canยดt get it to work.

My model class looks like this:

export class PostalCode extends Typegoose{
    @prop({ required: true })
    code: String
    @prop({ required: true })
    city: String
}

I thought, I could simply replace the model creation (which is commented out and works fine) with the getModelForClass statement, but this doesnยดt work:

const service = require('feathers-mongoose');

function services() {
    /* var postalCodeSchema = mongoose.Schema({
        code: String,
         city: String
     });

    var PostalCodeModel = mongoose.model('PostalCode', postalCodeSchema); */

    var PostalCodeModel = new PostalCode().getModelForClass(PostalCode);

    this.use('/postalcode', service({
           Model: PostalCodeModel,   
           paginate: {
            default: 2,
            max: 4
           }
      }));
} 

I would be great if you could give me a hint, what I did wrong.

Thanks Alex

Add missing SchemaType Options (unique, index, etc).

I'm not quite sure why there is an agrupation of the "allowed" SchemaType Options (required, enum, default, min, max, etc) in the code (BasePropOptions, ValidateNumberOptions, etc). I guess you want TS to ensure what is being sent in the @prop() and @arrayProp() annotations... right?

The annoying part of this is that we should add the missing SchemaType Options manually ๐Ÿ˜ข

I've already tested adding unique and index in the BasePropOptions:

export interface BasePropOptions {
  unique?: boolean; // Unique is always boolean!
  index?: boolean; // Same here.
...

...and they work just fine. Would you like a PR for this? This is another simple addition ;)

Error with nested classes order

I got the following error when trying to nest models...:

Error: There is no metadata for the "job" property. Check if emitDecoratorMetadata is enable in tsconfig.json
at [...]/node_modules/typegoose/src/prop.ts:148:11

... if the nested model is defined after the parent one (this is the same example of your md file, without the Car ref):

import { prop, Typegoose } from 'typegoose';

class User extends Typegoose {
    @prop()
    name?: string;

    @prop({ required: true })
    age: number;

    @prop()
    job?: Job;


}

class Job extends Typegoose {
    @prop()
    title?: string;

    @prop()
    position?: string;
}


export const UserModel = new User().getModelForClass(User);

The work around is to define Job before User.

I'm using:
Hapi 16.4.3,
typegoose 3.0.0,
typescript 2.4.1,
ts-node 3.1.0,

And yes, I have "emitDecoratorMetadata": true in tsconfig ๐Ÿ˜‰

Let me know if you can reproduce this!
Thanks!

GeoJSON Support

Hi,

is it possible to use GeoJSON with Typegoose?
I'm not able to create an index so I can search it with $nearSphere.

What I've tried is to create the following model to store the data:

export class LocationData extends Typegoose {
    @prop()
    public type: string;

    @arrayProp({ items: Number })
    public coordinates: [number];
}

In my user model I have added the following attribute:

export class User extends Typegoose {
    @prop()
    public currentLocation: LocationData;
}

Since there must be an index on currentLocation to be able to perform the $nearSphere query I tried @prop({index: true}).

But I got the following error message:

TypeError: Undefined type `undefined` at `currentLocation.index`
  Did you try nesting Schemas? You can only nest using refs or arrays.

Some tutorials said you must define your own type to store the location data.
But how do I do it with Typegoose?

The mongoose way is:

var locationSchema = new Schema({  
  loc: {
    type: {
      type: "String",
      required: true,
      enum: ['Point', 'LineString', 'Polygon'],
      default: 'Point'
    },
    coordinates: [Number]
  }
});

locationSchema.index({ 'loc': '2dsphere' });
var Location = mongoose.model( 'Location', locationSchema );

What do I need to do to solve this issue?

Fields loaded are more than in the Model definition

Hi.
Great job but I have encountered maybe a trouble:
If I use the 'find' method of my little example in that I defined only two fields (username and email), if there are more fields in the database (firstname, lastname, username, email, password, ...), Typegoose load all the field. If I use the mongoose schema, mongoose load only the two field. It seems like when you define an empty schema.
Can you explain me that?
Below I attached my source files: server, the model and a route page.

user.model.ts
IndexRoute.ts
server.ts

Thank you! Typegoose is really THE typescript solution for mongoose.

EDIT (06/oct/2017):
I found the solution using the 'projection' parameter of the 'find' method.
I don't know if there is a typegoose method or a better idea to compose the projection string:

let proj = Object.keys(UserModel.schema.obj).join(' ');

public index(req: Request, res: Response, next: NextFunction) {
       let proj = Object.keys(UserModel.schema.obj).join(' ');

        UserModel.find({}, proj, (err, data) => { 
            if(err) {
                res.status(200).json({msg: 'err', "err": err});
                return next(err);
            }
            res.status(200).json({msg: 'ok', "data": data});
           // prints:
           // {"msg":"ok","data": [{"_id":"57669d50a87cfa704393bf54","username":"myusername","email":"[email protected]"}]}
        });

    }

Troubles with 3.6.0 version of Typegoose

Hi.
I used an old version of typegoose and all works correctly.
To day I tested the 3.6.0 version of Typegoose and I encountered some troubles.

I tested my project with the 3.5.2 version of Typegoose and it works correctly again.

With the debugger I checked that the execution is looped at the count instance method of 'pgLoginAttemptModel' in the 'getIpUserCount' function.

The source file is:

import * as async from 'async';
export class pgLoginAttempt extends Typegoose {
  
    @prop({ default: '', index: true }) ip: string;
    @prop({ default: '', index: true }) user: string;
    @prop({ default: Date.now()}) time: Date;

}
      
export const pgLoginAttemptModel = new pgLoginAttempt().getModelForClass(pgLoginAttempt, { schemaOptions: { collection: 'pg-login-attempts'}});

protected abuseFilter(successEvent: string) {

        let getIpCount = (done) => {
            let conditions = { ip: req.app.ip };

            pgLoginAttemptModel.count(conditions, (err, count) => {
                if (err) return done(err);
                done(null, count);
            });
        };

        let getIpUserCount = (done) => {
            let conditions = { ip: req.app.ip, user: req.body.username };

            pgLoginAttemptModel.count(conditions, (err, count) => {
                if (err) return done(err);
                done(null, count);
            });
        };

        let asyncFinally = (err, results) => {
            if (err) return this.emit(this.err, err);

            if (results.ip >= 5 || results.ipUser >= 5) {
                return this.emit(this.fail);
            }
            else {
                this.emit(successEvent);
            }
        };

        async.parallel({ ip: getIpCount, ipUser: getIpUserCount }, asyncFinally);
};

Support inheritance from abstractModel (old: staticMethod not giving model as this)

Using the following code:

export default abstract class AbstractModel extends Typegoose {
    @staticMethod
    public static findByIdentifier(this: ModelType<AbstractModel>, identifier: Long) {
        return new Promise((resolve, reject) => {
            console.log(this);
            this.findOne({identifier}).then(resolve).catch(reject);
        });
    }

    @prop({required: true, index: true, unique: true})
    public identifier: string;
}

this is always an instance of the model, instead of the model type, so i can't call findOne.

Feel like im missing something...

Enhanced enum support

Right now, you support string array as an enum:

// Enum-like type and definition example.
type Gender = 'male' | 'female';
const Genders = {
  MALE: 'male' as Gender,
  FEMALE: 'female' as Gender,
};

@prop({ enum: Object.values(Genders) })
gender?: Gender;

However we have string enum support in TS now. It would be nice if the decorator function accept also the object type as the enum value and get the string values using Object.values(object) inside:

// true enum type
enum Gender {
  MALE = 'male',
  FEMALE = 'female',
};

@prop({ enum: Gender })
gender?: Gender;

So the signature of the BasePropOptions would be enum?: string[]|object; and you can in runtime check if Array.isArray(enum) to behave like now or convert enum object to values.

defining propArray as a type that extends typegoose results in an error

_\node_modules\mongoose\lib\index.js:477
  if (schema.$globalPluginsApplied) {
                    
TypeError: Cannot read property '$globalPluginsApplied' of undefined
    at Mongoose._applyPlugins (_\node_modules\mongoose\lib\index.js:477:14)
    at Mongoose._applyPlugins (_\node_modules\mongoose\lib\index.js:487:10)
    at Mongoose.model (_\node_modules\mongoose\lib\index.js:396:10)
    at TestModel.setModelForClass (_\node_modules\typegoose\lib\typegoose.js:44:31)
    at TestModel.getModelForClass (_\node_modules\typegoose\lib\typegoose.js:20:18)

Setup looks like the following:

export const TestModel = new Test().getModelForClass(Test, {existingConnection: mongoose});

export class Test extends Typegoose {
    @prop({index:true})
    userId:string;
    @arrayProp({ items: Item})
    items?: Item[];
export class Item extends Typegoose {
    @prop({index:true})
    id:string;
    @prop()
    name: string;
}

using mongoose 4.13.6

Optional object fields always created

Models containing fields of a custom class type seem to always be created when retrieving a document from the collection, even if said field is optional.

For example, the model:

class Thing extends Typegoose {
    @prop() text: string;
}

class Container extends Typegoose {
    @prop() numeric: number;
    @prop() custom?: Thing;
}

The collection:

[ { numeric: 1 } ] // _id omitted for brevity

If you query and log the result to the console, it logs the expected value

Collection.findOne({ numeric: 1 }).then(r => console.log(r));

logs

{ numeric: 1 } // _id omitted for brevity

But you can somehow still access the non-existent 'custom' field and it will return an empty object

Collection.findOne({ numeric: 1 }).then(r => console.log(r['bonus']));

logs

{ }

whereas it should be undefined since it isn't defined in the document.

I'm not exactly sure that this is Typegoose's fault, but I don't think this should happen using just Mongoose, but I haven't tried. ๐Ÿ˜•

Tests failing in the version published in master.

I'm getting this erros while running the test in master, it seems to be related to the last changes of plugins.

> [email protected] lint /home/dgomez/Proyectos/OpenSource/typegoose
> tslint --type-check --project tsconfig.json

Error at src/typegoose.ts:53:29: Property 'mongoosePlugin' does not exist on type 'string'.
Error at src/typegoose.ts:53:52: Property 'options' does not exist on type 'string'.
Error at src/typegoose.ts:59:19: Property 'get' does not exist on type 'string'.
Error at src/typegoose.ts:60:38: Property 'get' does not exist on type 'string'.
Error at src/typegoose.ts:62:19: Property 'set' does not exist on type 'string'.
Error at src/typegoose.ts:63:38: Property 'set' does not exist on type 'string'.

Plugins

Dear Szokodiakos

Thank you for Typegoose that helps a lot in simplifying code and improve maintainability. I have an issue and could not understand why and how it happens when using 'mongoose-paginate' as the Typegoose plugin, I have followed the guides for similar issue in this git but it still does not work, could you help look at and kindly advise why plugin does not return result?
Extracted code is as below (which worked well if I use plugin with native mongoose but not via Typegoose)

import {
prop,
Typegoose,
InstanceType,
staticMethod,
plugin,
} from 'typegoose';

// tslint:disable-next-line:no-var-requires
import * as paginate from "mongoose-paginate";

export interface PageinateResult {
docs: [InstanceType];
total?: number;
limit?: number;
}

@plugin(paginate)
export class ClientType extends Typegoose {
@prop({ required: true, minlength: 5, index: true })
name: string;

@prop()
status?: string;

static paginate: (query, options) => Promise<PageinateResult<ClientType>>;    
// I thought the issue happens here but try several scenarios still not work

}
export const model = new ClientType().getModelForClass(ClientType);

Then

// As I use multi DB thus below
var client = new ClientType().getModelForClass(ClientType, {schemaOptions: {collection: 'Client'}, existingConnection: mongooseConnection});

Where I expect it returns the documents, it only return the unfulfilled Promise
client.paginate(query, options)
.then(result => {
console.log(result)
});

Thank you for your advices.

Limited Hooks methods

type Method = 'init' | 'validate' | 'save' | 'remove';

please add all methods, like 'findOne', 'find' ... etc.

i can't create a class with @pre findOne

Automatic pluralizing collection names

Typegoose automatically pluralizing my collection names. for example my collection is device_warehouse, on the db it became device_warehouses with "s". I hope I can get feedback soon.

Objects within Schema

I want to do the following (regular Mongoose):

let personSchema = new mongoose.Schema({
  id: String,
  address: {
    street: String,
    streetnumber: Number,
  }
});

With Typegoose:

class Person extends Typegoose {
  @prop()
  id: string;

 @prop()
  address: {
    street: string;
    streetnumber: number;
  }
});

But it says: Object is not a primitive type nor a Typegoose schema (Not extending it).

If i do like described in the readme(creating a seperate class for address) Typegoose would create a new schema and a seperate document for address and that is not what i want to do.

Possible to use references in instance methods

Something like:

class GuildMember extends Typegoose {
    @prop({ref: User})
    public user: Ref<User>;

    public get id() {
        return this.user.identifier; // <------ identifier doesn't exist on `Ref`
    }
}

Feature: Class Type reflection capabilities

I would love to see the reflection capabilities with class instances, just like obj.constructor works like.

I use class-transformer for document serialization:

import { Typegoose, prop as Property } from "typegoose";
import { Exclude, Expose } from "class-transformer";

@Exclude()
export class User extends Typegoose {

    @Expose()
    id: string;

    @Expose()
    @Property({ unique: true })
    email: string;

    @Property()
    password: string;

}

export const UserModel = new User().getModelForClass(User);

It would be amazing to be able to write generic serialize method which could take the original ClassType from the mongoose document and invoke plaintToClass(ClassType, object) without explicit specifying the constructor class function.

Examples:

// instead writing in http framework controllers methods
const dbUser = await UserModel.findOne({ email: "test" });
const clsUser = plainToClass(User, dbUser!);
return classToPlain(clsUser);

// I would like to return plain document
return await UserModel.findOne({ email: "test" });

// and the `serialize` method would be invoked by http framework before sending response to client
function serialize(document) {
    const ClassType = getClassTypeForTypegooseDocument(document);
    return plainToClass(ClassType, document);
}

I know that your main goal is to define the schema by class but since you don't return the class itself as a document (like TypeORM does) it would be necessary to provide posibility to get the class of the document of schema created by the class ๐Ÿ˜‰

I think that the best way would be to store this.constructor function from getModelForClass inside typegoose's constructors dictionary and then get the constructor by model name with the model name from docObj.constructor.modelName ๐Ÿ˜ƒ

Type return of getModelForClass

Hi, I'm trying typegoose since yesterday, and I have a question related to your implementation. (Thanks a lot for your work, this package rocks!):

Why can't we assign a model as a type?

import { prop, Typegoose} from 'typegoose'

class User extends Typegoose {
    ...
    @prop({ required: true})
    public firstName: string;
    ...
}

export default new User().getModelForClass(User);
import User from './User'

let user:User = new User(); // -> Does not work because User is not a type 

Removing 'id' field from Typegoose

Would it be possible to remove the id field from the Typegoose class?

My documents have their own id field which are numbers instead of strings, so TypeScript gives me an error telling me that I haven't extended the Typegoose class properly. However, removing the id field from the Typegoose typings allows it to compile and work just fine.

If having the id field is necessary in some way, maybe consider renaming it to _id since id does not necessarily seem to be reserved in MongoDB.

TypeError: Class constructor Typegoose cannot be invoked without 'new'

Hi,
I'm for some reason getting this error "TypeError: Class constructor Typegoose cannot be invoked without 'new'"

I have a class like this:

export class Item extends Typegoose {
	@prop()
	name: string;

	@prop()
	description: string;

	@prop({ref: Asset})
	question: Asset;

	@prop({enum: ItemTypes})
	itemType: ItemTypes;

	@prop({enum: ItemModes})
	itemMode: ItemModes;
}

I also have service which uses the class like this:

export class ItemsService {
	private itemModel;

	constructor(
	) {
		this.itemModel = new Item().getModelForClass(Item);
	}
}

As TS compiles:

TypeError: Class constructor Typegoose cannot be invoked without 'new'
    at new Item (/Users/anvlkv/Projects/intervey/intervey-api/src/models/item.ts:40:42)
    at new ItemsService (/Users/anvlkv/Projects/intervey/intervey-api/src/server/services/itemsService.ts:11:20)
    at new SequenceService (/Users/anvlkv/Projects/intervey/intervey-api/src/server/services/sequenceService.ts:13:23)
    at Object.<anonymous> (/Users/anvlkv/Projects/intervey/intervey-api/src/server/routes/InterviewerRoutes.ts:9:42)
    at Module._compile (module.js:570:32)
    at Module.m._compile (/Users/anvlkv/Projects/intervey/intervey-api/node_modules/ts-node/src/index.ts:392:23)
    at Module._extensions..js (module.js:579:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/Users/anvlkv/Projects/intervey/intervey-api/node_modules/ts-node/src/index.ts:395:12)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/anvlkv/Projects/intervey/intervey-api/src/server/main.ts:4:1)
    at Module._compile (module.js:570:32)
    at Module.m._compile (/Users/anvlkv/Projects/intervey/intervey-api/node_modules/ts-node/src/index.ts:392:23)
[nodemon] app crashed - waiting for file changes before starting...

The strangest part is that my src/models/item.ts is only 33 lines and problem supposed to be at line 40.

Cannot set custom mongoose.Promise when using Typegoose

I'm using typegoose 3.6.0 with mongoose 4.13.0.

Setting mongoose.Promise doesn't work when using Typegoose, even if setting it everywhere I import mongoose. As a result (I'm assuming) the mongoose promises are never executed. Removing Typegoose solves the problem.

Eveywhere I import mongoose:

const mongoose = require("mongoose")
(<any>mongoose).Promise = global.Promise;

Defining the model:

export enum Role {
    USER = "user",
    CONTRIBUTOR = "contributor",
    ADMIN = "admin"
}

export class UserProto extends Typegoose {
    _id: mongoose.Types.ObjectId;
    createdAt: Date;
    updatedAt: Date;

    @prop({ enum: Role, default: Role.USER })
    role: Role;
}

export const User = new UserProto().getModelForClass(UserProto, { schemaOptions: { timestamps: true }});

"in save" is never logged

let user = new User({role: Role.USER});
user.save().then((doc) => {
      console.log("in save");
      
      return doc.toObject();
    }).catch((err) => {
      console.log("error");
    }); 

Error with required nested elements

If you put { required: true } in the prop annotation over a nested element you get this error:

TypeError: Undefined type undefined at job.required
Did you try nesting Schemas? You can only nest using refs or arrays.

You can try it with the same code of your md file example:

class Job extends Typegoose {
    @prop()
    title: string;

    @prop()
    position: string;
}

class Contact extends Typegoose {

    @prop({ required: true }) // Added the required constraint
    job: Job;


}

The work around is... not using "required: true" over nested elements (yay!).

I'm using:
Hapi 16.4.3,
typegoose 3.0.0,
typescript 2.4.1,
ts-node 3.1.0,

Once again, let me know if you can reproduce this.
Thanks in advance!

Troubles with reciprocal reference fields in the models

Hi.
Please, can you help me to solve a problem with reciprocal reference fields in the models?
Yes, it seems strange but usefull in something situations.

If you have two classes with reciprocal reference fields in the models, you can receive an Error (In property user: Object is not a primitive type nor a Typegoose schema (Not extending it).) during che compilation phase.
Maybe because one of the classes is not yet defined. This happens also if the two classes are in the same file.
I searched for a foreward instruction without success.

Below a short example:

import { prop, staticMethod, instanceMethod, Typegoose, ModelType, InstanceType, Ref } from 'typegoose';

export class Admin extends Typegoose {
    @prop() firstName: string;
    @prop({ ref: User }) user?: Ref<User>;
}
export const AdminModel = new Admin().getModelForClass(Admin);

export class UserRole {
    @prop({ ref: Admin }) admin?: Ref<Admin>;
}

export class User extends Typegoose {
    @prop({ required: true }) email: string;
    @prop({ required: true }) username: string;
    @prop() roles?: UserRole;
}
export const UserModel = new User().getModelForClass(User);

Thank you in advance.

'expires' option in @prop decorator missing

Hi.
It's very usefull the 'expires' option for the schema field definition when data type is Data.

Below a mongoose example:

new Schema({ 
   createdAt: { type: Date, expires: 60*60*24 }
});

Please, can you consider to implement it?
Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.