Ottoman Class
Defining an Ottoman Instance
import { Ottoman } from "./ottoman";
const ottoman = new Ottoman();
Ottoman's instances are the backbone of Ottoman.js and the entry point to use the ODM in your app.
Ottoman Constructor Options
Ottoman allows you to modify it's settings, this is useful for database modeling or migration. One example is changing the metadata key to define the model in your documents.
import { Ottoman } from "./ottoman";
const ottoman = new Ottoman({
modelKey: 'type'
});
The modelKey
default value is set to _type
, but maybe you want to change it to type
as it's described in the example above.
The available configurations are:
interface OttomanConfig {
collectionName?: string;
scopeName?: string;
idKey?: string;
modelKey?: string;
populateMaxDeep?: number;
consistency?: SearchConsistency;
maxExpiry?: number;
keyGenerator?: (params: { metadata: ModelMetadata }) => string;
keyGeneratorDelimiter?: string;
}
collectionName
: Store value to use for each Model if not provided. Default: Model's namescopeName
: Store value to use for each Model if not provided. Default:_default
idKey
: Value of the key to save your id. Default: Document'sid
modelKey
: Key to store the model name into the document. Default:_type
populateMaxDeep
: Numeric Value for how many levels deep you want to _populate. Default:1
consistency
: Value for Search Consistency Strategy. Default:SearchConsistency.NONE
maxExpiry
: Numeric value (based in Milliseconds) used to create a collection for this instance. Default:0
keyGenerator
: Function to generate the key to store documents. Default:(params: { metadata: ModelMetadata }) => string
keyGeneratorDelimiter
: String value used to build the document key. Default:::
The default implementation for the keyGenerator
function is:
KEY_GENERATOR = ({ metadata }) => `${metadata.modelName}`;
keyGenerator
can be overridden for each Model
if needed. check this in model options
keyGeneratorDelimiter
has 2 restrictions:
- Only supports up to 2 characters
- The available characters are
~!#$%&*_\-:<>?
If the value provided to keyGeneratorDelimiter
is invalid Ottoman will throw a BadKeyGeneratorDelimiterError exception.
Some examples of keyGeneratorDelimiter
:
::
*default&
&?
_
##
keyGeneratorDelimiter
can be overridden for each Model
if needed. For more info see: model options
Connections
All your Models will be created via a connection and it will map to a Collection.
Create a Connection
You can connect to Couchbase Server with the connect() method.
import { Ottoman } from 'ottoman';
const ottoman = new Ottoman();
const main = async () => {
await ottoman.connect('couchbase://localhost/travel-sample@admin:password');
// connected to Couchbase Server
}
main();
This is the minimum needed to connect to the travel-sample bucket.
If the connection fails on your machine, try using 127.0.0.1
instead of localhost
.
Connection String Anatomy
Connection Options
connect
function also supports a JavaScript object as a parameter.
import { Ottoman } from 'ottoman';
const ottoman = new Ottoman();
const main = async () => {
await ottoman.connect({
connectionString: 'couchbase://localhost',
bucketName: 'travel-sample',
username: 'admin',
password: 'password',
});
}
main();
The available connections options are:
interface ConnectOptions {
connectionString: string;
username: string;
password: string;
bucketName: string;
authenticator?: CertificateAuthenticator;
trustStorePath?: string;
transcoder?: unknown;
logFunc?: unknown;
}
transcoder
: Provides an interface for performing custom transcoding of document contents being retrieved and stored to the cluster. For more details see transcoders-nonjson.
logFunc
: A callback function that receives the entry
variable as paramater.
Certificate Authentication
Couchbase Server supports the use of X.509 certificates to authenticate clients (only available in the Enterprise Edition, not Community Edition). This allows authenticated users to access specific resources using the data service in Couchbase Server 5.1 and up and all other services in more recent releases of Couchbase Data Platform.
The process relies on a certificate authority for the issuing of certificates that validate identities. A certificate includes information such as the name of the entity it identifies, an expiration date, the name of the authority that issued the certificate, and the digital signature of the authority. A client attempting to access Couchbase Server can present a certificate to the server allowing the server to check the validity of the certificate. If the certificate is valid, the user under whose identity the client is running and the roles assigned to that user are verified. If the assigned roles are appropriate for the level of access requested to the specified resource, access is granted.
For a more detailed conceptual description of using certificates, see Certificates.
Authenticating Ottoman by Certificate
For sample procedures whereby certificates can be generated and deployed, see Manage Certificates.
The rest of this document assumes that the processes there, or something similar, have been followed. That is, a cluster certificate has been created and installed on the server, a client certificate has been created, and it is stored in a JVM Keystore along with the cluster’s certificate.
import { Ottoman, CertificateAuthenticator } from 'ottoman';
const ottoman = new Ottoman();
const main = async () => {
await ottoman.connect({
connectionString: 'couchbases://localhost',
bucketName: 'travel-sample',
authenticator: new CertificateAuthenticator(
"/path/to/client/certificate.pem",
"/path/to/client/key.pem"
),
trustStorePath: "/path/to/ca/certificates.pem"
})
}
main();
Default Ottoman Instance
Ottoman creates an instance by default to make the job easier.
This instance is stored in the NodeJS process where the application runs and allows to use of the standalone functions connect
, model
, start
, close
.
Let's see an example of how to create an Ottoman application.
import {Ottoman, connect, model, start} from 'ottoman';
const main = async () => {
// Create the ottoman instance
const ottoman = new Ottoman();
// Connect to DB
await connect('couchbase://localhost/travel-sample@admin:password');
// Define your model
const User = model ('User', {name: String});
// Bootstrap the application
await start();
}
main();
Notice: We do not have to explicitly call the connect
, model
, or start
methods using the Ottoman instance, since the first Ottoman instance created is registered and stored as the default instance which will be used by the standalone under the hood functions.
e.g. connect(...)
will run for the default instance like this getDefaultInstance().connect(...)
Ottoman handles this logic for you.
The default instance can be obtained with the getDefaultInstance
function, which will return the default instance if it was created or undefined
otherwise.
import {Ottoman, getDefaultInstance} from 'ottoman';
const ottoman1 = new Ottoman ();
const ottoman2 = new Ottoman ();
// Getting default instance
const defaultInstance = getDefaultInstance ();
// defaultInstance = ottoman1
This can greatly simplify work in modular applications.
Using the standalone functions
import { connect, model } from 'ottoman';
const main = async () => {
// connecting to server
await connect('couchbase://localhost/travel-sample@admin:password');
// Now you can use the model function to create Models in the default instance
const User = model('User', { name: String });
}
main();
::: tip
Notice we start using Ottoman without creating any instance, it's possible by using the connect
function.
connect
function will create a default ottoman instance with default options if there's not an ottoman default instance created yet.
:::
IMPORTANT: This will be the recommended way to use Ottoman
if your app uses only 1 instance.
This way ottoman will save for you the Ottoman instance to work in any place of your code.
Example model
instead of ottoman.model
.
Also there are start
, close
, connect
, getDefaultInstance
, getCollections
functions are available for Ottoman default instance.
Multiple Ottoman instances
Sometimes it is necessary to use different databases in the same application for some reason, it is not the common case but it is perfectly probable that at some point you will come across this use case. Ottoman provides multiple instance creation to solve this requirement, let's see how it works:
So far we have used the default instance to connect, create the models, and even to start the application through the autonomous functions connect
, model
, start
. These functions use the instance that Ottoman creates by default on the back to facilitate its handling, in this way the developer does not have to control the reference to the app instance. This works well and simplifies the work a lot, but sometimes it is necessary to use several databases in the same application, which Ottoman solves through multiple instances
let's see an example:
- Create the instance and connect it to the DB
import {Ottoman} from 'ottoman';
const ottoman = new Ottoman ();
await ottoman.connect('couchbase://localhost/travel-sample@admin:password')
- Create the models and start Ottoman.
import {Ottoman} from 'ottoman';
const main = async () => {
const ottoman = new Ottoman ();
await ottoman.connect('couchbase://localhost/travel-sample@admin:password')
const User = ottoman.model ('User', schema)
await ottoman.start()
}
main();
With these simple steps, we create an Ottoman application as simple as possible.
If we want to have multiple instances, we only have to repeat the steps described above, the code to create 2 instances would be left in this way:
import {Ottoman} from 'ottoman';
const main = async () => {
const ottoman1 = new Ottoman ();
await ottoman1.connect('couchbase://localhost/travel-sample@admin:password');
const ottoman2 = new Ottoman ();
await ottoman2.connect('couchbase://localhost/other-bucket@admin:password');
// After connect you can create an explicitly Model from a given instance
// Creating UserModel from ottoman1
const UserModel = ottoman1.model ('User', {name: String});
// Creating CatModel from ottoman2
const CatModel = ottoman2.model ('Cat', {age: Number});
}
main();
You can create as many Ottoman instances as you need, there is not a defined limit.
When working with multiple instances avoid using the standalone functions (connect
, model
, start
, and close
), the recommended way is to use the desired Ottoman instance as a prefix ensuring that each component is registered in the correct instance.
Example:
// working with multiple instances
await ottoman1.connect('couchbase://localhost/travel-sample@admin:password');
await ottoman2.connect('couchbase://localhost/other-bucket@admin:password');
// not recommended
const User = model(User, schema);
// Recommended (register explicitly model User to the ottoman2 Instance)
const User = ottoman2.model(User, schema);
Closing Connections
import { Ottoman } from 'ottoman';
const ottoman1 = new Ottoman();
await ottoman1.connect('couchbase://localhost/travel-sample@admin:password');
// Closing connection1
ottoman1.close();
// Or just call the `close` function to close the default ottoman instance connection.
// In this case, the `ottoman1` connection will be closed
close();
::: tip Always remember to close your connections. :::
Not Using Scopes/Collections
If you don't want to use the scopes and collections approach, set the ottoman instances this way:
import { Ottoman } from "./ottoman";
const ottoman = new Ottoman({ collectionName: '_default' });
This way Ottoman will store all your data in a bucket.
Bootstrapping
The Ottoman class will provide three main methods to bootstrap the app:
ensureCollections
will attempt to create collections and scopes for each modelensureIndexes
will attempt to create all indexes defined in the schema definition- start method is just a shortcut to run
ensureCollections
andensureIndexes
Notice: It's not required to execute the start
method for Ottoman work.
Setting Environment Variables
Ottoman provides a set
function to help you define environment variables.
The next example will show how to set debug mode:
import { set } from 'ottoman';
// Setting Ottoman in debuggin mode
set('DEBUG', true);
Remember: You must define your environment variables at the very beginning.
Events
Ottoman will enable you to handle some events:
IndexOnline
Event IndexOnline
This event will be trigger when the Ottoman's instance N1QL indexes are online to be used.
usage:
import { Ottoman } from 'ottoman';
const ottoman = new Ottoman();
const main = async () => {
await ottoman.connect(...)
... // do some stuff
await ottoman.start(); // Ottoman start will attempt to create scope/collections and indexes
// sometimes you will need to use the `IndexOnline` event
// to ensure the indexes were built and all the documents were indexed
// to can accept queries.
ottoman.on('IndexOnline', (err) => {
if (!err) {
// this way you can start your web-server with your database ready to work.
app.listen(4000);
}
console.log(err);
})
}
Notice: IndexOnline
event will depend on start
or ensureIndexes
been called to be executed.
Helper Functions
Ottoman provides these helpers functions:
- dropBucket drops a bucket from the cluster.
- dropScope drops a scope from a bucket.
- dropCollection drops a collection from a scope in a bucket.
Next Up
Great job! Now we're connected, let's take a look at Schemas.