@rakered/mongo
v1.6.0
Published
A tiny but elegant wrapper around the native mongodb driver for Node.js, removing boilerplate and fixing id generation.
Downloads
74
Readme
@rakered/mongo
A tiny but elegant wrapper around the native mongodb driver for Node.js, removing boilerplate and fixing id generation.
Usage
Connections to mongodb are established when they're needed. Lazily. You don't need to handle this yourself, it's handled for you. Connection strings in this library always default to process.env.MONGO_URL
, or to mongodb://localhost:27017
if the env key is not set.
import db from '@rakered/mongo';
await db.comments.insertOne({ body: 'Oh my! Is that all?!' });
Create
The connection is established when the collection method is invoked. In the example above, that is the moment that insertOne
is executed. If you wish to have more control over the connection, for example because you need to connect to multiple databases at the same time, you can use the `create`` method.
import { create } from '@rakered/mongo';
const db = create('mongodb://example.com:27017/rakered', {
/* mongo options */
});
await db.users.findOne({ username: 'smeijer' });
Connect
It's possible that you need to connect early, because you don't want to let the first user wait for the connection to be established. Lazy connect works nice in dev environments or cli tooling, but it's not always what we want in web production. The connect
method makes early connect dead simple.
import { connect } from '@rakered/mongo';
const db = await connect();
await db.users.findOne({ username: 'smeijer' });
The example above does not have any performance benefit, but you can imagine that when the findOne
statement is running in an express handler, it's nice to have that connection ready when the user hits your endpoint.
The connect
method is a simple wrapper around create
async function connect(uri?: string, options?: Options): Promise<Db> {
const db = create(uri, options);
await db.connect();
return db;
}
Disconnect
Now, if you're working with a script and needing to close the connection, db
has a method for that:
await db.disconnect();
Differences with Native Driver
All methods return promises. This choice was made to keep our codebase simple, while being able to offer the lazy connections. Most of the mongodb native collection methods already return promises, the differences are listed below. Methods that are not listed here, act the same as the native mongodb variant.
pkFactory
& pkPrefix
We use picoid
as pkFactory
, and have hot-patched the native driver to add support for bulk operations as well.
In addition to the native driver, it's possible to configure id prefixes per collection. Note that separators are not automatically added! It's for you to decide if you want to use prefixes, and if you want to include a separator, and if so, which character it should be.
db.customers.pkPrefix = 'cus_';
db.customers.insertOne({ name: 'Stephan' });
// » customer._id is now cus_vsrd…
db.collection.find
The find method now returns a promise, which directly resolves the results. There is no more need to call toArray()
.
await db.users.find({ ... });
// » User[]
db.collection.aggregate
The aggregate method now returns a promise, which directly resolves the results. There is no more need to call toArray()
.
await db.users.aggregate([{ ... }])
// » User[]
db.collection.paginate
The paginate method is an addition to the native methods, that can be used for (relay style) cursor based pagination. It resolves to a connection with nodes, edges, and pageInfo.
Use the optional type
option to limit the response to either nodes
or edges
.
await db.users.paginate({ ... }, { first: 10, sort: ['name', 'asc'], after: '...' })
/* » {
nodes: User[],
edges: { cursor: string, node: User }[],
totalCount: number;
pageInfo: {
hasNextPage: boolean;
hasPreviousPage: boolean;
endCursor: string;
startCursor: string;
}
} */
db.collection.listIndexes
The listIndexes method now returns a promise, which directly resolves the results. There is no more need to call toArray()
.
await db.users.listIndexes([{ ... }])
// » Index[]
db.collection.initializeOrderedBulkOp
This method now returns a promise, while it's a sync method in the native driver.
const bulk = await db.users.initializeOrderedBulkOp();
db.collection.initializeOrderedBulkOp
This method now returns a promise, while it's a sync method in the native driver.
const bulk = await db.users.initializeUnorderedBulkOp();
db.transaction
Run operations inside a transaction.
await db.transaction(async (session) => {
const amount = 1000;
await db.accounts.updateOne(
{ name: 'sender' },
{ $inc: { balance: -amount } },
{ session },
);
await db.accounts.updateOne(
{ name: 'receiver' },
{ $inc: { balance: amount } },
{ session },
);
});
Good to knows
This library wraps the native mongo driver, and adds a few restrictions to it. Let's talk about those.
Every collection method, now returns a promise. We don't support the callback style, and unlike the native drivers, the collection methods
find
,aggregate
,listIndexes
,initializeOrderedBulkOp
andinitializeUnorderedBulkOp
return promises as well.We wrap the
db
object to get a mongo shell kind of feeling, where you don't need to writedb.getCollection('test').insert
but can simply calldb.test.insert
. This does mean that some names should not be picked as collection name as those will result in name conflicts. The reserved ares
,serverConfig
,bufferMaxEntries
,databaseName
andclose
. I believe that's manageable.We've added a
disconnect
handler to thedb
, which proxiesclient.close
. Just for developer convenience.We also use
picoid
to generate string based random ids. The Mongo ObjectID has its good side, but it makes_id
handling and parsing quite verbose. Strings are easier to work with.As MongoDB didn't feel for fixing their
pkfactory
to support custom id's in bulk upserts because "It would deviate from other drivers that do not have this behavior", we've patched that in this library.