Install and Start Using the Ottoman ODM with Couchbase Server

    +
    Installing the Ottoman ODM & a Hello World program.

    Ottoman is an open-source Object Document Mapper(ODM) library, built for Node.js and Couchbase, that aspires to give developers an even better experience when building modern applications.

    Ottoman ODM adds an abstraction layer over Couchbase Node.js SDK and significantly reduces the level of boilerplate needed during application development. It provides features such as the ability to define document schemas and perform validations on your data in a NoSQL landscape — which is inherently schema-less or schema-flexible by nature.

    Whether you are building your application with JavaScript or TypeScript, Ottoman will work seamlessly with either.

    Ottoman fully supports the Scopes and Collections features introduced in Couchbase Sever 7.0. We recomend familiarizing yourself with these concepts before proceeding with this guide.

    For a full feature comparison between Ottoman ODM and the Couchbase Node.js SDK you can read more here.

    Creating a New Ottoman Project

    Creating a new Ottoman project is as easy as making a directory and initializing it with npm. The next two commands will do that for us. Open up a terminal and run the following command:

    $ mkdir node-ottoman-project && cd $_

    The command above will create our directory and change the current working directory.

    $ npm init -y

    If a directory does not already have a package.json at its root, this means it is not initialized. The command above will accomplish this.

    Note: We have used the -y flag to take the initialization defaults. To change any of these defaults, just open the package.json and manually make any changes.

    {
      "name": "node-ottoman-project",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }

    Compatibility

    Before installing Ottoman, you will need to ensure that you meet the minimum requirements shown in the compatibility table below:

    Ottoman Node.js Couchbase SDK Couchbase Server

    ^2.0.0

    ^8.0.0

    ^3.2.2

    ^6.5.0

    Node.js SDK 4.x is currently not supported by the Ottoman ODM. We intend to support version 4.x of the Node.js SDK in a future Ottoman release.

    Couchbase Capella

    Since Ottoman ODM uses Node.js SDK 3.2.x internally, it is fully compatible with Couchbase Capella, our fully-hosted database-as-a-service.

    Note, Capella is offered as a fully provisioned service, so the underlying version of Couchbase Server changes over time. For this reason, compatibility information between Capella and the SDK is available on the Capella compatibility page.

    Installing the Ottoman ODM

    $ npm install ottoman --save

    This will download the latest Ottoman ODM, and add a dependency to your package.json.

    Hello Ottoman

    In the directory we have just created, named node-ottoman-project, let’s create an empty file named index.js. At this point we want to transition from the terminal to your code editor of choice. We will walk through, step by step, adding code to enable us to connect to a bucket, add a document, and retrieve it. First, import Ottoman in your new index.js file:

    const { Ottoman, Schema, SearchConsistency } = require('ottoman')
    const ottoman = new Ottoman()

    For the purposes of this example we will create an async function called main under our imports. This will contain all our example code going forward.

    async function main() {
      // your code goes here...
    }

    Now, let’s create a connection to our database.

    • Couchbase Capella

    • Local Couchbase Server

    To connect to Couchbase Capella, be sure to set up credentials for your database by following these steps. Capella requires mandatory use of TLS (Transport Layer Security), therefore a security certificate must be provided when connecting.

    If you don’t already have your security certificate, you can download it from the Capella UI:

    1. Go to the "Clusters" section, in the left hand navigation.

    2. Click on the relevant cluster.

    3. Select the "Connect" tab and scroll down to the "Security Certificate" section.

    4. Download the "Root Certificate".

      // Replace the following connection details with your own
      const endpoint = 'cb.<your-endpoint>.cloud.couchbase.com'
      const cloudRootCertificate = './cert.pem'
      const username = 'username'
      const password = 'Password1!'
      const bucketName = 'travel-sample'
    
      const connection = await ottoman.connect({
        connectionString: `couchbases://${endpoint}`,
        username: username,
        password: password,
        bucketName: bucketName,
        trustStorePath: cloudRootCertificate,
        kvTimeout: 10000, // milliseconds
      })
    If your application is running in a constrained network environment (such as your laptop), you may experience timeout issues. To mitigate this, you can tweak your connection settings to account for any latency or bandwidth issues that may arise.

    Couchbase Capella uses Roles to control user access to database resources. For the purposes of this example, we can use the Organization Owner role automatically assigned to a user account during installation of the Capella cluster. This role grants full access to a Capella organization, including full data access to all projects and clusters. In a production scenario, we strongly recommend setting up users with more granular access roles as a best practice.

    var connection = await ottoman.connect({
      connectionString: 'couchbase://localhost',
      bucketName: 'travel-sample',
      username: 'Administrator',
      password: 'password',
    })

    Note that you can also use the below shorthand when creating a connection:

    // Alternate connect syntax
    connection = await ottoman.connect(
      'couchbase://localhost/travel-sample@Administrator:password'
    )

    Couchbase uses Role Based Access Control (RBAC) to control access to resources. For the purposes of this example, we are connecting to Couchbase using the Full Admin role created during the installation of our Couchbase Server.

    Since we are running this locally, we will use the Couchbase alias for localhost.

    If you are not working with the travel-sample data bucket, substitute travel-sample with your bucket name.

    Schemas, Models, and Documents

    Everything in Ottoman starts with a Schema. A Schema maps to a Collection and defines the shape of the documents within that Collection.

    Models in Ottoman are simply constructors compiled from Schema definitions, which help you to easily create, read, update, and delete documents in your Couchbase database. An instance of a model is called a Document.

    First, we’ll define a schema to represent the shape of our data:

    const airlineSchema = new Schema({
      type: String,
      id: Number,
      name: String,
      callsign: String,
      iata: String,
      icao: String,
    })

    Now, we call the model() constructor to build the model based on the schema we just created.

    const Airline = connection.model('airline', airlineSchema, {
      scopeName: 'inventory',
    })

    Notice that the constructor takes arguments like the collection name, schema, and model options. In the model options we can define properties such as scopeName to tell Ottoman which Scope we want to store our data in. In this case we are storing our data in the inventory Scope.

    Before performing any database actions we must call the start() function. This will ensure that the relevant scopes, collections, and indexes have been created in Couchbase.

    await connection.start()

    To persist the data in Couchbase we will need to create a Document object and subsequently call the save() function:

    const airlineDoc = new Airline({
      type: 'airline',
      id: 8091,
      callsign: 'CBS',
      name: 'Couchbase Airways 1',
      icao: 'CBICAO',
      iata: 'CBIATA',
    })
    
    // Persist the airline document we just created in the database
    await airlineDoc.save()
    console.log(`Success: airline ${airlineDoc.name} added!\n`)

    Alternatively, we can also use the create() function to create and save the document:

    await Airline.create({
      type: 'airline',
      id: 8092,
      callsign: 'CBS2',
      name: 'Couchbase Airways 2',
      icao: 'CBICAO2',
      iata: 'CBIATA2',
    })

    Querying Documents

    Querying documents is easy with Ottoman, thanks to the underlying Query Builder built into the ODM. The Query Builder can handle many complex operations supported by Couchbase and N1QL.

    Documents can be retrieved using model helper methods such as find(), findById(), or findOne(). For this example, let’s retrieve one of the airline documents we saved previously with find(), and filter by callsign:

    const result = await Airline.find(
      { callsign: 'CBS' },
      { consistency: SearchConsistency.LOCAL }
    )
    console.log('Query Result: ', result.rows)

    Finally, add this last code snippet after the main function:

    main()
      .catch((error) => console.log('ERR:', error))
      .finally(process.exit)

    Now we can run our code using the following command:

    $ node index.js

    The results you should expect are as follows:

    Success: airline Couchbase Airways 1 added!
    
    Query Result:  [
      _Model {
        callsign: 'CBS',
        iata: 'CBIATA',
        icao: 'CBICAO',
        id: 'airline_8091',
        name: 'Couchbase Airways 1',
        type: 'airline'
      }
    ]

    Full Example

    If you want to copy and paste to run the full example, here it is:

    • Couchbase Capella

    • Local Couchbase Server

    'use strict'
    
    const { Ottoman, Schema, SearchConsistency } = require('ottoman')
    const ottoman = new Ottoman()
    
    async function main() {
      // Replace the following connection details with your own
      const endpoint = 'cb.<your-endpoint>.cloud.couchbase.com'
      const cloudRootCertificate = './cert.pem'
      const username = 'username'
      const password = 'Password1!'
      const bucketName = 'travel-sample'
    
      const connection = await ottoman.connect({
        connectionString: `couchbases://${endpoint}`,
        username: username,
        password: password,
        bucketName: bucketName,
        trustStorePath: cloudRootCertificate,
        kvTimeout: 10000, // milliseconds
      })
    
      const airlineSchema = new Schema({
        type: String,
        id: Number,
        name: String,
        callsign: String,
        iata: String,
        icao: String,
      })
    
      const Airline = connection.model('airline', airlineSchema, {
        scopeName: 'inventory',
      })
    
      await connection.start()
    
      const airlineDoc = new Airline({
        type: 'airline',
        id: 8091,
        callsign: 'CBS',
        name: 'Couchbase Airways 1',
        icao: 'CBICAO',
        iata: 'CBIATA',
      })
    
      // Persist the airline document we just created in the database
      await airlineDoc.save()
      console.log(`Success: airline ${airlineDoc.name} added!\n`)
    
      await Airline.create({
        type: 'airline',
        id: 8092,
        callsign: 'CBS2',
        name: 'Couchbase Airways 2',
        icao: 'CBICAO2',
        iata: 'CBIATA2',
      })
    
      const result = await Airline.find(
        { callsign: 'CBS' },
        { consistency: SearchConsistency.LOCAL }
      )
      console.log('Query Result: ', result.rows)
    
      // cleanup
      await Airline.removeById(8092).catch((e) => undefined)
    }
    
    main()
      .catch((error) => console.log('ERR:', error))
      .finally(process.exit)
    'use strict'
    
    const { Ottoman, Schema, SearchConsistency } = require('ottoman')
    const ottoman = new Ottoman()
    
    async function main() {
      var connection = await ottoman.connect({
        connectionString: 'couchbase://localhost',
        bucketName: 'travel-sample',
        username: 'Administrator',
        password: 'password',
      })
    
      // Alternate connect syntax
      connection = await ottoman.connect(
        'couchbase://localhost/travel-sample@Administrator:password'
      )
    
      const airlineSchema = new Schema({
        type: String,
        id: Number,
        name: String,
        callsign: String,
        iata: String,
        icao: String,
      })
    
      const Airline = connection.model('airline', airlineSchema, {
        scopeName: 'inventory',
      })
    
      await connection.start()
    
      const airlineDoc = new Airline({
        type: 'airline',
        id: 8091,
        callsign: 'CBS',
        name: 'Couchbase Airways 1',
        icao: 'CBICAO',
        iata: 'CBIATA',
      })
    
      // Persist the airline document we just created in the database
      await airlineDoc.save()
      console.log(`Success: airline ${airlineDoc.name} added!\n`)
    
      await Airline.create({
        type: 'airline',
        id: 8092,
        callsign: 'CBS2',
        name: 'Couchbase Airways 2',
        icao: 'CBICAO2',
        iata: 'CBIATA2',
      })
    
      const result = await Airline.find(
        { callsign: 'CBS' },
        { consistency: SearchConsistency.LOCAL }
      )
      console.log('Query Result: ', result.rows)
    
      // cleanup
      await Airline.removeById(8092).catch((e) => undefined)
    }
    
    main()
      .catch((error) => console.log('ERR:', error))
      .finally(process.exit)

    Additional Resources

    To learn more about Ottoman ODM you can head over to the official Ottoman page. You can also find more in-depth information on some of the topics we touched on:

    If you are evaluating whether to use Ottoman in your next project, the FAQs here should also answer some questions.

    Links to each release are to be found in the individual release notes.

    Couchbase welcomes community contributions to the Ottoman ODM. The Ottoman ODM source code is available on GitHub.