@nestjsplus/massive

NestJS module integrating MassiveJS


Keywords
nestjs, javascript, massive, postgresql, sql, typescript
License
MIT
Install
npm install @nestjsplus/massive@1.2.0

Documentation

A Module for Utilizing MassiveJS with NestJS

Installation

npm install @nestjsplus/massive

(or yarn equivalent)

About Massive and PostgreSQL

This module is a thin layer on top of the MassiveJS library.

As an old database guy, I sometimes feel like the OR/M days passed me by 😃

In truth, I find the OR/M model and the pure SQl model to just define opposite ends of a spectrum, and I'm happy to live somewhere in the middle. Massive provides a very happy middle ground. Looked at one way, it introspects your database and provides an automatic API to it in a way that seems similar to a MongoDB type API. You can get quite far with save(), find(), etc. methods that are similar to MongoDB in many ways, but do not require a model. As Dian Fay, author of Massive says, "Massive analyzes and builds an API for the data model expressed in your database's tables, views, and functions".

Massive is also PostgreSQL only. If you don't care about database portability (I'm firmly attached to PostgreSQL, and would have to change a lot more than a DB API library to move), this is a big boon. Because it doesn't sacrifice at the alter of "portability", Massive takes significant advantage of native PostgreSQL functionality.

It has a nice query builder, but doesn't obscure SQL, and let's you get right down to the metal easily.

It fully supports database functions. It even lets you write parameterized SQL files on the file system and call them just like procedures (one of my favorite features, and an underrated one at that, as it gets SQL out of your TypeScript, lets you manage the SQL script files nicely in your git repo just like the rest of your app, and it makes it dead easy to avoid SQL injection).

On top of all that, it has full JSONB support, meaning you get to treat your PostgreSQL database like a full-fledged NoSQL database, storing JSON objects natively. You can store them alongside your relational data, query them with API calls or native SQL (with JSONB support), or mix-and-match.

Quick Start

To configure your DB connection, import the Massive module using the familiar register() / registerAsync() pattern. See the example repo for an example. Basically, you configure the module with at least a connectionOptions object, and optionally a configurationOptions object and driverOptions object. These options objects map directly to the connection, configuration, and driver options in the Massive docs.

Once configured, inject the SINGLETON connection object into any service using the MASSIVE_CONNECTION injection token.

For example, your AppModule might look like this (full example in the sample repo):

// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';
import { ConfigService } from './config/config.service';
import { MassiveModule } from '@nestjsplus/massive';

@Module({
  imports: [
    MassiveModule.registerAsync({
      useClass: ConfigService,
    }),
    ConfigModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Now you have access to a MASSIVE_CONNECTION token that is associated with the PostgreSQL connection pool, which you can inject into any provider, and use directly. For example, you might do this:

// src/app.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { MASSIVE_CONNECTION } from '@nestjsplus/massive';

@Injectable()
export class AppService {
  constructor(@Inject(MASSIVE_CONNECTION) private readonly db) {}

  async find(age) {
    const criteria = age ? { 'age >=': age } : {};
    return await this.db.cats.find(criteria);
  }
  ...

Here, you've injected the connection as a local property of the service class, and can access any of the MassiveJS API through that property (e.g., return await this.db.cats.find(criteria), where db represents your MassiveJS connection object).

Configuring connectionOptions

I'm not showing the ConfigService in the AppModule above, but it's just an injectable that implements the MassiveOptionsFactory interface, meaning it has methods to return a connectionOptions object (and optionally, configurationOptions and driverOptions objects). A connectionOptions object looks like:

{
  "host": "localhost",
  "port": 5432,
  "database": "nest",
  "user": "john",
  "password": "password"
}

You can use any of the following methods to provide the connectionOptions (and optionally configurationOptions and driverOptions) to the module. These follow the usual patterns for custom providers:

  • register(): pass a plain JavaScript object
  • registerAsync(): pass a dynamic object via:
    • useFactory: supply a factory function to return the object; the factory should implement the MassiveOptionsFactory interface
    • useClass: bind to a provider/service that supplies the object; that service should implement the MassiveOptionsFactory interface
    • useExisting: bind to an existing (provided elsewhere) provider/service to supply the object; that service should implement the MassiveOptionsFactory interface

Connection availability on application startup

The MASSIVE_CONNECTION is an asynchronous provider. This means that the Nest application bootstrap process (specifically, the Dependency Injection phase) won't complete until the DB connection is made. So your app, once it bootstraps, is guaranteed to have a DB connection (pool) via the MASSIVE_CONNECTION injection token. Note that asynchronous providers must be injected with the @Inject() decorator instead of normal constructor injection (again, see the example).

Working Example

See massive-cats for a full example. It shows an example of using the MASSIVE_CONNECTION, a service that uses it to access a PostgreSQL database, and includes a few of the nifty Massive features described above.

Ingres

Boring end-notes here! My long love affair with SQL databases began when I started working for Ingres Corp., provider of the Ingres database commercial product. It had a long prior history as a university research project. Amazingly, while it had a relatively short-lived commercial life-span (solid ass-kicking by Oracle, which (to this day, I maintain) had inferior technology but superior sales and marketing), it was reborn again as an open source project - (Post Ingres) - PostgreSQL. From what I can gather, PostgreSQL is widely regarded as the leading open source RDBMS, and by my accounts, for good reason. Among its many virtues:

  • It's free! 😃
  • It's fast and scalable
  • It's supported on Amazon RDS
  • It's got every modern SQL feature, and many, many more (check out Postgis if you are into mapping/GIS, for example)
  • It supports JSONB and is a very good NoSQL database
  • It has a super passionate community, and support on StackOverflow is superb
  • I could go on, but I won't 😄

To Do

Change Log

See Changelog for more information.

Contributing

Contributions welcome! See Contributing.

Author

John Biundo (Y Prospect on Discord)

License

Licensed under the MIT License - see the LICENSE file for details.