Создание приложения с полным стеком с использованием Node.js и сервера Couchbase

  1. Установка сервера Couchbase
  2. Создание корзины и индекса
  3. Установка Node.js
  4. Настройте свой проект Node.js
  5. Бэкэнд проекта
  6. Настройка сервера
  7. Создание конечных точек маршрута API
  8. Фронт-энд проекта
  9. Включая все сторонние библиотеки и стили
  10. Создание Фонда AngularJS
  11. Настройка AngularJS UI-Router
  12. Добавление нашей логики контроллера
  13. Заключение
  14. Ник Рабой

Ранее я писал о том, как создать приложение AngularJS с PouchDB и Couchbase , Способ, который я продемонстрировал, - это, конечно, один из способов использования Couchbase в веб-приложении. На этот раз мы рассмотрим создание приложения с полным стеком, используя Couchbase , Express.js , AngularJS , а также Node.js (CEAN) стек.

Возможно, вы знакомы со стеком MongoDB, Express.js, AngularJS и Node.js (MEAN), но я собираюсь показать вам, что это возможно с Couchbase и даже проще.

Мы рассмотрим, как установить и настроить все четыре службы стека CEAN, а также сделать из них очень простое приложение.

Весь процесс установки не займет много времени, поэтому мы пройдем его очень быстро.

Установка сервера Couchbase

Начнем с установки нашей базы данных Couchbase Server. Направляйтесь к загрузок раздел веб-сайта Couchbase и получите последнюю версию установки для вашего компьютера, будь то Mac, Windows или Linux.

Установите загруженный файл и запустите Couchbase Server. При первом запуске он должен пройти через мастер настройки. Работа мастера занимает около двух минут.

Создание корзины и индекса

Couchbase хранит все документы NoSQL в так называемой корзине. Нам нужно будет создать файл под названием restful-sample , чтобы продолжить работу в приложении. На вкладке «Наборы данных» административной панели управления Couchbase выберите « Создать новый блок данных» и присвойте ему соответствующее имя.

С созданным контейнером нам нужно создать первичный индекс для него. Это позволит нам выполнять N1QL-запросы к данным. В этом уроке вы увидите, что они из себя представляют и почему они прекрасны.

Запустите клиент Couchbase Query Client (CBQ) из терминала или командной строки. На Mac он находится по следующему пути:

./Applications/Couchbase Server.app/Contents/Resources/couchbase-core/bin/cbq

На компьютере с Windows он находится по следующему пути:

C: / Program Files / Couchbase / Сервер / bin / cbq.exe

При открытом CBQ выполните следующую инструкцию, чтобы создать первичный индекс:

СОЗДАТЬ ПЕРВИЧНЫЙ ИНДЕКС НА `restful-sample` ИСПОЛЬЗУЯ GSI;

Наш Couchbase Bucket теперь готов к работе!

Установка Node.js

Теперь мы собираемся установить Node.js. Хотя мы не увидим этого на этом этапе, для загрузки Express.js и его зависимостей будет использован менеджер пакетов узлов (NPM), включенный в Node.js. Направляйтесь к Node.js веб-сайт и загрузите любую версию, подходящую для вашей операционной системы.

Установите Node.js, как обычно, в зависимости от вашей операционной системы.

Настройте свой проект Node.js

Создайте новый каталог, возможно на рабочем столе, под названием CEAN . Используя терминал (Mac и Linux) или командную строку (Windows), запустите следующую команду с CEAN в качестве текущего рабочего каталога:

npm init

Отвечайте на все вопросы в меру своих возможностей или просто создайте файл package.json со следующим содержимым:

{"name": "cean-project", "version": "1.0.0", "description": "Пример использования SDK Node.js для Couchbase для создания простого стека CEAN", "author": " Couchbase, Inc. "," license ":" MIT "}

Поскольку у нас есть NPM, работающий через установку Node.js, нам нужно установить Express.js и несколько других зависимостей локально для нашего проекта. В терминале или командной строке выполните следующую команду:

npm установить body-parser couchbase express uuid --save

Давайте разберем то, что мы только что установили. Зависимость body-parser позволит нам отправлять POST-запросы к нашему внутреннему API. Зависимость couchbase позволит вам общаться с сервера приложений на Couchbase Server. Конечно, зависимость express позволяет нам использовать среду Express.js, а зависимость uuid позволит нам генерировать уникальные идентификаторы позже в нашем проекте.

Прежде чем мы завершим настройку, давайте определим структуру нашего проекта. Идем дальше и создаем следующие файлы и каталоги:

CEAN маршруты маршруты. Js модели recordmodel.js общедоступные js app.js css шаблоны item.html list.html index.html app.js package.json config.json

