{"id":3943,"date":"2017-09-01T07:00:21","date_gmt":"2017-09-01T14:00:21","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=3943"},"modified":"2025-06-13T20:15:10","modified_gmt":"2025-06-14T03:15:10","slug":"midwest-js-project-source-full-stack-node-development-available","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/","title":{"rendered":"Midwest JS Project Source on Full Stack Node Development, Available"},"content":{"rendered":"<p>Back in August I had participated in <a href=\"https:\/\/midwestjs.com\/\" target=\"_blank\" rel=\"noopener\">Midwest JS<\/a> located in\u00a0Minneapolis,\u00a0Minnesota. As you may know, I&#8217;m a huge fan of developing full stack applications with the JavaScript stack. This is exactly what I had presented on at the conference.<\/p>\n<p>My session was well attended and many developers were taught how to use Node.js with Couchbase to develop a RESTful API, and Angular as the client facing layer.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-3953 size-full\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/08\/midwestjs-couchbase.jpg\" alt=\"Couchbase at Midwest JS\" width=\"768\" height=\"558\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/08\/midwestjs-couchbase.jpg 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/08\/midwestjs-couchbase-300x218.jpg 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/08\/midwestjs-couchbase-20x15.jpg 20w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/p>\n<p>As promised, I am going to revisit the material I went over during the presentation so the concepts and code can be reproduced and expanded upon.<\/p>\n<p><!--more--><\/p>\n<p>Going forward, the assumption is that you&#8217;ve got <a href=\"https:\/\/www.couchbase.com\" target=\"_blank\" rel=\"noopener\">Couchbase Server<\/a>, <a href=\"https:\/\/nodejs.org\" target=\"_blank\" rel=\"noopener\">Node.js<\/a>, and the <a href=\"https:\/\/ionicframework.com\/\" target=\"_blank\" rel=\"noopener\">Ionic Framework<\/a> CLI installed and configured. Couchbase will be the NoSQL Database, Node.js will power our backend, and Ionic Framework will give us a web frontend powered by Angular.<\/p>\n<p>The project created at Midwest JS allowed you to store information about video game consoles and video games for various consoles. This demonstrated the use of CRUD as well as relationships between NoSQL documents and how Couchbase makes it easy.<\/p>\n<h2>Creating the Node.js with Couchbase NoSQL Backend<\/h2>\n<p>Before we can begin development, we need to create a new Node.js project. From the command line, execute the following:<\/p>\n<pre class=\"lang:default decode:true \">npm init -y\r\nnpm install couchbase express body-parser uuid cors --save<\/pre>\n<p>The above command will create a project\u00a0<strong>package.json<\/strong> file and install our project dependencies. The <code>cors<\/code> package will allow us to use Node and Angular locally on two different ports without getting cross origin resource sharing errors. The <code>uuid<\/code> package will allow us to generate unique strings for use as document keys. The <code>body-parser<\/code> package will allow us to send JSON data in HTTP requests. We&#8217;ll be using Express and Couchbase which explains the other two packages.<\/p>\n<p>Create an\u00a0<strong>app.js<\/strong> file within your project. It will contain all the source code for our Node.js application. As boilerplate, it should look like the following:<\/p>\n<pre class=\"lang:default decode:true \">var Couchbase = require(\"couchbase\");\r\nvar Express = require(\"express\");\r\nvar BodyParser = require(\"body-parser\");\r\nvar UUID = require(\"uuid\");\r\nvar Cors = require(\"cors\");\r\n\r\nvar app = Express();\r\nvar N1qlQuery = Couchbase.N1qlQuery;\r\n\r\napp.use(BodyParser.json());\r\napp.use(BodyParser.urlencoded({ extended: true }));\r\napp.use(Cors());\r\n\r\nvar cluster = new Couchbase.Cluster(\"couchbase:\/\/localhost\");\r\nvar bucket = cluster.openBucket(\"default\", \"\");\r\n\r\napp.get(\"\/consoles\", (request, response) =&gt; {});\r\napp.post(\"\/console\", (request, response) =&gt; {});\r\napp.post(\"\/game\", (request, response) =&gt; {});\r\napp.get(\"\/games\", (request, response) =&gt; {});\r\napp.get(\"\/game\/:id\", (request, response) =&gt; {});\r\n\r\nvar server = app.listen(3000, () =&gt; {\r\n    console.log(\"Listening on port \" + server.address().port + \"...\");\r\n});<\/pre>\n<p>Notice that we&#8217;ve imported each of the downloaded dependencies, initialized and configured Express, and connected to a Bucket in our Couchbase cluster.<\/p>\n<p>We will have five different RESTful API endpoints for this application.<\/p>\n<p>The first logical thing to do would be to create a video game console so we can add games to it. Take a look at the following endpoint code:<\/p>\n<pre class=\"lang:default decode:true \">app.post(\"\/console\", (request, response) =&gt; {\r\n    if(!request.body.title) {\r\n        return response.status(401).send({ \"message\": \"A `title` is required.\" });\r\n    }\r\n    var id = UUID.v4();\r\n    request.body.type = \"console\";\r\n    bucket.insert(id, request.body, (error, result) =&gt; {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>In the above logic, we are validating that a <code>title<\/code> exists in our request. If it does, we will generate a new id, assign a <code>type<\/code> to the data in our request, and insert it into Couchbase. The success or failure response of the insert will be returned to the client, which will eventually be an Angular application.<\/p>\n<p>To query for video game consoles, we&#8217;ll need to query based on the <code>type<\/code> property. For this reason, we&#8217;ll have to use a N1QL query rather than a lookup by id.<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/consoles\", (request, response) =&gt; {\r\n    var statement = \"SELECT `\" + bucket._name + \"`.*, META().id FROM `\" + bucket._name + \"` WHERE type = 'console'\";\r\n    var query = N1qlQuery.fromString(statement);\r\n    query.consistency(N1qlQuery.Consistency.REQUEST_PLUS);\r\n    bucket.query(query, (error, result) =&gt; {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>The N1QL query is nothing more than a simple <code>SELECT<\/code> statement that you&#8217;d find in SQL. After executing the query, we would return the response back to the client.<\/p>\n<p>This brings us to the actual video games. Things get a little more complex, but not more difficult.<\/p>\n<pre class=\"lang:default decode:true \">app.post(\"\/game\", (request, response) =&gt; {\r\n    if(!request.body.title) {\r\n        return response.status(401).send({ \"message\": \"A `title` is required.\" });\r\n    } else if(!request.body.cid) {\r\n        return response.status(401).send({ \"message\": \"A `cid` is required.\" });\r\n    }\r\n    var id = UUID.v4();\r\n    request.body.type = \"game\";\r\n    bucket.insert(id, request.body, (error, result) =&gt; {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>In the above endpoint logic, we plan to insert a new video game into the database. This is no different than what we saw when inserting a new video game console into the database. We are defining a <code>type<\/code> property, but we are also making sure a <code>cid<\/code> exists. The <code>cid<\/code> will be a console id which will allow us to establish a relationship with our data.<\/p>\n<p>When you have relationships, you have <code>JOIN<\/code> operations.<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/games\", (request, response) =&gt; {\r\n    var statement = \"SELECT game.title AS game_title, console.title AS console_title FROM `\" + bucket._name + \"` AS game JOIN `\" + bucket._name + \"` AS console ON KEYS game.cid WHERE game.type = 'game'\";\r\n    var query = N1qlQuery.fromString(statement);\r\n    query.consistency(N1qlQuery.Consistency.REQUEST_PLUS);\r\n    bucket.query(query, (error, result) =&gt; {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>In the above endpoint, we are doing another N1QL query, but this time we have a <code>JOIN<\/code> operation. It isn&#8217;t useful to return a <code>cid<\/code> when querying for video games, so we <code>JOIN<\/code> and replace that information with the console title of the other document.<\/p>\n<p>Likewise we have a similar query when trying to find a specific video game:<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/game\/:id\", (request, response) =&gt; {\r\n    if(!request.params.id) {\r\n        return response.status(401).send({ \"message\": \"An `id` is required.\" });\r\n    }\r\n    var statement = \"SELECT game.title AS game_title, console.title AS console_title FROM `\" + bucket._name + \"` AS game JOIN `\" + bucket._name + \"` AS console ON KEYS game.cid WHERE game.type = 'game' AND META(game).id = $id\";\r\n    var query = N1qlQuery.fromString(statement);\r\n    bucket.query(query, { \"id\": request.params.id }, (error, result) =&gt; {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>The alternative to using N1QL and <code>JOIN<\/code> operations would be to do two lookups based on id. There is nothing wrong with this practice, but in my opinion it is easier to just let the database take care of a <code>JOIN<\/code> rather than trying to <code>JOIN<\/code> via the application layer.<\/p>\n<p>This brings us to the client frontend.<\/p>\n<h2>Creating the Ionic Framework with Angular Frontend<\/h2>\n<p>As previously mentioned, this time around we are using Ionic Framework which uses a flavor of Angular. I chose this because I was feeling too lazy to create an attractive frontend with Bootstrap or Foundation.<\/p>\n<p>With the Ionic Framework CLI available, execute the following:<\/p>\n<pre class=\"lang:default decode:true \">ionic start pwa sidemenu<\/pre>\n<p>The above command will create a project called <strong>pwa<\/strong> using the Ionic Framework <code>sidemenu<\/code> template.<\/p>\n<p>The base template is useful, but it doesn&#8217;t have everything we need. We need to add a few pages to the application.<\/p>\n<p>Using the Ionic Framework generators, or manually, create a <strong>consoles<\/strong>, <strong>games<\/strong>, and <strong>game<\/strong> page. Each of these pages should have an HTML, SCSS, and TypeScript file and each page directory should be in the <strong>pages<\/strong> directory.<\/p>\n<p>Open the project&#8217;s\u00a0<strong>app\/app.component.ts<\/strong> file and make it look like the following:<\/p>\n<pre class=\"lang:default decode:true \">import { Component, ViewChild } from '@angular\/core';\r\nimport { Nav, Platform } from 'ionic-angular';\r\nimport { StatusBar } from '@ionic-native\/status-bar';\r\nimport { SplashScreen } from '@ionic-native\/splash-screen';\r\n\r\nimport { GamesPage } from '..\/pages\/games\/games';\r\nimport { ConsolesPage } from '..\/pages\/consoles\/consoles';\r\n\r\n@Component({\r\n  templateUrl: 'app.html'\r\n})\r\nexport class MyApp {\r\n  @ViewChild(Nav) nav: Nav;\r\n\r\n  rootPage: any = GamesPage;\r\n\r\n  pages: Array&lt;{title: string, component: any}&gt;;\r\n\r\n  constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) {\r\n    this.initializeApp();\r\n\r\n    \/\/ used for an example of ngFor and navigation\r\n    this.pages = [\r\n      { title: 'Games', component: GamesPage },\r\n      { title: 'Consoles', component: ConsolesPage }\r\n    ];\r\n\r\n  }\r\n\r\n  initializeApp() {\r\n    this.platform.ready().then(() =&gt; {\r\n      \/\/ Okay, so the platform is ready and our plugins are available.\r\n      \/\/ Here you can do any higher level native things you might need.\r\n      this.statusBar.styleDefault();\r\n      this.splashScreen.hide();\r\n    });\r\n  }\r\n\r\n  openPage(page) {\r\n    \/\/ Reset the content nav to have just this page\r\n    \/\/ we wouldn't want the back button to show in this scenario\r\n    this.nav.setRoot(page.component);\r\n  }\r\n}<\/pre>\n<p>Notice that we&#8217;ve imported\u00a0<code>GamesPage<\/code> and <code>ConsolesPage<\/code>, updated the <code>pages<\/code> array, and set the default root page as <code>GamesPage<\/code>. By doing this we&#8217;ve set up navigation and the default page when the application launches.<\/p>\n<p>To complete the setup, we also need to alter the project&#8217;s\u00a0<strong>app\/app.module.ts<\/strong> file. Make it look like the following:<\/p>\n<pre class=\"lang:default decode:true \">import { BrowserModule } from '@angular\/platform-browser';\r\nimport { ErrorHandler, NgModule } from '@angular\/core';\r\nimport { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';\r\nimport { HttpModule } from \"@angular\/http\";\r\n\r\nimport { MyApp } from '.\/app.component';\r\nimport { GamesPage } from '..\/pages\/games\/games';\r\nimport { GamePage } from '..\/pages\/game\/game';\r\nimport { ConsolesPage } from '..\/pages\/consoles\/consoles';\r\n\r\nimport { StatusBar } from '@ionic-native\/status-bar';\r\nimport { SplashScreen } from '@ionic-native\/splash-screen';\r\n\r\n@NgModule({\r\n  declarations: [\r\n    MyApp,\r\n    GamesPage,\r\n    GamePage,\r\n    ConsolesPage\r\n  ],\r\n  imports: [\r\n    BrowserModule,\r\n    HttpModule,\r\n    IonicModule.forRoot(MyApp),\r\n  ],\r\n  bootstrap: [IonicApp],\r\n  entryComponents: [\r\n    MyApp,\r\n    GamesPage,\r\n    GamePage,\r\n    ConsolesPage\r\n  ],\r\n  providers: [\r\n    StatusBar,\r\n    SplashScreen,\r\n    {provide: ErrorHandler, useClass: IonicErrorHandler}\r\n  ]\r\n})\r\nexport class AppModule {}<\/pre>\n<p>Notice that we&#8217;ve imported each of our new pages and added them to the <code>declarations<\/code> and <code>entryComponents<\/code> arrays of the <code>@NgModule<\/code> block.<\/p>\n<p>Now we can focus on the development of the application and connecting it to our API.<\/p>\n<p>Open the project&#8217;s\u00a0<strong>src\/pages\/games\/games.ts<\/strong> file and make it look like the following. We&#8217;re going to break down what is happening next.<\/p>\n<pre class=\"lang:default decode:true \">import { Component } from '@angular\/core';\r\nimport { NavController, ModalController } from 'ionic-angular';\r\nimport { Http, Headers, RequestOptions } from \"@angular\/http\";\r\nimport \"rxjs\/Rx\";\r\nimport { GamePage } from \"..\/game\/game\";\r\n\r\n@Component({\r\n    selector: 'page-games',\r\n    templateUrl: 'games.html'\r\n})\r\nexport class GamesPage {\r\n\r\n    public games: Array&lt;any&gt;;\r\n\r\n    public constructor(public navCtrl: NavController, private http: Http, private modalCtrl: ModalController) {\r\n        this.games = [];\r\n    }\r\n\r\n    public ionViewDidEnter() {\r\n        this.http.get(\"https:\/\/localhost:3000\/games\")\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                this.games = result;\r\n            });\r\n    }\r\n\r\n    public create() {\r\n        let gameModal = this.modalCtrl.create(GamePage);\r\n        gameModal.onDidDismiss(data =&gt; {\r\n            let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n            let options = new RequestOptions({ headers: headers });\r\n            this.http.post(\"https:\/\/localhost:3000\/game\", JSON.stringify(data), options)\r\n                .subscribe(result =&gt; {\r\n                    this.games.push({ \"game_title\": data.title, \"console_title\": \"\"});\r\n                });\r\n        });\r\n        gameModal.present();\r\n    }\r\n\r\n}<\/pre>\n<p>Within the <code>GamesPage<\/code> class, we have a public variable called <code>games<\/code>. Because it is public, it will be accessible via the HTML. It will contain all the games returned from the Node.js application.<\/p>\n<p>When the page loads, we want to query our endpoint. It is never a good idea to do this in the <code>constructor<\/code> method, so instead we use the <code>ionViewDidEnter<\/code> method. After issuing the request, the result is transformed into JSON and then loaded into our public variable.<\/p>\n<p>If we want to create a new game in our database, things are a little different. We are going to display a modal dialog and collect input.<\/p>\n<pre class=\"lang:default decode:true \">public create() {\r\n    let gameModal = this.modalCtrl.create(GamePage);\r\n    gameModal.onDidDismiss(data =&gt; {\r\n        let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n        let options = new RequestOptions({ headers: headers });\r\n        this.http.post(\"https:\/\/localhost:3000\/game\", JSON.stringify(data), options)\r\n            .subscribe(result =&gt; {\r\n                this.games.push({ \"game_title\": data.title, \"console_title\": \"\"});\r\n            });\r\n    });\r\n    gameModal.present();\r\n}<\/pre>\n<p>The <code>create<\/code> method will display our <code>GamePage<\/code> which will be in modal format. Any data entered in the form on the modal will be returned back to the <code>GamesPage<\/code> and sent via an HTTP request to the API.<\/p>\n<p>Before we take a look at\u00a0<code>GamePage<\/code>, let&#8217;s look at the HTML that powers <code>GamesPage<\/code>.<\/p>\n<pre class=\"lang:default decode:true\">&lt;ion-header&gt;\r\n    &lt;ion-navbar&gt;\r\n        &lt;button ion-button menuToggle&gt;\r\n            &lt;ion-icon name=\"menu\"&gt;&lt;\/ion-icon&gt;\r\n        &lt;\/button&gt;\r\n        &lt;ion-title&gt;Games&lt;\/ion-title&gt;\r\n        &lt;ion-buttons end&gt;\r\n            &lt;button ion-button icon-only (click)=\"create()\"&gt;\r\n                &lt;ion-icon name=\"add\"&gt;&lt;\/ion-icon&gt;\r\n            &lt;\/button&gt;\r\n        &lt;\/ion-buttons&gt;\r\n    &lt;\/ion-navbar&gt;\r\n&lt;\/ion-header&gt;\r\n\r\n&lt;ion-content padding&gt;\r\n    &lt;ion-list&gt;\r\n        &lt;button ion-item *ngFor=\"let game of games\"&gt;\r\n            {{game.game_title}}\r\n<span class=\"item-note\">{{game.console_title}}<\/span>\r\n&lt;\/button&gt; &lt;\/ion-list&gt; &lt;\/ion-content&gt;<\/pre>\n<p>In the above HTML, we are looping through our public <code>games<\/code> array. Each object in the array is rendered to the screen within a list. Angular does all the heavy lifting for us.<\/p>\n<p>Open the project&#8217;s\u00a0<strong>src\/pages\/game\/game.ts<\/strong> file and include the following:<\/p>\n<pre class=\"lang:default decode:true \">import { Component } from '@angular\/core';\r\nimport { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';\r\nimport { Http, Headers, RequestOptions } from \"@angular\/http\";\r\nimport \"rxjs\/Rx\";\r\n\r\n@IonicPage()\r\n@Component({\r\n    selector: 'page-game',\r\n    templateUrl: 'game.html',\r\n})\r\nexport class GamePage {\r\n\r\n    public consoles: Array&lt;any&gt;;\r\n    public input: any;\r\n\r\n    constructor(public navCtrl: NavController, public navParams: NavParams, public viewCtrl: ViewController, private http: Http) {\r\n        this.consoles = [];\r\n        this.input = {\r\n            \"cid\": \"\",\r\n            \"title\": \"\"\r\n        }\r\n    }\r\n\r\n    ionViewDidEnter() {\r\n        this.http.get(\"https:\/\/localhost:3000\/consoles\")\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                for(let i = 0; i &lt; result.length; i++) {\r\n                    this.consoles.push(result[i]);\r\n                }\r\n            });\r\n    }\r\n\r\n    public save() {\r\n        this.viewCtrl.dismiss(this.input);\r\n    }\r\n\r\n}<\/pre>\n<p>This modal logic is similar to what we&#8217;ve already seen. There will be a form that is bound to HTML and TypeScript. When the <code>ionViewDidEnter<\/code> triggers, we query for console information. This console information will eventually be used for a radio list that the user can select from.<\/p>\n<p>When the user selects the <code>save<\/code> method, the data bound in the public form is passed to the previous <code>GamesPage<\/code> page.<\/p>\n<p>The HTML that powers this modal, found in\u00a0<strong>src\/pages\/game\/game.html<\/strong> looks like this:<\/p>\n<pre class=\"lang:default decode:true \">&lt;ion-header&gt;\r\n    &lt;ion-navbar&gt;\r\n    &lt;ion-title&gt;New Game&lt;\/ion-title&gt;\r\n    &lt;\/ion-navbar&gt;\r\n&lt;\/ion-header&gt;\r\n\r\n&lt;ion-content padding&gt;\r\n    &lt;ion-list&gt;\r\n        &lt;ion-item&gt;\r\n            &lt;ion-label stacked&gt;Title&lt;\/ion-label&gt;\r\n            &lt;ion-input type=\"text\" [(ngModel)]=\"input.title\"&gt;&lt;\/ion-input&gt;\r\n        &lt;\/ion-item&gt;\r\n        &lt;ion-item&gt;\r\n            &lt;ion-label&gt;Console&lt;\/ion-label&gt;\r\n            &lt;ion-select [(ngModel)]=\"input.cid\"&gt;\r\n                &lt;ion-option *ngFor=\"let console of consoles\" value=\"{{ console.id }}\"&gt;{{ console.title }}&lt;\/ion-option&gt;\r\n            &lt;\/ion-select&gt;\r\n        &lt;\/ion-item&gt;\r\n        &lt;ion-item&gt;\r\n            &lt;button ion-button full (click)=\"save()\"&gt;Save&lt;\/button&gt;\r\n        &lt;\/ion-item&gt;\r\n    &lt;\/ion-list&gt;\r\n&lt;\/ion-content&gt;<\/pre>\n<p>We have a simple list that makes up our form. The form elements are bound to our TypeScript variable and the console information is looped through to populate an HTML <code>select<\/code> element.<\/p>\n<p>This brings us to the final page of the Angular frontend.<\/p>\n<p>Open the project&#8217;s\u00a0<strong>src\/pages\/consoles\/consoles.ts<\/strong> file and include the following:<\/p>\n<pre class=\"lang:default decode:true \">import { Component } from '@angular\/core';\r\nimport { NavController, NavParams, AlertController } from 'ionic-angular';\r\nimport { Http, Headers, RequestOptions } from \"@angular\/http\";\r\nimport \"rxjs\/Rx\";\r\n\r\n@Component({\r\n  selector: 'page-consoles',\r\n  templateUrl: 'consoles.html'\r\n})\r\nexport class ConsolesPage {\r\n\r\n    public consoles: Array&lt;any&gt;;\r\n\r\n    public constructor(public navCtrl: NavController, private http: Http, private alertCtrl: AlertController) {\r\n        this.consoles = [];\r\n    }\r\n\r\n    public ionViewDidEnter() {\r\n        this.http.get(\"https:\/\/localhost:3000\/consoles\")\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                this.consoles = result;\r\n            });\r\n    }\r\n\r\n    public create() {\r\n        let alert = this.alertCtrl.create({\r\n            title: 'Add Console',\r\n            inputs: [\r\n                {\r\n                    name: 'title',\r\n                    placeholder: 'Title'\r\n                },\r\n                {\r\n                    name: 'year',\r\n                    placeholder: 'Year'\r\n                }\r\n            ],\r\n            buttons: [\r\n                {\r\n                    text: 'Cancel',\r\n                    role: 'cancel',\r\n                    handler: data =&gt; {\r\n                        console.log('Cancel clicked');\r\n                    }\r\n                },\r\n                {\r\n                    text: 'Save',\r\n                    handler: data =&gt; {\r\n                        let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n                        let options = new RequestOptions({ headers: headers });\r\n                        this.http.post(\"https:\/\/localhost:3000\/console\", JSON.stringify(data), options)\r\n                            .subscribe(result =&gt; {\r\n                                this.consoles.push(data);\r\n                            }, error =&gt; {});\r\n                    }\r\n                }\r\n            ]\r\n        });\r\n        alert.present();\r\n    }\r\n\r\n}<\/pre>\n<p>While not too different than what we&#8217;ve already seen, we have a new feature. We are using a popup dialog to collect input for new video game console information.<\/p>\n<p>When the popup is dismissed, the following is executed:<\/p>\n<pre class=\"lang:default decode:true \">handler: data =&gt; {\r\n    let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n    let options = new RequestOptions({ headers: headers });\r\n    this.http.post(\"https:\/\/localhost:3000\/console\", JSON.stringify(data), options)\r\n        .subscribe(result =&gt; {\r\n            this.consoles.push(data);\r\n        }, error =&gt; {});\r\n}<\/pre>\n<p>This will take the information found in the form and send it via HTTP to our API which will in turn save the console information to the database.<\/p>\n<p>Awesome right?<\/p>\n<p>The HTML found in the project&#8217;s\u00a0<strong>src\/pages\/consoles\/consoles.html<\/strong> file looks like the following:<\/p>\n<pre class=\"lang:default decode:true\">&lt;ion-header&gt;\r\n    &lt;ion-navbar&gt;\r\n        &lt;button ion-button menuToggle&gt;\r\n            &lt;ion-icon name=\"menu\"&gt;&lt;\/ion-icon&gt;\r\n        &lt;\/button&gt;\r\n        &lt;ion-title&gt;Consoles&lt;\/ion-title&gt;\r\n        &lt;ion-buttons end&gt;\r\n            &lt;button ion-button icon-only (click)=\"create()\"&gt;\r\n                &lt;ion-icon name=\"add\"&gt;&lt;\/ion-icon&gt;\r\n            &lt;\/button&gt;\r\n        &lt;\/ion-buttons&gt;\r\n    &lt;\/ion-navbar&gt;\r\n&lt;\/ion-header&gt;\r\n\r\n&lt;ion-content&gt;\r\n    &lt;ion-list&gt;\r\n        &lt;button ion-item *ngFor=\"let console of consoles\"&gt;\r\n            {{ console.title }}\r\n            &lt;span class=\"item-note\" item-right&gt;\r\n                {{ console.year }}\r\n            &lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n    &lt;\/ion-list&gt;\r\n&lt;\/ion-content&gt;<\/pre>\n<p>Again, it is near identical to the other HTML files that we&#8217;ve seen.<\/p>\n<h2>Conclusion<\/h2>\n<p>You just got a recap of everything I went over at Midwest JS 2017. We saw how to create a Node.js API that communicates with Couchbase, our NoSQL database, as well as creating a frontend using Angular and Ionic Framework. These are just a few components of a full stack application.<\/p>\n<p>For more information on <a href=\"https:\/\/www.couchbase.com\/blog\/build-a-rest-api-with-node-js-express-and-couchbase\/\">using Node.js<\/a> with Couchbase, check out the <a href=\"https:\/\/www.couchbase.com\/developers\/\" target=\"_blank\" rel=\"noopener\">Couchbase Developer Portal<\/a>. If you&#8217;d like me to come back to Midwest JS, let me know in the comments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Back in August I had participated in Midwest JS located in\u00a0Minneapolis,\u00a0Minnesota. As you may know, I&#8217;m a huge fan of developing full stack applications with the JavaScript stack. This is exactly what I had presented on at the conference. My [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1815,1816,9327,1822,1812],"tags":[1704,1588,1534,1543],"ppma_author":[9032],"class_list":["post-3943","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-best-practices-and-tutorials","category-couchbase-server","category-javascript","category-node-js","category-n1ql-query","tag-angular","tag-full-stack","tag-ionic-framework","tag-javascript"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Midwest JS Project Source on Full Stack Node Development, Available<\/title>\n<meta name=\"description\" content=\"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.\" \/>\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\/midwest-js-project-source-full-stack-node-development-available\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Midwest JS Project Source on Full Stack Node Development, Available\" \/>\n<meta property=\"og:description\" content=\"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/thepolyglotdeveloper\" \/>\n<meta property=\"article:published_time\" content=\"2017-09-01T14:00:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T03:15:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/08\/midwestjs-couchbase.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"768\" \/>\n\t<meta property=\"og:image:height\" content=\"558\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@nraboy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\"},\"author\":{\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1\"},\"headline\":\"Midwest JS Project Source on Full Stack Node Development, Available\",\"datePublished\":\"2017-09-01T14:00:21+00:00\",\"dateModified\":\"2025-06-14T03:15:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\"},\"wordCount\":1489,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"keywords\":[\"Angular\",\"full stack\",\"ionic framework\",\"javascript\"],\"articleSection\":[\"Application Design\",\"Best Practices and Tutorials\",\"Couchbase Server\",\"JavaScript\",\"Node.js\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\",\"name\":\"Midwest JS Project Source on Full Stack Node Development, Available\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2017-09-01T14:00:21+00:00\",\"dateModified\":\"2025-06-14T03:15:10+00:00\",\"description\":\"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Midwest JS Project Source on Full Stack Node Development, Available\"}]},{\"@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\/bb545ebe83bb2d12f91095811d0a72e1\",\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"caption\":\"Nic Raboy, Developer Advocate, Couchbase\"},\"description\":\"Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.\",\"sameAs\":[\"https:\/\/www.thepolyglotdeveloper.com\",\"https:\/\/www.facebook.com\/thepolyglotdeveloper\",\"https:\/\/x.com\/nraboy\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/nic-raboy-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Midwest JS Project Source on Full Stack Node Development, Available","description":"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.","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\/midwest-js-project-source-full-stack-node-development-available\/","og_locale":"en_US","og_type":"article","og_title":"Midwest JS Project Source on Full Stack Node Development, Available","og_description":"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.","og_url":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/","og_site_name":"The Couchbase Blog","article_author":"https:\/\/www.facebook.com\/thepolyglotdeveloper","article_published_time":"2017-09-01T14:00:21+00:00","article_modified_time":"2025-06-14T03:15:10+00:00","og_image":[{"width":768,"height":558,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/08\/midwestjs-couchbase.jpg","type":"image\/jpeg"}],"author":"Nic Raboy, Developer Advocate, Couchbase","twitter_card":"summary_large_image","twitter_creator":"@nraboy","twitter_misc":{"Written by":"Nic Raboy, Developer Advocate, Couchbase","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/"},"author":{"name":"Nic Raboy, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1"},"headline":"Midwest JS Project Source on Full Stack Node Development, Available","datePublished":"2017-09-01T14:00:21+00:00","dateModified":"2025-06-14T03:15:10+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/"},"wordCount":1489,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["Angular","full stack","ionic framework","javascript"],"articleSection":["Application Design","Best Practices and Tutorials","Couchbase Server","JavaScript","Node.js","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/","url":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/","name":"Midwest JS Project Source on Full Stack Node Development, Available","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2017-09-01T14:00:21+00:00","dateModified":"2025-06-14T03:15:10+00:00","description":"The full stack Node.js, Angular, and Couchbase NoSQL project source from Midwest JS is now available, with full write-up guide to success.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/midwest-js-project-source-full-stack-node-development-available\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Midwest JS Project Source on Full Stack Node Development, Available"}]},{"@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\/bb545ebe83bb2d12f91095811d0a72e1","name":"Nic Raboy, Developer Advocate, Couchbase","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354","url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","caption":"Nic Raboy, Developer Advocate, Couchbase"},"description":"Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.","sameAs":["https:\/\/www.thepolyglotdeveloper.com","https:\/\/www.facebook.com\/thepolyglotdeveloper","https:\/\/x.com\/nraboy"],"url":"https:\/\/www.couchbase.com\/blog\/author\/nic-raboy-2\/"}]}},"authors":[{"term_id":9032,"user_id":63,"is_guest":0,"slug":"nic-raboy-2","display_name":"Nic Raboy, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","author_category":"","last_name":"Raboy","first_name":"Nic","job_title":"","user_url":"https:\/\/www.thepolyglotdeveloper.com","description":"Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/3943","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\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=3943"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/3943\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=3943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=3943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=3943"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=3943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}