{"id":10947,"date":"2021-03-30T09:13:45","date_gmt":"2021-03-30T16:13:45","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=10947"},"modified":"2025-06-13T23:42:25","modified_gmt":"2025-06-14T06:42:25","slug":"introduction-to-ottoman-with-couchbase","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/","title":{"rendered":"Introduction to Ottoman With Couchbase"},"content":{"rendered":"<p><span style=\"font-weight: 400\">Ottoman is an Object Data Modeler (ODM) for Couchbase&#8217;s Node.js SDK providing JSON schema and validation for NoSQL.<\/span><\/p>\n<h2>Why Use an ODM for Couchbase<\/h2>\n<p><span style=\"font-weight: 400\">With Ottoman, you declare schema in your code. Although Couchbase has no schema enforcement for your documents, most applications need some level of schema even in NoSQL. We will explore how to achieve schema and validation in NoSQL using Ottoman and Couchbase. <\/span><\/p>\n<p><span style=\"font-weight: 400\">It&#8217;s important to validate that documents meet certain requirements before persisting. Although Ottoman creates an abstraction over the Couchbase SDK, the benefits outweigh the drawbacks. A developer creates a lot of logic around creating and updating documents, writing pre\/post lifecycle, working with data structures, and validation.<\/span><\/p>\n<h3>NoSQL Database and Schema Design<\/h3>\n<p class=\"q-text qu-display--block\">An ODM serves a similar role in NoSQL as it does in a relational database, but with additional benefits. Couchbase doesn&#8217;t enforce validation as it is schema-flexible. Ottoman can perform certain checks are being made as your applications persist data. We can warn and error against unwanted data types and formats for individual fields by defining schema and models for various document types.<\/p>\n<p class=\"q-text qu-display--block\">Your server application code is where a great place to apply business logic and validation. <span style=\"font-weight: 400\">Ottoman&#8217;s goal is to provide a better development experience along with giving you control over schema and validation while using Couchbase with Node. We want to give developers a reliable tool to build systems that are easy to design, maintain, and scale.<\/span><\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/03\/Intro-Ottoman-One.gif\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-10948\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/03\/Intro-Ottoman-One.gif\" alt=\"Object Data Mapping in Node.js with Ottoman for Couchbase\" width=\"1371\" height=\"703\" \/><\/a><\/p>\n<h2><span style=\"font-weight: 400\">Couchbase is a NoSQL Database<\/span><\/h2>\n<p><span style=\"font-weight: 400\">Couchbase Server is a schema-less document database, categorized as a NoSQL datastore, it&#8217;s not the best description as Couchbase uses a variant of SQL for querying called <a href=\"https:\/\/docs.couchbase.com\/server\/current\/getting-started\/try-a-query.html\">N1QL<\/a>. Just because a schema is not strictly enforced in NoSQL databases like Couchbase does not mean you should not enforce it.<\/span><\/p>\n<p><span style=\"font-weight: 400\">With Couchbase, you get the benefits of enterprise-level scaling, clustered nodes, and the ability to store and retrieve data in a JSON format.<\/span><\/p>\n<p><span style=\"font-weight: 400\">If you are familiar with Mongoose, an ODM for MongoDB, you will feel pretty comfortable with Ottoman as they have many overlapping features because they both are made for NodeJS and used to model and persist data to a JSON document-oriented key-value database.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Document vs Relational <\/span><span style=\"font-weight: 400\">Database<\/span><\/h2>\n<p><span style=\"font-weight: 400\">As we explore we will explore Couchbase and NoSQL Database and Schema Design, we will first look at how a document data structure differs from a relational database design, in the example below, you will see a side-by-side comparison of data that represents a Hotel. <\/span><\/p>\n<p><span style=\"font-weight: 400\">On the left, we have a document that can store phone numbers in an array allowing us to store multiple phone numbers for a single hotel. To do this in a relational database you would most certainly need a new table and to maintain a relationship between the two using primary keys.<\/span><\/p>\n<p><span style=\"font-weight: 400;font-size: 12px\"><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/03\/Hotel_PhoneNumbers.gif\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-10952\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/03\/Hotel_PhoneNumbers.gif\" alt=\"NoSQL Documents vs. Relational Tables in SQL\" width=\"1371\" height=\"573\" \/><\/a><\/span><\/p>\n<p style=\"text-align: center\"><span style=\"font-size: 14px;font-weight: 400\">For more info on document modeling, check out the resources I have put together in a blog post: <a href=\"https:\/\/www.couchbase.com\/blog\/a-json-data-modeling-guide\/\">JSON Data Modeling Guide<\/a><\/span><\/p>\n<p><span style=\"font-weight: 400\">In Ottoman, we have many constructs to help define schema and models at the application level. Let&#8217;s go over some of the most important terms you need to know in Ottoman.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Types<\/span><\/h3>\n<p><span style=\"font-weight: 400\">As seen in pink in the image above, document properties with the name type in Couchbase are near equivalent to tables in a relational database. They can help to group different types of JSON documents together for indexing purposes. When using Secondary Indexes in Couchbase, we are able to index on any key in the documents. If only 100 of 10,000 documents in your database use a type of &#8216;hotel&#8217; and you normally want to search for hotels based on city or state, then you might want to build a Composite Secondary Index that only needs to search through those hundred documents where city or state equal a certain value. This is much more efficient than using, for instance, a Primary Index. <\/span><\/p>\n<p><span style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/learn\/services-and-indexes\/indexes\/indexing-and-query-perf.html\">Learn more about indexing in Couchbase<\/a>!<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Collections<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Similar to types in Couchbase 6.x (latest Couchbase major version at the time of writing) and not shown in the illustration above, collections will be favored in Couchbase 7 (<\/span><a href=\"https:\/\/www.couchbase.com\/downloads\/\"><span style=\"font-weight: 400\">already in beta<\/span><\/a><span style=\"font-weight: 400\">). In the case you were to use collections, you would simply not have a <strong>\u2018type\u2019<\/strong> property on each document and instead have the document assigned to a <strong>\u2018hotel\u2019<\/strong> collection.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Documents<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Comparable to rows of data in a relational database. Traditional RDBMS systems will reference related documents from other tables as seen in the illustration above. You can also do this with a JSON document, however; it is suggested to include that information as an embedded document when possible, as we see with the hotel document\u2019s phone number property which is an array of phone numbers. Albeit a very simple example of this, think if you had an address property that itself was another JSON object with many properties, you may think this needs to be given its own document, but nesting that information in the parent document in most cases is fine.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Fields<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Also known as attributes, are similar to columns in a relational database and with Ottoman, you can create field-level requirements as part of your schema.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Schema<\/span><\/h3>\n<p><span style=\"font-weight: 400\">While Couchbase is schema-less or schema-flexible, we can still enforce structure at the application level for our documents.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Model<\/span><\/h3>\n<p><span style=\"font-weight: 400\">A constructor method that takes a schema and creates an instance of a document equivalent to a single record in a relational database. This document instance can be constructed and then persisted to Couchbase by Ottoman <a href=\"https:\/\/v2.ottomanjs.com\/guides\/model.html#constructing-documents\">using the <code>save()<\/code> method<\/a>.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Getting Started<\/span><\/h2>\n<p>Let&#8217;s get started creating a demo application that we can use to get familiar with Ottoman as an object document mapper.<\/p>\n<h3><span style=\"font-weight: 400\">Couchbase Installation<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Before we get started, let\u2019s set up Couchbase. <\/span><\/p>\n<p><span style=\"font-weight: 400\">You can choose from <\/span><b>one of the following options<\/b><span style=\"font-weight: 400\"> (we are using option #1 for this article):<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/developer.couchbase.com\/docker-image-manual-cb65\/\"><span style=\"font-weight: 400\">Install Couchbase Server using Docker<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Download Couchbase specific for your OS from the <\/span><a href=\"https:\/\/www.couchbase.com\/downloads\/\"><span style=\"font-weight: 400\">Couchbase Website<\/span><\/a><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400\">Let\u2019s navigate through some of the basics of Ottoman by implementing a model that represents data for a simplified airline example keeping in tradition with Couchbase\u2019s Travel-Sample dataset.<\/span><\/p>\n<p><span style=\"font-weight: 400\">I am using Visual Studio Code, NodeJS v12.14, and NPM 6.14.8, so you will need to have Node.js installed on your machine. next, we will create a blank project and get started writing some code.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Initializing our Project with NPM<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Create a directory, initialize our project, install Ottoman.js and open in VS Code<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Create directory, initialize project, install Ottoman &amp; open in VS Code\">mkdir intro-ottoman &amp;&amp; cd $_ &amp;&amp; npm init -y &amp;&amp; npm i ottoman &amp;&amp; touch createAirline.js &amp;&amp; code .<\/pre>\n<p><span style=\"font-weight: 400\">Open the terminal in your editor of choice, I have added a command at the end that will open in VS Code.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Connection to Couchbase with Ottoman<\/span><\/h3>\n<p><span style=\"font-weight: 400\">We will start working out of the file <code>.\/createAirline.js<\/code><\/span> <span style=\"font-weight: 400\">under the project root and add the following (based on the default configuration):<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Connecting to Couchbase in Ottoman\">const { Ottoman, model, Schema } = require('ottoman')\r\n\r\nconst ottoman = new Ottoman({collectionName: '_default'});\r\nottoman.connect({\r\n    connectionString: 'couchbase:\/\/localhost',\r\n    bucketName: 'travel',\r\n    username: 'Administrator',\r\n    password: 'password'\r\n});<\/pre>\n<p><span style=\"font-weight: 400\">Together this imports the ottoman package and specifies the default collection (Couchbase Server 6.x style)<\/span><\/p>\n<h3>Ottoman Schema and Models<\/h3>\n<p><span style=\"font-weight: 400\">Models are fancy constructors compiled from Schema definitions. An instance of a model is called a document. Models in Ottoman help you to easily create, read, update, and delete documents in your Couchbase database.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Creating an Ottoman model comprises of a few things:<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Defining a Document Schema<\/span><\/h3>\n<p><span style=\"font-weight: 400\">A schema defines document properties through an object where the key name corresponds to the property name in the collection.<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Create an Airline Schema\">const airlineSchema = new Schema({\r\n   callsign: String, \r\n   country: String, \r\n   name: String \r\n})<\/pre>\n<p><span style=\"font-weight: 400\">Here we define three properties <strong>(callsign, country, name)<\/strong> within our schema, all of type <\/span><b>String.<\/b> By specifying a type for each of our model properties, <span style=\"font-weight: 400\">\u00a0maps to an internal validator that will be triggered when the model is saved to the database and fail if the data type of the value is not of type <\/span><b>String<\/b><span style=\"font-weight: 400\">.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The following Schema Types are permitted:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/schema.html#string\"><span style=\"font-weight: 400\">String<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/schema.html#number\"><span style=\"font-weight: 400\">Number<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/schema.html#array\"><span style=\"font-weight: 400\">Array<\/span><\/a><span style=\"font-weight: 400\">\u00a0<\/span><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/schema.html#boolean\"><span style=\"font-weight: 400\">Boolean<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/schema.html#date\"><span style=\"font-weight: 400\">Date<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/embedtype.html\"><span style=\"font-weight: 400\">EmbedType<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/mixedtype.html\"><span style=\"font-weight: 400\">MixedType<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/v2.ottomanjs.com\/classes\/referencetype.html\"><span style=\"font-weight: 400\">ReferenceTypes<\/span><\/a><\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400\">Defining a Document Model<\/span><\/h3>\n<p><span style=\"font-weight: 400\">We need to call the model constructor on the ottoman instance and pass it the name of the collection and a reference to the schema definition.<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Create Airline Model\">const Airline = ottoman.model('Airline', airlineSchema)<\/pre>\n<p><span style=\"font-weight: 400\">When you call the <\/span><a href=\"https:\/\/v2.ottomanjs.com\/classes\/ottoman.html#model\"><span style=\"font-weight: 400\">model()<\/span><\/a><span style=\"font-weight: 400\"> function it creates a copy of the schema and compiles the model for you.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Let\u2019s also give the <\/span><code>airlineSchema<\/code><span style=\"font-weight: 400\"> a phone number property. We can add a validation function that will ensure that the value is a valid phone number. Replace the <code>airlineSchema<\/code> section with these three blocks of code:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Add Validator to Airline Schema\">const regx = \/^(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$\/\r\n  if(value &amp;&amp; !value.match(regx)) {\r\n    throw new Error(`Phone Number ${value} is not valid`)\r\n  }\r\n}\r\naddValidators({ \r\n  phone: phoneValidator\r\n})\r\nconst airlineSchema = new Schema({ \r\n  callsign: String, \r\n  country: String, \r\n  name: String,\r\n  phone: [{ type: String, validator: 'phone'}]\r\n})<\/pre>\n<p><span style=\"font-weight: 400\">In the example above, I show you how to create a custom validator, we just happen to be using a regular expression as the check-in our validator, just understand that you could have any logic inside one of these validators and in a moment I will show you a nice trick to reduce our code down considering we are using regular expression for our matching of the phone numbers.<\/span><\/p>\n<h4>Defining Validators<\/h4>\n<p><strong>Validators<\/strong> registered with Ottoman (as we have done here with the <code class=\"language-JavaScript\">ottoman.addValidators()<\/code> method) will be called once for every value our document&#8217;s property has in the array. If the property did not have an array and instead just a single String value, the validator would only be run once. For this reason, I print out the problematic phone number if the validation fails.<\/p>\n<p><span style=\"font-weight: 400\">There is however an easier way to validate any document properties value so long as the check you are performing uses a regular expression. The <a href=\"https:\/\/v2.ottomanjs.com\/interfaces\/validatoroption.html#hierarchy'\">ValidatorOption<\/a> can take a <strong>regexp<\/strong> and <strong>message <\/strong>as an argument, so we can reduce our code down to:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Udate Schema to use Validator\">const regx = \/^(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$\/\r\nconst airlineSchema = new Schema({\r\ncallsign: String,\r\ncountry: String,\r\nname: String,\r\nphone: [{type: String, validator: {regexp: regx, message: 'phone invalid'}}]\r\n})<\/pre>\n<p><span style=\"font-weight: 400\">As you can see, we can do everything we were doing before inline when creating a new Schema. But don&#8217;t let this keep you from understanding how to create a custom validator, sometimes we need additional logic and that is why the first example is still something worth knowing.<\/span><\/p>\n<h3><b>Basic <\/b><b>Document Operations in NoSQL<\/b><\/h3>\n<p><span style=\"font-weight: 400\">Most of the basic operations are covered in our Ottoman V2 documentation, we will cover some of that here, but feel free to dive into our <a href=\"https:\/\/v2.ottomanjs.com\">Ottoman V2 (alpha) docs<\/a> and let us know if there is something that you cannot find or don\u2019t understand.<\/span><\/p>\n<h4><b>Create Documents<\/b><\/h4>\n<p><span style=\"font-weight: 400\">Considering the code that we already went over above that creates a schema, model, and validators. Saving a model and persisting it to the database is quite easy. Let\u2019s create a new <code>Airline<\/code> model using our <code>Schema<\/code> and then save\/persist it to the database.<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Create Document &amp; Save to Couchbase\">\/\/ Constructing our document\r\nconst cbAirlines = new Airline({\r\n  callsign: 'CBA',\r\n  country: 'United States',\r\n  name: 'Couchbase Airlines',\r\n  phone: ['321-321-3210', '321-123-1234']\r\n})\r\n\r\n\/\/ Persist the Couchbase Airlines document to Couchbase Server\r\nconst saveDocument = async() =&gt; {\r\n  try {\r\n    const result = await cbAirlines.save()\r\n    console.log(result)\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}\r\n\r\n\/\/ Ensure that all indexes exist on the server\r\nottoman.start()\r\n  \/\/ Next, let's save our document and print a success message \r\n  .then(async() =&gt; {\r\n    saveDocument()\r\n      .then(() =&gt; process.exit(0))\r\n      .catch((error) =&gt; console.log(error))\r\n  })<\/pre>\n<p>You may be wondering why we don&#8217;t just call the <code>saveDocument()<\/code> function on its own. Instead, we call it after the <code>ottoman.start()<\/code> is finished. The <code>start<\/code> method is a shortcut to run\u00a0<code>ensureCollections<\/code>\u00a0and\u00a0<code>ensureIndexes<\/code>. All you have to know for now is that this method makes sure that the proper Ottoman-related indexes have been created on Couchbase and this is important to ensure we can run things like the <code>find()<\/code> method and use tools like the <code>QueryBuilder<\/code> which we will get to at the end of the article.<\/p>\n<p><span style=\"font-weight: 400\">At this point, if you were to run all of the code we have written using Node, our document would be saved to the database:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Run createAirline with Node\">node createAirline.js<\/pre>\n<p><span style=\"font-weight: 400\">The result from this operation:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Result of Save Airline\">_Model {\r\n  callsign: 'CBA',\r\n  country: 'United States',\r\n  name: 'Couchbase Airlines',\r\n  phone: [ '321-321-3210', '321-123-1234' ],\r\n  id: '2384568f-f1e9-446e-97d1-cad697c40e76',\r\n  _type: 'Airline'\r\n}<\/pre>\n<p><span style=\"font-weight: 400\">The following fields are returned:<\/span><\/p>\n<ol>\n<li>Callsign, country, and name fields are all String, the most basic value we could have in a document.<\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\"><span style=\"font-weight: 400\"><span style=\"font-weight: 400\">The id field is auto-generated by Couchbase and is a unique key. The ID value is what you will use in any case to find a document with Ottoman in methods like <code>findByID<\/code> or <code>removeByID<\/code><\/span><\/span><\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The phone field is represented by an array and contains valid phone numbers.<\/span><\/li>\n<li>The <span style=\"font-weight: 400\">_type field can help us to organize our documents like a table does in a relational database, <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/7.0\/learn\/data\/scopes-and-collections.html\"><span style=\"font-weight: 400\">in Couchbase 7, we can use collections and scopes<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/li>\n<\/ol>\n<h5><span style=\"font-weight: 400\">Validation Errors<\/span><\/h5>\n<p><span style=\"font-weight: 400\">If we enter an invalid phone number and run <code>node createAirline.js<\/code> this file again, we would get an error message:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"CResult of Validation Error\">ValidationError: Phone Number 321-321-32xx is not valid<\/pre>\n<p>TIP: You can define your connection, schema, and models in separate files, export, and use them in other files. Create a new file named <code>airline-schema-model.js<\/code> and move our schema and model definition to it:<\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Export Schema and Model\">const { model, Schema } = require('ottoman')\r\n\r\nconst regx = \/^(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$\/\r\nconst airlineSchema = new Schema({ \r\n  callsign: String, \r\n  country: String, \r\n  name: String,\r\n  phone: [{type: String, validator: {regexp: regx, message: 'phone invalid'}}]\r\n})\r\n\r\n\/\/ Compile our model using our schema\r\nconst Airline = model('Airline', airlineSchema)\r\n\r\nexports.airlineSchema = airlineSchema;\r\nexports.Airline = Airline;<\/pre>\n<p>Now we can create a few new files, <code>findAirline.js<\/code>, <code>updateAirline.js<\/code>, and <code>removeAirline.js<\/code> and populate each file with the following:<\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Boilerplate for Multiple Files\">const { Ottoman } = require('ottoman')\r\nconst ottoman = new Ottoman({collectionName: '_default'});\r\nottoman.connect({\r\n    connectionString: 'couchbase:\/\/localhost',\r\n    bucketName: 'travel',\r\n    username: 'Administrator',\r\n    password: 'password'\r\n});\r\n\r\nconst { Airline } = require('.\/airline-schema-and-model')<\/pre>\n<p><span style=\"font-weight: 400\">This will help us to separate some of our code so we are not repeating it in each file and as we go over each of the CRUD operations we can just add some code to each file and our schema and model will already be imported.<\/span><\/p>\n<h4><span style=\"font-weight: 400\">Find Documents<\/span><\/h4>\n<p><span style=\"font-weight: 400\">Let\u2019s try to retrieve the record we saved to the database earlier. The model class exposes several static and instance methods to perform operations on the database. We will now try to find the record that we created previously using the find method and pass the callsign as the search term. Let&#8217;s create a new file named <code>findAirline.js<\/code> and we can add the following code:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Find Airline Document by Callsign\">\/\/ Find the Couchbase Airline document by Callsign from Couchbase Server\r\nconst findDocument = async() =&gt; {\r\n  try {\r\n    Airline.find({ callsign: { $like: 'CBA' } })\r\n    .then((result) =&gt; console.log(result.rows));\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}\r\n\r\nottoman.start()\r\n  .then(async() =&gt; {\r\n    findDocument()\r\n      .then(() =&gt; process.exit(0))\r\n      .catch((error) =&gt; console.log(error))\r\n  })<\/pre>\n<p><span style=\"font-weight: 400\">Find document result:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Reult of Find Airline by Callsign\">Query Result:  [\r\n  _Model {\r\n    _type: 'Airline',\r\n    callsign: 'CBA',\r\n    country: 'United States',\r\n    name: 'Couchbase Airlines',\r\n    phone: ['321-321-3210','321-123-1234'],\r\n    id: '971045ac-39d8-4e72-8c93-fdaac69aae31',\r\n  }<\/pre>\n<h4><span style=\"font-weight: 400\">Update Documents<\/span><\/h4>\n<p><span style=\"font-weight: 400\">Let\u2019s modify the record above by finding it using the callsign, which we can assume that callsign will be a unique field in our data, then we can update the document all in a single operation.<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Find Airline Document and Update\">\/\/ Update the Couchbase Airline document by Callsign from Couchbase Server\r\nconst findDocumentAndUpdate = async() =&gt; {\r\n  const newDocument = {\r\n    callsign: 'CBSA',\r\n    country: 'United States',\r\n    name: 'Couchbase Airways',\r\n    phone: ['321-321-3210','321-123-1234']\r\n  }\r\n  try {\r\n    let result = await Airline.findOneAndUpdate(\r\n      { callsign: { $like: 'CBA' } }, newDocument, { new: true }\r\n    )\r\n    console.log(result)\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}\r\n\r\nottoman.start()\r\n  .then(async() =&gt; {\r\n    findDocumentAndUpdate()\r\n      .then(() =&gt; process.exit(0))\r\n      .catch((error) =&gt; console.log(error))\r\n  })<\/pre>\n<p><span style=\"font-weight: 400\">Find document and update result:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Result of Find Airline Document and Update\">_Model {\r\n  _type: 'Airline',\r\n  callsign: 'CBSA',\r\n  country: 'United States',\r\n  id: '971045ac-39d8-4e72-8c93-fdaac69aae31',\r\n  name: 'Couchbase Airways',\r\n  phone: [ '321-321-3210', '321-123-1234' ]\r\n}<\/pre>\n<h4><span style=\"font-weight: 400\">Remove Documents<\/span><\/h4>\n<p><span style=\"font-weight: 400\">Ottoman has several methods that deal with removing documents: <\/span><a href=\"https:\/\/v2.ottomanjs.com\/classes\/document.html#remove\"><span style=\"font-weight: 400\">remove<\/span><\/a><span style=\"font-weight: 400\">, <\/span><a href=\"https:\/\/v2.ottomanjs.com\/classes\/model.html#static-removebyid\"><span style=\"font-weight: 400\">removeById<\/span><\/a><span style=\"font-weight: 400\"> and <\/span><a href=\"https:\/\/v2.ottomanjs.com\/classes\/model.html#static-removemany\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400\">removeMany<\/span><\/a><span style=\"font-weight: 400\">. Considering the many examples we have had so far, each of these should be very easy to understand how to use, so we will just provide a simple example here to show how to remove a document that we have already found using the <strong>find()<\/strong> method.<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Remove Airline Document by Id\">\/\/ Remove the Couchbase Airline document by ID from Couchbase Server\r\nconst removeDocument = async() =&gt; {\r\n  try {\r\n    await Airline.removeById('60e3f517-6a2a-41fe-be45-97081181d675')\r\n      .then((result) =&gt; console.log(result))\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}<\/pre>\n<p><span style=\"font-weight: 400\">Remove document result is a simple <a href=\"https:\/\/docs.couchbase.com\/nodejs-sdk\/current\/howtos\/concurrent-document-mutations.html\" target=\"_blank\" rel=\"noopener\">cas value<\/a>, used to track changes in Couchbase documents.<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Result of Remove Airline Document by Id\">{ cas: CbCas { '0': &lt;Buffer 00 00 2e 30 62 db 6c 16&gt; } }<\/pre>\n<h3><span style=\"font-weight: 400\">Middleware<\/span><\/h3>\n<p><span style=\"font-weight: 400\">We have already seen our middleware in action, our validator that we initially created can take advantage of middleware using functions that run at specific stages of a pipeline passing control during the execution of asynchronous functions.<\/span><\/p>\n<h4 id=\"the-available-hooks-are\">Available Hooks<\/h4>\n<ul>\n<li><code>validate<\/code><\/li>\n<li><code>save<\/code><\/li>\n<li><code>update<\/code><\/li>\n<li><code>remove<\/code><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">Example of Middleware (a.k.a. <a href=\"https:\/\/v2.ottomanjs.com\/guides\/schema.html#instance-methods\" target=\"_blank\" rel=\"noopener\">pre and post hooks<\/a>)<\/span><\/p>\n<p><span style=\"font-weight: 400\">Let\u2019s try an example by simply generating a log in the console before and after the creation (save) of a document, I&#8217;m going to create a new file called createWithHooks.js and most of the code will look familiar except I have added pre and post hooks that will just report to us the document name pre-save and document id post-save:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Create Document with Pre\/Post Save Hooks\">const { Ottoman } = require('ottoman')\r\nconst ottoman = new Ottoman({collectionName: '_default'});\r\nottoman.connect({\r\n    connectionString: 'couchbase:\/\/localhost',\r\n    bucketName: 'travel',\r\n    username: 'Administrator',\r\n    password: 'password'\r\n});\r\n\r\nconst { Airline, airlineSchema } = require('.\/airline-schema-and-model')\r\n\r\n\/\/ Plugins and Hooks are middleware, think lifecycle hooks!\r\nconst pluginLog = (airlineSchema) =&gt; {\r\n  airlineSchema.pre('save', (doc) =&gt; \r\n    console.log(`Doc: ${doc.name} about to be saved`)\r\n  )\r\n  airlineSchema.post('save', (doc) =&gt; \r\n    console.log(`Doc: ${doc.id} has been saved`)\r\n  )\r\n};\r\n\r\n\/\/ Our plugin must be registered before the model creation\r\nairlineSchema.plugin(pluginLog)\r\n\r\n\/\/ Constructing our document\r\nconst cbAirlines = new Airline({\r\n  callsign: 'UNITED',\r\n  country: 'United States',\r\n  name: 'United Airlines',\r\n  phone: ['321-321-3210', '321-123-1234']\r\n})\r\n\r\nconst saveDocument = async() =&gt; {\r\n  try {\r\n    \/\/ pre and post hooks will run\r\n    const result = await cbAirlines.save()\r\n    console.log(result)\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}\r\n\r\nottoman.start()\r\n  .then(async() =&gt; {\r\n    saveDocument()\r\n      .then(() =&gt; process.exit(0))\r\n      .catch((error) =&gt; console.log(error))\r\n  })<\/pre>\n<p><span style=\"font-weight: 400\">Save document result:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Result Save Airline Document\">Doc: United Airlines about to be saved\r\nDoc: 1316488a-98ba-4dbb-b0d7-ea6001a0bf57 has been saved\r\n_Model {\r\n  callsign: 'UNITED',\r\n  country: 'United States',\r\n  name: 'United Airlines',\r\n  phone: [ '321-321-3210', '321-123-1234' ],\r\n  id: '1316488a-98ba-4dbb-b0d7-ea6001a0bf57',\r\n  _type: 'Airline'\r\n}<\/pre>\n<p><span style=\"font-weight: 400\">We got our messages before and after the save. With validation, you can ensure certain document property values meet your criteria. Tapping into the lifecycle when the document is saved, updated, and removed also helped us gain a handle on Ottoman middleware!\u00a0<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Query Building<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Ottoman has a very rich API that handles many complex operations supported by Couchbase and N1QL. Our query builder behind the scenes creates your N1QL statements for you. When using Query Builder you have three options of which mode to use.\u00a0<\/span><\/p>\n<ol>\n<li><a href=\"https:\/\/v2.ottomanjs.com\/guides\/query-builder.html#build-a-query-by-using-parameters\" target=\"_blank\" rel=\"noopener\">Using parameters<\/a><\/li>\n<li><a href=\"https:\/\/v2.ottomanjs.com\/guides\/query-builder.html#build-a-query-by-using-access-functions\" target=\"_blank\" rel=\"noopener\">Access Functions<\/a><\/li>\n<li><a href=\"https:\/\/v2.ottomanjs.com\/guides\/query-builder.html#build-a-query-by-using-parameters-and-function-parameters\" target=\"_blank\" rel=\"noopener\">or using Parameters and Access Functions<\/a><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400\">In the next three examples, I&#8217;ll do the same thing using each of the three different QueryBuilder modes (params, access functions, and mixed-mode). Each example will:<br \/>\n<\/span><\/p>\n<ol>\n<li><span style=\"font-weight: 400\">Select <code>name<\/code> and <code>country<\/code> from Airline<\/span><\/li>\n<li>Where the <code>country<\/code> value is &#8220;United States&#8221;<\/li>\n<li>And LIMIT our results to 10<\/li>\n<\/ol>\n<p>Let&#8217;s first create a new file named: <code>findWithQueryBuilder.js<\/code>, and add the following code:<\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Find Airline Document with QueryBuilder\">const { Ottoman, Query } = require('ottoman')\r\nconst ottoman = new Ottoman({collectionName: '_default'});\r\n\r\nottoman.connect({\r\n    connectionString: 'couchbase:\/\/localhost',\r\n    bucketName: 'travel',\r\n    username: 'Administrator',\r\n    password: 'password'\r\n});\r\n\r\n\/* Replace with QueryBuilder Example *\/\r\n\r\nconst executeQuery = async(query) =&gt; {\r\n  try {\r\n    const result = await ottoman.query(query)\r\n    console.log('Query Result: ' , result)\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}\r\n\r\ngenerateQuery()\r\n  .then((query) =&gt; {\r\n    executeQuery(query)\r\n      .then(() =&gt; process.exit(0))\r\n  })\r\n  .catch((error) =&gt; console.log(error))<\/pre>\n<p>This file has a comment in the middle that says: <strong>&#8220;Replace with QueryBuilder Example&#8221;<\/strong>. We can just copy any of the following examples in this section into that area of the file.<\/p>\n<h4>Parameters<\/h4>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Demonstrate Query Builder using Parameters\">const generateQuery = async() =&gt; {\r\n  try {\r\n    const params = {\r\n      select : [\r\n        { $field: 'name' }, \r\n        { $field: 'country'}\r\n      ],\r\n      where: { $and: [\r\n        { country: {$eq: 'United States'}},\r\n        { _type: {$eq: 'Airline'}}\r\n      ] },\r\n      limit: 10\r\n    }\r\n    const query = new Query(params, '`travel`').build()\r\n    console.log('Query Generated: ', query)\r\n    return query\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}<\/pre>\n<h4>Access Functions<\/h4>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Demonstrate Query Builder using Access Functions\">const generateQuery = async() =&gt; {\r\n  try {\r\n    const query = new Query({}, '`travel`')\r\n      .select([\r\n        { $field: 'name' }, \r\n        { $field: 'country'}\r\n      ])\r\n      .where({ $and: [\r\n        { country: {$eq: 'United States'}},\r\n        { _type: {$eq: 'Airline'}}\r\n      ]})\r\n      .limit(10)\r\n      .build()\r\n      console.log('Query Generated: ', query)\r\n      return query\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}<\/pre>\n<h4>Mixed Mode<\/h4>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:javascript decode:true\" title=\"Demonstrate Query Builder using Mixed Mode (Parameters &amp; Access Functions)\">const generateQuery = async() =&gt; {\r\n  try {\r\n    const where =  { $and: [\r\n      { country: {$eq: 'United States'}},\r\n      { _type: {$eq: 'Airline'}}\r\n    ] }\r\n    \/\/ pass in our query as a condition expression\r\n    const query = new Query({ where }, '`travel`')\r\n      .select([\r\n        { $field: 'name' }, \r\n        { $field: 'country' }\r\n      ])\r\n      .limit(10)\r\n      .build()\r\n      console.log('Query Generated: ', query)\r\n      return query\r\n  } catch (error) {\r\n    throw error\r\n  }\r\n}<\/pre>\n<p><span style=\"font-weight: 400\">Once you have created a <code>generateQuery()<\/code> function using one of the mixed modes above, you would then need to asynchronously call <code>generateQuery<\/code> and <code>executeQuery<\/code> and the code for that like I said will work with many flavors of the above code:<\/span><\/p>\n<p><span style=\"font-weight: 400\">A result from any of the three modes above:<\/span><\/p>\n<pre class=\"theme:vs2012 font:ubuntu-mono font-size:14 line-height:24 top-margin:0 h-align:1 toolbar:1 toolbar-overlay:false lang:sh decode:true\" title=\"Result of Find Airline Document with QueryBuilder\">Query Generated:  SELECT name,country FROM default:`travel` WHERE (country=\"United States\" AND _type=\"Airline\") LIMIT 10\r\nQuery Result:  {\r\n  meta: {\r\n    requestId: '1514fa20-755e-49b3-bbfa-4ed75a1a40ee',\r\n    clientContextId: '0334862c79e727f8',\r\n    status: 'success',\r\n    signature: { country: 'json', name: 'json' },\r\n    profile: undefined,\r\n    metrics: {\r\n      elapsedTime: 6.219,\r\n      executionTime: 5.9619,\r\n      sortCount: undefined,\r\n      resultCount: 2,\r\n      resultSize: 106,\r\n      mutationCount: undefined,\r\n      errorCount: undefined,\r\n      warningCount: undefined\r\n    }\r\n  },\r\n  rows: [\r\n    { country: 'United States', name: 'United Airlines' },\r\n    { country: 'United States', name: 'Jet Blue Airlines' }\r\n  ]\r\n}<\/pre>\n<h3><span style=\"font-weight: 400\">Resources<\/span><\/h3>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li><a href=\"https:\/\/github.com\/couchbaselabs\/node-ottoman\" target=\"_blank\" rel=\"noopener\">GitHub Repository for Ottoman<\/a><\/li>\n<li><a href=\"https:\/\/ottomanjs.com\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400\">Documentation for Ottoman V2<\/span><\/a><\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/ottoman\" target=\"_blank\" rel=\"noopener\">Ottoman Package on NPM<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/httpJunkie\/intro-to-ottoman-v2\" target=\"_blank\" rel=\"noopener\">Intro to Ottoman with Couchbase source<\/a><\/li>\n<li><a href=\"https:\/\/www.couchbase.com\/blog\/a-json-data-modeling-guide\/\" target=\"_blank\" rel=\"noopener\">A JSON Data Modeling Guide<\/a><\/li>\n<li><a href=\"https:\/\/www.youtube.com\/watch?v=0FIVaXCh2E8\" target=\"_blank\" rel=\"noopener\">A Better Developer Experience with OttomanJS<\/a><\/li>\n<li><a href=\"https:\/\/docs.couchbase.com\/nodejs-sdk\/current\/hello-world\/start-using-sdk.html\" target=\"_blank\" rel=\"noopener\">Couchbase NodeJS SDK<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/_\/couchbase\" target=\"_blank\" rel=\"noopener\">Official Couchbase Docker Images<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400\">Conclusion<\/span><\/h3>\n<p><span style=\"font-weight: 400\">We&#8217;ve taken a guided tour through Ottoman gaining an understanding of many concepts. Schema, models, middleware, plugins, hooks, and query building.<\/span><\/p>\n<p><span style=\"font-weight: 400\">We explored how to connect to Couchbase in Ottoman. Defined schema and models. Touched on various fundamentals in NoSQL database schema and design. Finally, we walked through some of the most useful CRUD operations. All an effort to get you up to speed creating, reading, updating, and deleting documents in Couchbase via Ottoman.<\/span><\/p>\n<h4>Give Feedback and Contribute<\/h4>\n<p>I hope that this article has demystified why and how to use Ottoman and ODM for Couchbase. As shown you can use Ottoman for NoSQL database schema design, validation, and reduction of boiler-plate. Writing CRUD operations are made simple and aid in rapid development.<\/p>\n<p>If you have any questions about Ottoman, want to help contribute to this open-source project, or would just like to say hello, my name is Eric Bishard and I am the Developer Advocate here at Couchbase focusing on the Node.js and JavaScript developer experience, my DM&#8217;s are always open at <a href=\"https:\/\/twitter.com\/httpjunkie\">Twitter\/@httpJunkie<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ottoman is an Object Data Modeler (ODM) for Couchbase&#8217;s Node.js SDK providing JSON schema and validation for NoSQL. Why Use an ODM for Couchbase With Ottoman, you declare schema in your code. Although Couchbase has no schema enforcement for your [&hellip;]<\/p>\n","protected":false},"author":53002,"featured_media":11030,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1819,1822,10128,2201],"tags":[2312,1510],"ppma_author":[8922],"class_list":["post-10947","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-modeling","category-node-js","category-ottoman","category-tools-sdks","tag-document-database","tag-odm"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Ottoman: Object Data Modeler (ODM) for Couchbase Node.js SDK<\/title>\n<meta name=\"description\" content=\"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Introduction to Ottoman With Couchbase\" \/>\n<meta property=\"og:description\" content=\"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-03-30T16:13:45+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T06:42:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Eric Bishard\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@httpJunkie\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Eric Bishard\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/\"},\"author\":{\"name\":\"Eric Bishard\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/67d3a4b597e42370ccd34b715a6b1f4c\"},\"headline\":\"Introduction to Ottoman With Couchbase\",\"datePublished\":\"2021-03-30T16:13:45+00:00\",\"dateModified\":\"2025-06-14T06:42:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/\"},\"wordCount\":2756,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2021\\\/03\\\/feature-image-c.jpg\",\"keywords\":[\"document database\",\"odm\"],\"articleSection\":[\"Data Modeling\",\"Node.js\",\"Ottoman.js ODM\",\"Tools &amp; SDKs\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/\",\"name\":\"Ottoman: Object Data Modeler (ODM) for Couchbase Node.js SDK\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2021\\\/03\\\/feature-image-c.jpg\",\"datePublished\":\"2021-03-30T16:13:45+00:00\",\"dateModified\":\"2025-06-14T06:42:25+00:00\",\"description\":\"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2021\\\/03\\\/feature-image-c.jpg\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2021\\\/03\\\/feature-image-c.jpg\",\"width\":1200,\"height\":628,\"caption\":\"Introduction to Ottoman\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/introduction-to-ottoman-with-couchbase\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Introduction to Ottoman With Couchbase\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/admin-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/67d3a4b597e42370ccd34b715a6b1f4c\",\"name\":\"Eric Bishard\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=gb7d1d2580c41d35a21654fb1abe65d23\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=g\",\"caption\":\"Eric Bishard\"},\"description\":\"International speaker, blogging and advocating for the JavaScript, React, GraphQL and NoSQL community working as a Senior Developer Advocate for Couchbase.\",\"sameAs\":[\"https:\\\/\\\/www.reactstateofmind.com\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/eric-b\\\/\",\"https:\\\/\\\/x.com\\\/httpJunkie\"],\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/author\\\/eric-bishard\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Ottoman: Object Data Modeler (ODM) for Couchbase Node.js SDK","description":"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/","og_locale":"en_US","og_type":"article","og_title":"Introduction to Ottoman With Couchbase","og_description":"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.","og_url":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/","og_site_name":"The Couchbase Blog","article_published_time":"2021-03-30T16:13:45+00:00","article_modified_time":"2025-06-14T06:42:25+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg","type":"image\/jpeg"}],"author":"Eric Bishard","twitter_card":"summary_large_image","twitter_creator":"@httpJunkie","twitter_misc":{"Written by":"Eric Bishard","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/"},"author":{"name":"Eric Bishard","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/67d3a4b597e42370ccd34b715a6b1f4c"},"headline":"Introduction to Ottoman With Couchbase","datePublished":"2021-03-30T16:13:45+00:00","dateModified":"2025-06-14T06:42:25+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/"},"wordCount":2756,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg","keywords":["document database","odm"],"articleSection":["Data Modeling","Node.js","Ottoman.js ODM","Tools &amp; SDKs"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/","url":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/","name":"Ottoman: Object Data Modeler (ODM) for Couchbase Node.js SDK","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg","datePublished":"2021-03-30T16:13:45+00:00","dateModified":"2025-06-14T06:42:25+00:00","description":"Ottoman is an Object Data Modeler (ODM) for Couchbase\u2019s Node.js SDK providing JSON schema and validation for NoSQL. Learn why to use an ODM for Couchbase.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/03\/feature-image-c.jpg","width":1200,"height":628,"caption":"Introduction to Ottoman"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/introduction-to-ottoman-with-couchbase\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Introduction to Ottoman With Couchbase"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/67d3a4b597e42370ccd34b715a6b1f4c","name":"Eric Bishard","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=gb7d1d2580c41d35a21654fb1abe65d23","url":"https:\/\/secure.gravatar.com\/avatar\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=g","caption":"Eric Bishard"},"description":"International speaker, blogging and advocating for the JavaScript, React, GraphQL and NoSQL community working as a Senior Developer Advocate for Couchbase.","sameAs":["https:\/\/www.reactstateofmind.com","https:\/\/www.linkedin.com\/in\/eric-b\/","https:\/\/x.com\/httpJunkie"],"url":"https:\/\/www.couchbase.com\/blog\/author\/eric-bishard\/"}]}},"acf":[],"authors":[{"term_id":8922,"user_id":53002,"is_guest":0,"slug":"eric-bishard","display_name":"Eric Bishard","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/a316a2658772914defd259571b8cad18878eb23c9d0cc3a97dd803deca0c09ca?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/10947","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/53002"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=10947"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/10947\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/11030"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=10947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=10947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=10947"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=10947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}