Мы будем добавлять другие библиотеки и таблицы стилей, но все, что мы будем создавать, - это выше.

Бэкэнд проекта

Серверная часть будет отвечать за передачу данных нашему клиенту (AngularJS). Это основано на конечных точках, что облегчает разделение между интерфейсом и сервером.

Настройка сервера

Вся конфигурация нашего сервера будет в файле app.js, который находится в корне нашего проекта. Однако, чтобы сохранить наш проект в чистоте, мы собираемся поместить все наши постоянные переменные в их собственный файл. Этот файл будет называться config.json и будет содержать следующее:

{"couchbase": {"server": "127.0.0.1:8091", "bucket": "restful-sample"}}

По сути, у нас есть только две константы для этого проекта. Какой сервер мы планируем использовать и какое ведро мы хотим использовать. Контейнер в терминах Couchbase - это набор документов NoSQL.

Откройте app.js в корне вашего проекта и включите следующий код:

var express = require ("express"); var bodyParser = require ("body-parser"); var couchbase = require ("couchbase"); var path = require ("путь"); var config = require ("./ config"); var app = express (); app.use (bodyParser.json ()); app.use (bodyParser.urlencoded ({extended: true})); module.exports.bucket = (новый couchbase.Cluster (config.couchbase.server)). openBucket (config.couchbase.bucket); app.use (express.static (path.join (__dirname, "public"))); var маршруты = требуется ("./ маршруты / маршруты.js") (приложение); var server = app.listen (3000, function () {console.log ("Прослушивание порта% s ...", server.address (). port);});

Давайте разберемся, что здесь происходит. Во-первых, нам нужны все зависимости, которые мы загрузили с помощью NPM в наш проект. Затем мы сообщаем серверу приложений, что мы хотим принимать запросы POST с телами JSON, а также с данными в кодировке URL.

module.exports.bucket = (новый couchbase.Cluster (config.couchbase.server)). openBucket (config.couchbase.bucket);

Здесь мы подключаемся к кластеру Couchbase и открываем определенный сегмент , оба определены в нашем файле config.json . Этот открытый контейнер назначается глобальной переменной с помощью команды module.exports.

app.use (express.static (path.join (__dirname, "public")));

Приведенная выше строка позволит нам разместить HTML, JavaScript и другие интерфейсные файлы в нашем проекте. Внешний и внутренний интерфейсы будут по-прежнему обмениваться запросами, но внешний интерфейс все еще будет включен в наш проект.

Наконец, мы включаем наш файл routs / rout.js, который будет содержать пути к каждой из наших конечных точек и слушателю сервера.

Прежде чем мы настроим наши маршруты, давайте определим некоторые функции для связи с базой данных. Все эти функции перейдут в класс RecordModel, находящийся в файле models / recordmodel.js . Эти функции будут отвечать за вставку, обновление, извлечение и удаление данных из Couchbase. В значительной степени ваши основы CRUD.

Давайте начнем с создания основы этого файла models / recordmodel.js . Откройте его и добавьте следующее:

var uuid = require ("uuid"); var db = require ("../ app"). bucket; var config = require ("../ config"); var N1qlQuery = require ('couchbase'). N1qlQuery; function RecordModel () {}; module.exports = RecordModel;

Вы можете видеть выше, что нам требуется загруженная нами зависимость uuid , а также файлы app.js и config.json из корня нашего проекта. Это потому, что мы планируем использовать все это.

Теперь давайте разберем все функции, начиная с функции сохранения. В файле models / recordmodel.js добавьте следующую функцию перед модулем.exports = RecordModel; линия:

RecordModel.save = function (data, callback) {var jsonObject = {имя: data.firstname, фамилия: data.lastname, электронная почта: data.email} var documentId = data.document_id? data.document_id: uuid.v4 (); db.upsert (documentId, jsonObject, function (error, result) {if (error) {callback (error, null); return;} обратный вызов (null, {message: "success", data: result});}); }

Что здесь происходит? Что ж, мы принимаем объект данных и обратный вызов, переданный из скоро создаваемого родительского маршрута. Мы извлекаем только необходимые нам элементы данных из переменной данных и создаем ключ документа, который будет использоваться. Эта функция сохранения будет обрабатывать как обновления, так и вставки (upsert), поэтому ключи очень важны. В основном, если идентификатор документа был передан из маршрута, используйте его, в противном случае создайте какое-то уникальное значение и используйте его. Upsert решит, стоит ли нам вставлять или обновлять.

В зависимости от того, как отвечает функция upsert, мы используем родительскую функцию обратного вызова.

