GraphQL сервер с Koa2 и MongoDB

от автора

Привет Мир! Сервер — это сердце любого проекта. Сегодня я расскажу, как заставить его биться, чтобы вы смогли использовать в разных целях. Начиная от SPA, мобильный платформ Android + iOS и ограничиваясь лишь вашей фантазией.



Тот кто знаком с GraphQL, в этой статье не узнает ничего нового. Тот кто спешит, может сразу заглянуть в готовый репозиторий GitHub. Тот, кто заинтересован и располагает хотя бы часом времени, сможет с нулевыми знаниями создать свой полностью рабочий GraphQL сервер и что важнее — разобраться, как это все работает, даже если вы не дружите с node.js.

Введение в GraphQL

GraphQL — это язык запросов, который разработал Facebook и анонсировал в 2015 году. Он позволяет получать данные с сервера, добавлять, удалять, редактировать записи и создавать приложение реального времени буквально на коленках.

Подготовка компьютера

Кто знаком с основами node.js, может смело пропустить этот раздел.

В первую очередь на рабочем компьютере нам понадобится последняя версия Node.js 7.7.4+
По завершению установки откройте консоль и напишите команду:

node -v

Консоль вывела v7.7.4+ ? Отлично.

Чтобы не путаться в консольных командах, я составлю компактную таблицу, которая поможет ориентироваться:

npm  // пакетный менеджер, через который мы будем все устанавливать i    // сокращенно от install -g   // установка модуля глобально init // ручное создание файла package.json init -y   // автоматическое создание файла package.json -D   // установка пакетов в раздел "devDependencies" -S   // установка пакетов в раздел "dependencies" package.json: "devDependencies" - раздел с модулями используемых при разработке "dependencies"    - раздел с модулями используемых в продакшене 

Приступим к настройке проекта. Выполните по порядку команды в консоле:

// создаст рабочую директорию mkdir test // будет выполнять последующие команды из указанной директории cd test // создаст автоматически package.json в папке test npm init -y

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

npm i -g nodemon

По умолчанию все пакеты устанавливаются локально. При глобальной установке, пакет будет доступен на любом проекте в пределах рабочего компьютера.

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

У вас создана папка проекта, в ней имеется файл package.json, глобально установлен nodemon , проект открыт в редакторе кода или IDE? Чудно, пока этого будет достаточно.

Откроем package.json в разделе ”scripts”, удалим строку “test” и добавим новую, чтобы получилось:

"scripts": {   "dev": "nodemon ./src/main.js" }, 

В корне проекта создайте папку srс , а в ней 3 файла: main.js, server.js, db.js .

В файле server.js будут храниться основные настройки сервера. main.js — точка входа проекта. db.js — отдельный файл с настройками подключения к базе данных.

При написание кода, будет использоваться синтаксис es2015 (es6), поэтому понадобится Babel для компиляции es6 в es5:

// babel-preset-es2015 компилирует es6 в es5 // babel-register компилировать на ходу // babel-plugin-transform-runtime + babel-preset-stage-3  // пригодятся позже npm i -D babel-register babel-preset-es2015 babel-preset-stage-3 babel-plugin-transform-runtime

Создадим еще один файл в корне проекта .babelrc c кодом:

{   "presets": [     "es2015",     "stage-3"   ],   "plugins": ["transform-runtime"] }

.babelrc — это файл настроек для компилятора Babel, он используется автоматически. В main.js добавим:

// babel-register будет компилировать код из server.js require('babel-register'); require('./server');

Установим пакет mongoose , который позволит взаимодействовать с MongoDB.

npm i -S mongoose

В файл db.js добавьте:

import mongoose from 'mongoose'; mongoose.Promise = global.Promise; // если подключение к БД успешно, то в консоле увидим:  // 'Connected to mongo server.' mongoose.connection.on('open', function (ref) {     console.log('Connected to mongo server.'); }); // если сервер не может подключится к БД, то выведет сообщение:  // 'Could not connect to mongo server!' + ошибки mongoose.connection.on('error', function (err) {     console.log('Could not connect to mongo server!');     console.log(err); }); // пример подключения к MongoDB // mongodb://username:password@host:port/myDataBase mongoose.connect('mongodb://localhost:27017/test');

Напомню, mongoose всего лишь мост, между сервером и базой данных. Запустить MongoDB можно в облаке или на локальной машине. Поиск в Google: free mongodb cloud поможет найти бесплатное облачный хостинг. Также обратитесь к документации mongoose.

У себя я использую локальную БД: mongodb://localhost:27017/test .
В строке подключения нет username и password, в качестве host: localhost , порт: 27017, имя базы данных: test .

Наконец, мы подошли к последнему шагу настройки сервера. Вернемся к файлу server.js и установим требуемые пакеты:

// @next - это самая последняя версия пакета npm i -S koa koa-router@next koa-bodyparser@next graphql-server-koa

В сам файл server.js скопируем код:

