Создавать API весело! Особенно, когда вы можете использовать современные технологии, такие как Koa, GraphQL и MongoDB.
Koa – это фреймворк Node, точно так же, как и Express является фреймворком Node. Мы заменим Express на Koa, так как Koa использует синтаксис async/await вместо “коллбэков”.
Заметка; Если вы новичок в API, я рекомендую прочитать эту книгу, чтобы начать работу.
Начинаем
Необходимые условия для создания нашего API:
- Node установлен
- Текстовый редактор (я использую код Visual Studio)
- Терминал
- Браузер
Если у вас есть все, что вам нужно, продолжайте – если нет, пожалуйста, установите их.
Откройте свой терминал и создайте проект node, например, такой:
Итак, мы создали папку нашего проекта и инициализировали новый проект Node. Теперь у нас есть доступные пакеты NPM, которые мы можем использовать для установки Koa, Mongo и GraphQL.
Давайте установим коа с NPM.
npm i koa
Запустить новый сервер Koa просто. Все, что нам нужно – это файл server.js со следующим содержимым:
const Koa = require('koa'); const app = new Koa(); app.listen(9000); app.on('error', err => { log.error('server error', err) });
Начать проект с Node:
Установка GraphQL
Нам нужны два пакета для настройки GraphQL с Koa: koa-mount и koa-graphql
npm i koa-mount koa-graphql
GraphQL требует, чтобы мы передали нашу исходную схему на сервер GraphQL. Давайте создадим один.
Мы помещаем схему graphQL в graphql/schema.js
const { buildSchema } = require('graphql'); const schema = buildSchema(` type Query { hello: String } `); module.exports = schema;
Мы передаем наш начальный Query функции buildSchema.
app.use(mount('/graphql', graphqlHTTP({ schema: schema, graphiql: true })))
Примечание: аргумент для buildSchema является литералом шаблона.
Теперь мы можем передать исходную схему на наш сервер GraphQL:
const mount = require('koa-mount'); const graphqlHTTP = require('koa-graphql'); const schema = require('./graphql/schema')
Не забудьте импортировать koa-mount, koa-graphql и, наконец, schema.js.
И затем, если мы перейдем к localhost:9000/graphql:
Вуаля! Начальная настройка завершена. Пока она не очень полезна – в идеале, мы хотели бы запросить GraphQL, чтобы сохранить данные в нашей MongoDB и прочитать их оттуда.
Настройка MongoDB
Чтобы читать и писать с помощью GraphQL, нам нужно место для чтения. Здесь Mongo как раз и пригодится. Мы будем сохранять и читать наши данные оттуда.
Чтобы упростить ситуацию, мы будем использовать облачный экземпляр для Mongo. Зайдите на mlab.com и создайте базу данных пользователей и монго.
Создание базы данных MongoDB
После того, как вы создали базу данных, вам потребуется пользователь для базы данных.
Создание пользователя MongoDB
Нажмите на вкладку пользователей и создайте новое имя пользователя с паролем.
Теперь вы можете использовать mongoDB в паре с Mongoose. Удаленный URL для вашей базы данных будет выглядеть примерно так:
MongoDB: //: @ ds213615.mlab.com: 13615 / КоА-graphql
Установка mongoose
npm i mongoose
Создание файла database.js
Мы создаем специальный файл для подключения к базе данных:
const mongoose = require('mongoose'); const initDB = () => { mongoose.connect( 'mongodb://indrek:graphql1@ds213615.mlab.com:13615/koa-graphql', { useNewUrlParser: true } ); mongoose.connection.once('open', () => { console.log('connected to database'); }); } module.exports = initDB;
Примечание. Убедитесь, что вы используете имя пользователя и учетные данные для своей базы данных.
Этот блок кода попытается подключиться к удаленному MongoDB..
Откройте server.js и require и запросите метод initDB.
const initDB = require('./database'); initDB();
Если мы все сделали правильно, наша консоль должна сообщить нам, что мы подключились успешно.
Браво!
Заметьте, как досадно постоянно обновлять сервер? Давайте решим это с помощью пакета pm2.
PM2 – это менеджер производственных процессов для приложений Node.js со встроенным балансировщиком нагрузки. Это позволяет вам поддерживать приложения в актуальном состоянии, перезагружать их без простоев и облегчать общие задачи системного администратора.
npm install pm2 -g
Добавьте скрипт с именем start в наш package.json:
"scripts": { "start": "pm2 start server.js" },
Pm2 работает в фоновом режиме, что освобождает наш терминал. Если вы когда-нибудь захотите остановить процесс, просто запустите pm2 kill. Теперь нам не нужно постоянно перезагружать наш сервер, pm2 делает это автоматически.
Примечание: pm2 logs возвращает выражения журнала консоли на терминал.
Модели MongoDB
Если вы когда-либо работали с Mongo, вы знаете, что MongoDB позволяет нам создавать модели для наших данных. Это отличный способ структурировать наши данные.
Создайте папку для моделей MongoDB и внутри файла gadgets.js:
const mongoose = require('mongoose'); const Schema = mongoose.Schema; /* notice there is no ID. That's because Mongoose will assign an ID by default to all schemas */ const GadgetSchema = new Schema({ name: String, release_date: Date, by_company: String, price: Number, }); module.exports = mongoose.model('Gadget', GadgetSchema);
Примечание. В нашей схеме нет поля идентификатора. Это потому, что Mongoose назначит идентификатор по умолчанию для всех схем.
Отлично, теперь давайте добавим коллекцию и несколько фиктивных данных. Название коллекции должно отображать имя нашего гаджета во множественном числе, в данном случае gadgets.
После создания коллекции вставьте документ в формате JSON следующим образом:
Это все для Mongo, теперь давайте получим данные с помощью GraphQL.
Запросы GraphQL
GraphQL также требует от нас создания типов (наподобие инструкций для компьютеров).
graphql/gadgetType.js
const graphql = require('graphql'); const { GraphQLObjectType, GraphQLString } = graphql; const GadgetType = new GraphQLObjectType({ name: 'Gadget', fields: () => ({ }) }); module.exports = GadgetType;
Обратите внимание, что мы создали тип graphql. Внутри полей мы можем указать свойства данного типа.
const graphql = require('graphql'); const { GraphQLObjectType, GraphQLObjectType } = graphql; const GadgetType = new GraphQLObjectType({ name: 'Gadget', fields: () => ({ id: { type: GraphQLString }, name: { type: GraphQLString }, release_date: { type: GraphQLString }, by_company: { type: GraphQLString }, price: { type: GraphQLString } }) }); module.exports = GadgetType;
Обратите внимание на типы GraphQLObjectType и GraphQLObjectType, которые мы деконструируем из graphQL. Это примитивные типы для graphQL.
Создание типов GraphQL также предоставляет подсказки типов, которые мы будем использовать при создании наших запросов.
Последнее, что нам нужно сделать – это рефакторинг нашего schema.js. Мы хотим запросить гаджет по id.
Импортируйте модель Gadget, gadgetGraphQLType graphql типа и GraphQLSchema, GraphQLObjectType, GraphQLString из graphQL в schema.js.
const { GraphQLSchema, GraphQLObjectType, GraphQLString} = require('graphql'); const gadgetGraphQLType = require('./gadgetType'); const Gadget = require('../models/gadget');
Далее нам нужен корневой запрос. Каждый запрос GraphQL начинается с фигурных скобок {} – это корневой запрос.
const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { } })
Вуаля! Внутри полей мы можем указать gadget запрос.
const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { gadget: { type: gadgetGraphQLType, args: { id: { type: GraphQLString }}, resolve(parent, args) { return Gadget.findById(<a href="http://args.id" class="link link-url" target="_blank" rel="external nofollow noopener noreferrer">args.id</a>) } } } })
Обратите внимание на три свойства внутри запроса гаджета:
- тип – это тип запроса, в данном случае gadgetGraphQLType.
- аргументы – мы можем предоставить аргументы для запросов graphql, например: gadgets (id: “1”)
- решение – как мы хотим разрешить запрос? Что должно произойти после выполнения запроса? Здесь мы возвращаем модель гаджета по id.
И наконец, экспортируйте.
module.exports = new GraphQLSchema({ query: RootQuery });
Файл schema.js должен выглядеть следующим образом.
const { GraphQLSchema, GraphQLObjectType, GraphQLString} = require('graphql'); const gadgetGraphQLType = require('./gadgetType'); const Gadget = require('../models/gadget'); const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { gadget: { type: gadgetGraphQLType, args: { id: { type: GraphQLString }}, resolve(parent, args) { return Gadget.findById(<a href="http://args.id" class="link link-url" target="_blank" rel="external nofollow noopener noreferrer">args.id</a>) } } } }) module.exports = new GraphQLSchema({ query: RootQuery });
Теперь зайдите на http://localhost:9000/graphql и сделайте запрос.
{ gadget(id: "5c4e188efb6fc05326ad9264") { name price by_company release_date id } }
Это то, что мы должны в конечном итоге:
Спасибо за чтение! ❤