С сохранением в стороне, давайте взглянем на функцию getByDocumentId для извлечения конкретного документа. В файле models / recordmodel.js добавьте следующее:

RecordModel.getByDocumentId = function (documentId, callback) {var Statement = "SELECT имя, фамилия, адрес электронной почты" + "FROM` "+ config.couchbase.bucket +" `AS users" + "WHERE META (users) .id = $ 1 «; var query = N1qlQuery.fromString (оператор); db.query (query, [documentId], function (error, result) {if (error) {return callback (error, null);} обратный вызов (null, result);}); };

Эта функция снова принимает два параметра из родительской конечной точки, идентификатор документа и обратный вызов. Однако на этот раз мы используем очень похожее на SQL выражение, на которое ссылается Couchbase как N1QL , Это параметризованный запрос для предотвращения атак внедрения, но он по сути просто находит любой документ NoSQL, где ключ документа соответствует значению, переданному в функцию.

Следующая функция, delete, довольно проста. Внутри файла models / recordmodel.js добавьте следующее:

RecordModel.delete = function (documentId, callback) {db.remove (documentId, function (error, result) {if (error) {callback (error, null); return;} обратный вызов (null, {message: "success", данные: результат});}); };

Мы просто передаем идентификатор документа и обратный вызов и используем Node.js SDK для Couchbase. Очень просто и ничего особенного.

Наконец, наша последняя функция, getAll, которая будет получать все документы из нашей корзины Couchbase:

RecordModel.getAll = function (callback) {var Statement = "SELECT META (users) .id, имя, фамилия, электронная почта" + "FROM` "+ config.couchbase.bucket +" `AS users"; var query = N1qlQuery.fromString (оператор) .consistency (N1qlQuery.Consistency.REQUEST_PLUS); db.query (запрос, функция (ошибка, результат) {if (ошибка) {возврат обратного вызова (ошибка, ноль);} обратный вызов (ноль, результат);}); };

Опять же, мы используем здесь N1QL, потому что это очень удобно для возврата пачки документов.

WEW! Теперь у нас есть вся логика в нашем приложении CEAN, которое взаимодействует с Couchbase Server. Это оставляет нас для разработки наших конечных точек и шрифтов.

Создание конечных точек маршрута API

Все, что мы здесь делаем, будет в файле roads / rout.js. Основание этого файла будет выглядеть следующим образом:

var RecordModel = require ("../ models / recordmodel"); var appRouter = function (app) {}; module.exports = appRouter;

Каждый маршрут заканчивается в функции appRouter. Давайте пойдем в том же порядке, который мы сделали для функций класса, начиная с маршрута сохранения:

app.post ("/ api / save", function (req, res) {if (! req.body.firstname) {return res.status (400) .send ({"status": "error", "message" : "Требуется имя"});} иначе if (! Req.body.lastname) {return res.status (400) .send ({"status": "error", "message": "Требуется фамилия "});} else if (! req.body.email) {return res.status (400) .send ({" status ":" error "," message ":" Требуется электронная почта "});} RecordModel .save (req.body, function (error, result) {if (error) {return res.status (400) .send (error);} res.send (result);});});

Когда пользователь (или клиентский интерфейс) попадает в конечную точку / api / save с запросом POST, произойдет описанное выше. Сначала мы должны убедиться, что тело POST содержит имя, фамилию и адрес электронной почты, в противном случае мы возвращаем ошибку. Если эти условия выполняются, мы вызываем функцию сохранения нашего класса RecordModel. Обратный вызов передается и в зависимости от того, что возвращает дочерняя функция, является тем, что мы возвращаем обратно пользователю (или front-end).

С сохранением в стороне, давайте посмотрим на конечную точку get для получения конкретного документа из базы данных:

app.get ("/ api / get", function (req, res) {if (! req.query.document_id) {return res.status (400) .send ({"status": "error", "message" : "Требуется идентификатор документа"});} RecordModel.getByDocumentId (req.query.document_id, function (error, result) {if (error) {return res.status (400) .send (error);} res. отправить (результат);});});

Когда внешний интерфейс достигает конечной точки / api / get с запросом GET, произойдет описанное выше. Сначала мы удостоверимся, что запрос GET содержит document_id, и если это условие выполнено, мы можем перейти к вызову функции getByDocumentId нашего класса RecordModel.

Я уверен, что вы можете увидеть тенденцию здесь в нашем дизайне конечной точки.

Двигаясь дальше, мы хотим создать конечную точку для удаления данных. В файле route / rout.js добавьте следующую функцию:

app.post ("/ api / delete", function (req, res) {if (! req.body.document_id) {return res.status (400) .send ({"status": "error", "message" : "Требуется идентификатор документа"});} RecordModel.delete (req.body.document_id, function (error, result) {if (error) {return res.status (400) .send (error);} res. отправить (результат);});});

Если запрос POST к конечной точке / api / delete сделан выше, мы сначала подтверждаем, что document_id существует в теле POST. Если это так, тогда вызовите функцию удаления класса RecordModel.

Наконец у нас есть последняя конечная точка. Последняя конечная точка отвечает за возврат всех документов, которые существуют в базе данных. Это можно увидеть в следующем коде:

app.get ("/ api / getAll", function (req, res) {RecordModel.getAll (function (error, result) {if (error) {return res.status (400) .send (error);} res. отправить (результат);});});

Там нет никаких требований выше. Пока к конечной точке / api / getAll сделан запрос GET, данные будут возвращены.

Наш бэкэнд сделан. Мы можем праздновать сейчас! Не совсем, нам все еще нужен интерфейс для общения с ним. Любой интерфейс будет работать, но в этом стеке CEAN мы будем использовать AngularJS.

Фронт-энд проекта

Интерфейс будет отвечать за запрос данных от нашего сервера (Node.js и Couchbase Server). Он отправляет HTTP-запросы на сервер, обеспечивая лучшее разделение и гибкость между интерфейсом и сервером.

Включая все сторонние библиотеки и стили

Внешний интерфейс, который мы делаем, конечно, использует AngularJS , но он также использует AngularJS UI-Router библиотека, а также Twitter Bootstrap , Первое, что мы хотим сделать, это загрузить все.

После загрузки всего файла поместите уменьшенные (.min.js) файлы в каталог public / js вашего проекта. Поместите все CSS-файлы в каталог public / css вашего проекта и поместите все шрифты в каталог public / fonts вашего проекта.

Теперь в ваши проекты можно включить все файлы public / index.html . Откройте его и включите следующее:

<! DOCTYPE html> <html> <head> <link rel = "stylesheet" href = "css / bootstrap.min.css"> <script src = "js / angular.min.js"> </ script> <script src = "js / angular-ui-router.min.js"> </ script> <script src = "js / bootstrap.min.js"> </ script> <script src = "js / app.js"> </ script> </ head> <body ng-app = "recordsapp"> <div style = "padding: 20px"> <div ui-view> </ div> </ div> </ body> </ html>

Вам может быть интересно, что такое ng-app = "recordsapp" или что такое <div ui-view> </ div>. Не волнуйся, это скоро.

Создание Фонда AngularJS

Внутри вашей публичной директории / js у вас должен быть файл app.js. Идите дальше и добавьте следующий код основания:

angular.module ("recordsapp", ["ui.router"]) .config (function ($ stateProvider, $ urlRouterProvider) {}) .controller ("MainController", функция ($ scope, $ http, $ state, $ stateParams) ) {});

