Mongo
module for the Hapiness framework including mongoose
, mongoose-gridfs
(deprecated) and mongoose-gridfs-bucket
adapter one.
- Using your module inside Hapiness application
- Creating
Adapters
- Registering adapters
- Using a registered adapter
- Configuration
- Get your adapter anywhere
- Model management
- Helpers functions
- Contributing
- Change History
- Maintainers
- License
$ npm install --save @hapiness/core @hapiness/mongo rxjs
or
$ yarn add @hapiness/core @hapiness/mongo rxjs
"dependencies": {
"@hapiness/core": "^1.3.0",
"@hapiness/mongo": "^1.1.3",
"rxjs": "^5.5.6",
//...
}
//...
This module provide an Hapiness extension for Mongo. To use it, simply register it during the bootstrap
step of your project like that:
import { MongoModule, MongoClientExt } from '@hapiness/mongo';
@HapinessModule({
version: '1.0.0',
providers: [ ],
declarations: [ ],
imports: [ MongoModule ]
})
class MyModule implements OnStart {
constructor() { /* ... */ }
}
Hapiness.bootstrap(MyModule, [ MongoClientExt.setConfig(/* ... */) ]);
The Mongo
module is based on adapters. Included to the module, there is an adapter using mongoose and one using mongoose to manage gridfs.
But you can create your own adapters if you want by following some required steps described belows.
Your adapter should be a class which inherits from AbstractHapinessMongoAdapter
.
You absolutely need to implement a static function getInterfaceName
, which will return a uniq string identifier for your adapter (NOTE mongoose
, mongoose-gridfs
and mongoose-gridfs-bucket
are already used by adapters provided by this module).
You need to override 4 functions
_tryConnect(): Observable<void> { /* ... */ }
_afterConnect(): Observable<void> { /* ... */ }
getLibrary(): any { /* ... */ }
registerValue(): any { /* ... */ }
_tryConnect: you will create your database connection inside
Example for mongoose
protected _tryConnect(): Observable<void> {
return Observable
.create(observer => {
this._isReady = false;
if (this._db) {
this._db.close();
}
const connectOptions = {
server: {
reconnectTries: Number.MAX_VALUE,
reconnectInterval: 5000,
},
};
this._connection = mongoose.createConnection(this._uri, connectOptions);
this._connection.once('connected', () => {
observer.next();
observer.complete();
});
this._connection.once('error', err => {
observer.error(err);
});
});
}
_afterConnect: this function will be called just after _tryConnect
if you want to manage some stuff once your connection is fine.
Example for mongoose:
protected _afterConnect(): Observable<void> {
return Observable
.create(observer => {
this._db = this._connection.db;
this.onConnected().subscribe(_ => {
/* ... */
}, (e) => {
/* ... */
});
this._connection.once('error', err =>
this.onError(err).subscribe(_ => {
/* ... */
}, (e) => {
/* ... */
})
);
this._connection.once('disconnected', () =>
this.onDisconnected().subscribe(_ => {
/* ... */
}, (e) => {
/* ... */
})
);
observer.next();
observer.complete();
});
}
getLibrary: this will just return the library your adapter use to use it as you want.
Example for mongoose:
public getLibrary(): any {
return mongoose;
}
registerValue: this will register your document and return a model value to get through the DI.
Example for mongoose:
public registerValue(document, collection): any {
return this._connection.model(collection, document);
}
NOTE DONT FORGET TO SET _isReady = true
once you are done, else your adapter will never be ready.
You should also override:
close(): Observalbe<void> { /* */ }
When you want to create your own adapters, first you need to tell the Mongo extension to register it. The Mongo extension will add your classes and map it with the uniq identifier you put inside the static ddd
.
class MyCustomAdapter extends AbstractHapinessMongoAdapter {
public static getInterfaceName(): string {
return 'custom-identifier-for-my-adapter';
}
constructor(options) { super(options); }
/* ... */
}
Hapiness.bootstrap(
MyModule,
[
MongoClientExt
.setConfig(
{
register: [ MyCustomAdapter ]
}
)
]
);
Now, the mongo extension knows that an Adapter with the identifier custom-identifier-for-my-adapter
exists.
The two provided adapters don't need to be registered as it is already done.
It will work the same for both custom adapters you made and provided adapters.
Just load them with the config you want to use:
class MyCustomAdapter extends AbstractHapinessMongoAdapter {
public static getInterfaceName(): string {
return 'custom-identifier-for-my-adapter';
}
constructor(options) { super(options); }
}
Hapiness.bootstrap(
MyModule,
[
MongoClientExt
.setConfig(
{
register: [ MyCustomAdapter ]
load: [
{
name: 'custom-identifier-for-my-adapter',
config: {
host: 'my.hostname1.com',
port: 27017,
db: 'db_1'
}
},
{
name: 'mongoose',
config: {
host: 'my.hostname2.com',
port: 27017,
db: 'db_1'
}
}
]
}
)
]
);
So you can load as many connections as you want and provide custom configs for each adapter you load.
When you load adapters (see previous section), you can provide a config, but you have the possibility to not provide one every time.
Lets say you want two adapters pointing to the same database, you can for that use the common
option.
class MyCustomAdapter extends AbstractHapinessMongoAdapter {
public static getInterfaceName(): string {
return 'custom-identifier-for-my-adapter';
}
constructor(options) { super(options); }
}
Hapiness.bootstrap(
MyModule,
[
MongoClientExt
.setConfig(
{
register: [ MyCustomAdapter ]
common: {
host: 'my.hostname.com',
port: 27017,
db: 'my_db'
}
load: [
{
name: 'custom-identifier-for-my-adapter'
},
{
name: 'mongoose'
}
]
}
)
]
);
To get your adapter and play with it, you need to inject the MongoClientService in your class and call the get()
function to get an instance of the adapter manager.
Once you did it, you'll be able to get your adapter with its name only or with its name and options (if you have the same adapter in different DBs or host ...) calling the function getAdapter(...)
.
Example showing how to get mongoose adapter for my_database
:
@Injectable()
class MyModelDocument {
private _myModelConnection: any;
constructor(
private _mongoClient: MongoClientService
) {
this._myModelConnection = null;
}
init() {
// You can do that...
const dao = this._mongoClient.get().getAdapter('mongoose').getLibrary();
// ... or that
const connection = this._mongoClient.get().getAdapter('mongoose', { db: 'my_database' }).getConnection();
const MyModel = dao.Schema({
username: String,
});
this._myModelConnection = connection.model('MyModel', MyModel);
}
get() {
return this._myModelConnection;
}
}
You can implement and register models in the adapter
Example:
@MongoModel({
adapter: 'mongoose',
collection: 'collection', // The name of the collection, will be pluralize using mongoose if no collectionName is explicitly given
collectionName: 'collectionName', // Optional, will force this name as the collection name
options: { ... } // @see HapinessMongoAdapterConstructorArgs type
})
class MyModel extends Model {
readonly schema;
constructor(private mongoClientService: MongoClientService) {
super(MyModel) // /!\ Important to get connection options
const DAO = mongoClientService.getDao(this.connectionOptions);
this.schema = new DAO.Schema({
id: String
});
...
}
}
@Route({
path: '/my-route',
method: 'get'
})
class MyRoute implements OnGet {
constructor(private mongoClientService: MongoClientService) {}
onGet(request, reply) {
const model = this.mongoClientService.getModel({ adapter: 'mongoose', options: {} }, MyModel);
...
}
}
@HapinessModule({
version: '1.0.0',
declarations: [ MyModel, MyRoute ]
})
class MyModule {}
The module gives you an helpers to perform some basic mongo-related operations.
Just import the class from the module
import { MongoUtils } from '@hapiness/mongo'
There is 4 static functions (for now)
public static prepareUpdateObject(dto: any): any
: Give to this function an object like { "meta": { "key": "value" } }
and it returns you the object { "meta.key": "value" }
. Very usefull to perform update operations!
public static toObjectId(id: string)
: Returns an ObjectID type from the given string (null
if the string is not a valid ObjectID)
public static fieldsStringFromArray(fields: string[]): string
: If you want to select only some fields and you need for that to compute a string from an array of string, use this function
public static filterFindCondition(condition: any): any
: If you have a query object for mongo, this function will parse your condition, convert the field id
into _id
and then convert _id
to an ObjectID
before giving back the query object
To set up your development environment:
- clone the repo to your workspace,
- in the shell
cd
to the main folder, - hit
npm or yarn install
, - run
npm or yarn run test
.- It will lint the code and execute all tests.
- The test coverage report can be viewed from
./coverage/lcov-report/index.html
.
- v2.1.0
- Now set by default
useNewUrlParser: true
,useCreateIndex: true
anduseFindAndModify: false
on the connectionOptions of mongoose.
- Now set by default
- v2.0.2
- More debug and events for mongoose adapter
- v2.0.1
- Fix Mongo Utils - prepareUpdateObject
- v2.0.0
- Updated mongoose to latest version
- Removed the call to createConnection from the constructor of mongo adapter.
- Now catch connection errors and pipe them out to be catch by user later on. No more waiting for timeout to realize there is a connection error.
- MongooseGridfsAdapter is now deprecated. Use MongooseGridfsBucketAdapter
- v1.2.0 (2018-04-05)
- Updated packages' versions.
- added support of
OnShutdown
ofhapiness Core
- added
close
functions to adapters. - Fix typos in documentation.
- v1.1.3 (2018-01-16)
- Fix get adapter using adapter name
- v1.1.2 (2018-01-09)
- Latest packages' versions.
- Added connectionName options to identify an adapter by an alias
- Bug fix on an
indexOf !== 1
instead ofindexOf !== -1
- Documentation.
- v1.1.1 (2017-12-12)
- Latest packages' versions.
- Add field
collectionName
to force using a specific name for a collection. - Fix key computation when retrieving an adapter.
- Allow developers to not give all options for the adapter when getting a document.
- Documentation.
- v1.1.0 (2017-11-20)
- Latest packages' versions.
- Update Module + Tests related to latest
core
version. - Documentation.
- Change packaging process.
- v1.0.0 (2017-11-14)
MongoDB
module implementation.- Tests module API.
- Documentation.
Julien Fauville | Antoine Gomez | Sébastien Ritz | Nicolas Jessel | Florent Bennani |
Copyright (c) 2017 Hapiness Licensed under the MIT license.