// koa - node.js фреймворк на базе которого запускается сервер // koa-router - маршрутизация на сервере // graphql-server-koa модуль связки, чтобы подружить Koa и GraphQL import koa from 'koa'; // koa@2 import koaRouter from 'koa-router'; // koa-router@next import koaBody from 'koa-bodyparser'; // koa-bodyparser@next import {graphqlKoa, graphiqlKoa} from 'graphql-server-koa'; // знакомство с schema ждет нас впереди // db.js - файл отвечающий за подключение к MongoDB import schema from './data/schema' import './db' const app = new koa(); const router = new koaRouter(); const PORT = 3000; // koaBody is needed just for POST. app.use(koaBody()); // POST и GET запросы будут перенаправляться в схему GraphQL router.post('/graphql', graphqlKoa({schema: schema})); router.get('/graphql', graphqlKoa({schema: schema})); // инструмент для тестирования запросов localhost:3000/graphiql router.get('/graphiql', graphiqlKoa({endpointURL: '/graphql'})); app.use(router.routes()); app.use(router.allowedMethods()); // запуск сервера app.listen(PORT, () => {     console.log('Server is running on', 'localhost:' + PORT);     console.log('GraphiQL dashboard', 'localhost:' + PORT + '/graphiql'); });

Настройка сервера завершена. Если вы это осилили, значит хотите увидеть результат работы. В файле server.js закомментируем три строчки кода:

//import schema from './data/schema'; //router.post('/graphql', graphqlKoa({schema: schema})); //router.get('/graphql', graphqlKoa({schema: schema}));

Мы еще не создавали схему GraphQL, поэтому так мы избежим ошибок и сможем насладиться рабочим сервером. Запустим его:

npm run dev

Если все сделали правильно и имеется подключение к MongoDB, в консоле увидим следующее:

> nodemon ./src/main.js [nodemon] 1.11.0 [nodemon] to restart at any time, enter `rs` [nodemon] watching: *.* [nodemon] starting `node ./src/main.js` Sever is running on localhost:3000 GraphiQL dashboard localhost:3000/graphiql Connected to mongo server.

В консоле ошибки? Ничего страшного. Откройте оригинальный проект на Github и сверьте файлы за исключением папок data и .idea .

Если получилось запустить без ошибок, откройте в браузере:

http://localhost:3000/graphiql

Вам будет доступен графический инструмент GraphiQL для тестирования запросов. Получилось? Отлично.

Пришло время сделать небольшой перерыв. Налейте себе кофе или чай, в зависимости от того, что вам больше нравится. Позже мы продолжим говорить о самом интересном.

2 + 2 = GraphQL

GraphQL — прост и я могу это доказать.

Недавно, вы возможно прислушались моего совета и сделали себе чай или кофе. Теперь представьте, что за вас это бы сделал некая Jane Dohe.

Так, вам нужно лишь ей сказать, что именно вы хотите: чай или кофе. Jane сможет сама приготовить и принести вам. Даже если вы предпочитаете сахар и молоко в напиток, Jane возьмет у соседки сахар, а за молоком дойдет до магазина. Сама все приготовит и уже в готовом виде принесет вам, в то самое место, где вы об этом ее попросили.

У Jane есть лишь один нюанс, сама она не знает, где брать сахар и молоко, поэтому ей нужно один раз объяснить и при любой повторной просьбе она будет это делать самостоятельно.

Именно так работает GraphQL на примере Jane Dohe. Вы отправляете запрос или запросы из любого места проекта. В тоже самое место, вы получаете ответ в формате JSON. Даже если запрашиваемые данные находятся в разных базах: MongoDB, MySQL, PostgreSQL. С одним нюансом, как и Jane, прежде чем делать запрос, нужно один раз объяснить GraphQL серверу, как готовить данные и откуда их нужно брать.

Помните, когда мы комментировали три строчки кода в server.js, чтобы запустить проект? Пора раскомментировать их обратно:

import schema from './data/schema'; router.post('/graphql', graphqlKoa({schema: schema})); router.get('/graphql', graphqlKoa({schema: schema}));

В папке src создайте папку data . В папке data создайте файл schema.js и добавьте папку user в которой нам потребуются 3 файла: queries.js , mutations.js и models.js .

Вся схема взаимосвязи будет выглядеть следующим образом:

Прежде чем углубляться, давайте разберемся. У Jane Dohe один нюанс: ей нужно объяснить, где можно взять сахар и молоко. Так вот, когда нам потребуется получить с сервера данные, добавить, изменить, удалить, то каждый случай необходимо описать отдельно.

Например, для получения данных пользователя, мы создадим отдельное поле User, которое по определенным критериям найдет и вернет одного пользователя. То же самое для массива пользователей Users. Любые операции для вывода данных в GraphQL называются — queries и будут храниться в queries.js .

Аналогично queries, операция добавление, удаление и изменение данных, называются mutations и будут храниться в mutations.js . Каждой операции будет соответствовать конкретное поле: UserCreate, UserDelete, UserEdit.

Queries и mutations имеют много общего, помимо того, что они практически идентичны — у них общая модель.

models.js — это файл в котором мы описываем схему коллекции данных, определяем имя, описываем типы, отдельно для queries и mutations.