Здесь вы можете видеть, что мы назвали наш модуль recordsapp, который относится к тому, что мы видели в файле public / index.html . Мы также внедрили ui.router и создали функции для наших .config и .controller.

Настройка AngularJS UI-Router

Если вы не знакомы с AngularJS UI-Router, это замечательная библиотека для добавления нескольких экранов (представлений) в ваше интерфейсное приложение. Вся конфигурация для этих представлений будет в конечном итоге в нашей функции config в файле public / js / app.js :

.config (function ($ stateProvider, $ urlRouterProvider) {$ stateProvider .state ("list", {"url": "/ list", "templateUrl": "templates / list.html", "controller": "MainController") , "cache": false}) .state ("item", {"url": "/ item /: documentId", "templateUrl": "templates / item.html", "controller": "MainController", "cache ": false}); $ urlRouterProvider.otherwise (" список ");})

По сути, мы создаем два представления. Мы создаем представление списка и представление для добавления или редактирования элементов списка. Каждый из маршрутов указывает на определенный контроллер и файл шаблона. Оба маршрута указывают на MainController, который мы скоро увидим, но каждый указывает либо на файл public / templates / list.html, либо на файл public / templates / item.html .

По умолчанию представление списка будет отображаться через $ urlRouterProvider.otherwise («список»); линия.

Мы знаем, над какими файлами шаблонов нам нужно работать сейчас, как объясняется конфигурацией AngularJS UI-Router. Начиная с файла public / templates / list.html, добавьте следующий код:

<button class = "btn btn-primary" ui-sref = "item"> Новый элемент </ button> <br /> <table class = "table table-striped" ng-init = "fetchAll ()"> <thead > <tr> <th> Имя </ th> <th> Фамилия </ th> <th> Электронная почта </ th> <th> </ th> </ tr> </ thead> <tbody> <tr ng-repeat = "(ключ, значение) в элементах"> <td> {{value.firstname}} </ td> <td> {{value.lastname}} </ td> <td> {{value.email }} </ td> <td> <a href="" ui-sref="item( enjdocumentId: key rout)"> изменить </a> | <a href="" ng-click="delete(key)"> delete </a> </ td> </ tr> </ tbody> </ table>

По сути, у нас есть таблица, которая перебирает каждый объект в конкретном объекте для заполнения таблицы. Такие функции, как fetchAll и delete, пока неизвестны, но они идут. Если вы посмотрите на элементы ui-sref в коде, вы заметите, что они ссылаются на определенные состояния UI-Router. Один из которых передает необязательный параметр, содержащий ключ документа. Вы скоро увидите, как это используется.

Второй шаблон, который мы хотим рассмотреть, это файл public / templates / item.html :

<form> <div class = "form-group"> <label for = "firstname"> Имя </ label> <input type = "text" class = "form-control" id = "firstname" placeholder = "First Имя "ng-model =" inputForm.firstname "> </ div> <div class =" form-group "> <label for =" lastname "> Фамилия </ label> <input type =" text "class =" form-control "id =" lastname "placeholder =" Фамилия "ng-model =" inputForm.lastname "> </ div> <div class =" form-group "> <label for =" email "> Электронная почта </ label> <input type = "text" class = "form-control" id = "email" placeholder = "Электронная почта" ng-model = "inputForm.email"> </ div> <button type = "button" class = " btn btn-danger "ui-sref =" list "> Отмена </ button> <button type =" button "class =" btn btn-success "ng-click =" save (inputForm.firstname, inputForm.lastname, inputForm. электронная почта) "> Сохранить </ button> </ form>

Этот файл не более чем форма. Когда кнопка успеха нажата, вызывается функция сохранения. Мы еще не создали его, но оно идет.

Добавление нашей логики контроллера

Последняя часть нашего интерфейса - это вся логика приложения, которая подпитывает визуальные эффекты. В файле public / js / app.js вашего проекта добавьте в свой контроллер следующее:

.controller ("MainController", функция ($ scope, $ http, $ state, $ stateParams) {$ scope.items = {}; $ scope.fetchAll = function () {$ http ({method: "GET", url : "/ api / getAll"}) .success (function (result) {for (var i = 0; i <result.length; i ++) {$ scope.items [result [i] .id] = result [i] ;}}) .error (function (error) {console.log (JSON.stringify (error));});} if ($ stateParams.documentId) {$ http ({method: "GET", url: "/ api / get ", params: {document_id: $ stateParams.documentId}}) .success (function (result) {$ scope.inputForm = result [0];}) .error (function (error) {console.log (JSON) .stringify (error));});} $ scope.delete = function (documentId) {$ http ({method: "POST", url: "/ api / delete", data: {document_id: documentId}}). success (function (result) {delete $ scope.items [documentId];}) .error (function (error) {console.log (JSON.stringify (error));});} $ scope.save = function (firstname) , фамилия, адрес электронной почты) {$ http ({метод: "POST", URL: "/ api / save", данные: {имя: имя, фамилия: las tname, email: email, document_id: $ stateParams.documentId}}) .success (function (result) {$ state.go ("list"); }) .error (function (error) {console.log (JSON.stringify (error));}); }});

Это много запросов $ http, и в этом суть! Внешний интерфейс этого стека предполагается только для запроса данных от внутреннего интерфейса. Это делает его очень модульным.

На этом этапе ваш сервер Couchbase должен быть запущен. Из командной строки или терминала запустите следующее с проектом в качестве текущего рабочего каталога:

узел app.js

Если вы посетите HTTP: // локальный: 3000 из вашего веб-браузера у вас должно быть полнофункциональное приложение стека CEAN.

Выше, как должно выглядеть ваше приложение.

Заключение

Вы только что увидели, как создать полноценное приложение стека CEAN. Наш код был коротким, чистым и очень простым для понимания благодаря красоте Node.js SDK для Couchbase.

Если вы заинтересованы в полном исходном коде этого проекта, его можно загрузить с Couchbase Labs GitHub репозиторий.

Ник Рабой

Ник Рабой - сторонник современных веб-технологий и технологий мобильной разработки. Он имеет опыт работы с Java, JavaScript, Golang и множеством фреймворков, таких как Angular, NativeScript и Apache Cordova. Ник пишет о своем опыте разработки, связанном с тем, чтобы сделать веб-и мобильную разработку проще для понимания.

Document_id?