Типы очень похожи на схему коллекции, при этом имеет три явных преимущества:

  • Типы фильтруют входящие запросы. Вы можете их считать дополнительной защитой на пути к базе данных.
  • Из-за того, что запросы проверяются до модели, это увеличивает производительность сервера.
  • В типах можно ограничивать исходящие данные. Например, запретить получение пароля пользователя или другую важную информацию. Для этого достаточно их не указывать.

Именно из-за преимуществ, для queries и mutations будут отдельные типы.

GraphQL — Just do it!

Пришло время заполнить последние файлы с кодом.

Установите модуль для работы GraphQL сервера, который включает уже готовые пакеты для создания схемы, мутаций, запросов:

npm i -S graphql

Схема — это ядро GraphQL сервера. Она может быть только одна и содержать только по 1 параметру queries и mutations.

Добавим код в schema.js :

import {     GraphQLObjectType,     GraphQLSchema } from 'graphql'; // импортируем queries и mutations из папки user import UserQueries from './user/queries' import UserMutations from './user/mutations' // создадим GraphQL схему и заполним параметрами // каждый параметр может содержать только один GraphQLObjectType export default new GraphQLSchema({     query: new GraphQLObjectType({         name: 'Query',      // произвольное имя для API библитеки         fields: UserQueries // поля из файла queries.js     }),     mutation: new GraphQLObjectType({         name: 'Mutation',         fields: UserMutations     }) });

От схемы перейдем к models.js :

import {     GraphQLObjectType,     GraphQLInputObjectType,     GraphQLNonNull,     GraphQLString,     GraphQLID } from 'graphql'; import mongoose from 'mongoose'; // схема коллекции const schema = new mongoose.Schema({     firstName: {         type: String,     },     lastName: {         type: String,     } }); // определяем коллекцию User и подключаем к ней схему export let UserModel = mongoose.model('User', schema); // тип для queries export let UserType = new GraphQLObjectType({     name: 'User',     fields: {         _id: {             type: new GraphQLNonNull(GraphQLID)         },         firstName: {             type: GraphQLString         },         lastName: {             type: GraphQLString         }     } }); // тип для mutations export let UserInput = new GraphQLInputObjectType({     name: "UserInput",     fields: {         firstName: {             type: GraphQLString         },         lastName: {             type: GraphQLString         }     } });

Вспомните, мы ранее говорили о полях User и Users, для вывода пользователя и пользователей соответственно. Пора заполнить файл queries.js :

import {     GraphQLID,     GraphQLList,     GraphQLNonNull } from 'graphql'; // импортируем данные из models.js import {UserModel, UserType, UserInput} from './models'; // создаем поле для получения одного пользователя const User = {     type: UserType,  // тип для получения данных пользователя     args: {         // аргумент для поиска пользователь         id: {                   name: 'id',             type: new GraphQLNonNull(GraphQLID)         }     },     // метод, в котором формируется запрос и возвращаются данные     resolve (root, params, options) {         return UserModel             .findById(params.id)             .exec();  // возвращаем JSON     } }; const Users = {     type: new GraphQLList(UserType),     args: {},      resolve (root, params, options) {         return UserModel             .find()             .exec();     } }; export default {     User: User,     Users: Users, }

Mutations практически аналогичны queries. Queries выполняются асинхронно, а mutations последовательно, один за одним. Добавьте код в mutations.js .

import {     GraphQLNonNull,     GraphQLBoolean, } from 'graphql'; import {UserModel, UserType, UserInput} from './models'; const UserCreate = {     description: "Create new user",     type: GraphQLBoolean,     args: {         data: {             name: "data",             type: new GraphQLNonNull(UserInput)         }     },     async resolve (root, params, options) {         const userModel = new UserModel(params.data);         const newUser = await userModel.save(); if (!newUser) {             throw new Error('Error adding new user');         }         return true;     } }; export default {     UserCreate: UserCreate, }

Я вас поздравляю! Вы создали свой GraphQL сервер с нуля. Осталось его протестировать.

npm run dev

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

http://localhost:3000/graphiql

В открытом окне, напишите свои первые мутации:

mutation firstMutation{   UserCreate(data: {firstName: "John", lastName: "Dohe"}) } mutation secondMutation{   UserCreate(data: {firstName: "Jane", lastName: "Dohe"}) }

При успешном результате вы получите:

{   "data": {     "UserCreate": true   } }

Последним шагом, выведем всех пользователей из MongoDB:

{   Users{     firstName     lastName   } }

В ответ мы получим:

{   "data": {     "Users": [       {         "firstName": "John",         "lastName": "Dohe"       },       {         "firstName": "Jane",         "lastName": "Dohe"       }     ]   } }

На этом мы закончили. Возможно, к этой статье вы пришли с базовыми знаниями или нашли для себя что-то новое. Так, начиная с простых вещей о node.js, создания сервера на Koa и подключением к MongoDB, полулось собрать полностью рабочий GraphQL сервер.

Если у вас имеются вопросы или пожелания, я с удовольствием отвечу в комментариях.

Спасибо всем за внимание.
ссылка на оригинал статьи https://habrahabr.ru/post/327816/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *