Рубрики
Без рубрики

Учитесь создавать сервер GraphQL с минимальными усилиями

Сегодня в веб-разработке мы будем изучать, как: легко настроить сервер GraphQL с помощью данных Nodejsmock без базы данных, используя приложение JSON-ServerBuild, которое говорит GraphQLHOW APOLLO, сэкономит нам много времени, и все возможное, кто-либо из этих предметов, которые вас интересуют на! Обязательно проверьте

Автор оригинала: Ian Wilson.

Сегодня в веб-разработке мы узнаем, как:

  • Легко установить сервер GraphQL с Nodejs
  • Mock Data без базы данных с помощью JSON-Server
  • Построить приложение CRUD, которое говорит график
  • Как Аполлон экономит нам много времени и усилий

Если какой-либо из этих предметов интересует вас, прочитайте! Обязательно проверьте Исходный код для этого репо Если вы хотите обратиться к заполненному примеру.

Нежное введение

Пару лет назад я повернул свой первый узел HTTP Server с Express. На мой конец потребовалось всего 6 строк кода.

const express = require('express')
const app = express()

app.get('/', function(req, res) { 
  res.send({ hello: 'there' })
})

app.listen(3000, () => 'Listening at http://localhost:3000')

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

Пожары были открыты для бесчисленных учебных пособий и видео при настройке сервера узла, обычно для создания какого-то вроде API Crud REST в записи.

CRUD относится к приложению, серверу или бэкере, которое может создавать, читать, обновлять и удалять – возможно, из реальной базы данных.

Но это 2018 год, мы можем сделать много прохладных вещей.

Давайте заменим отдых с graphql.

Введите graphql.

GraphQl – это декларативная обработка данных и манипулирующий слой, который зарабатывает потребление API на более дружественных клиентов.

Некоторые преимущества потребляющих данных через сервер GraphQL:

  • Вы получаете именно данные, которые вы запрашиваете, указав необходимые поля.
  • Меньше запросов и меньше переосмысления. Запросы GraphQL обычно достаточно конкретны, чтобы не захватить ненужные записи или поля.
  • Сильно напечатанные схемы, в отличие от полей RAW JSON, которые не имеют мнения о том, как возвращаются данные.
  • Детская площадка Graphql для разведки данных, которая поставляется с автозаполнением и встроенной документацией. Если вам нравится работать с Почтальон Вы были бы правы дома с этим интерфейсом.

Эта последняя точка в частности делает намного проще на борт новых разработчиков.

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

Больше в ближайшее время давайте перейдем к кодированию.

Начало работы и установка зависимостей

Давайте начнем с создания каталога и инициализации A Package.json файл.

mkdir social-graphql && cd social-graphql && npm init -y

Наш текстовый стек будет выглядеть так:

  • JavaScript работает с узлом (ни один код клиента сегодня)
  • Babel для написания современных ES6
  • Экспресс для быстрого настройки HTTP-сервера
  • APOLLO Server для всех полезных утилит GraphQL, которые помогают нам настроить сервер и сборки схем
  • JSON-Server для тестирования на поддельном наборе данных (намного проще, чем за запрос настоящей базы данных)
npm install -S express apollo-server-express graphql json-server axios

Кроме того, у нас будут некоторые зависимости разработки, которые помогут нам.

npm install -D babel-cli babel-preset-env nodemon npm-run-all

С зависимости от того, мы можем попасть в кодирование.

Начиная с базового HTTP-сервера

Давайте создадим HTTP-сервер, который обрабатывает индекс маршрута. То есть, если я запускаю сервер и перейдите к http://localhost: 3500 Я должен увидеть сообщение JSON, в отличие от «не могу получить»/«».

Создать index.js файл:

import express from 'express'

const PORT = process.env.PORT || 3500
const app = express()

app.get('/', function(req, res) {
  res.send({ hello: 'there!' })
})

app.listen(PORT, () => `Listening at http://localhost:${PORT}`)

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

Чтобы получить при этом синтаксис импорта, нам понадобится воспользоваться нашей предустановкой Babel. Создайте файл под названием .babelrc а также:

{
  "presets": ["env"]
}

Наконец, для запуска сервера обновите сценарий запуска в Package.json к этому:

"scripts": {
  "dev:api": "nodemon --exec 'babel-node index.js'"
}

А потом введите NPM запустить dev: API в вашем терминале. Навигации по http://localhost: 3500 Вы сможете увидеть ответ, который говорит «Привет: Там!».

В отличие от более типичных Узел index.js в NPM начать Скрипт, мы используем команду dev наряду с Newema, выполняющим узел Babel-Node.

Nodemon перезагружает ваш сервер Dev всякий раз, когда вы сохраняете файлы, чтобы вам не нужно. Обычно он выполняется с Узел , но мы говорим это выполнить с Бабел-узел Таким образом, он обрабатывает наше причудливое импорт ES6.

Обновление до Аполлона

Хорошо, мы собрали базовый HTTP-сервер, который может служить конечными точками отдыха. Давайте обновим его, чтобы служить GraphQL.

import express from 'express'
import { ApolloServer } from 'apollo-server-express'
import { resolvers, typeDefs } from './schema'

const PORT = process.env.PORT || 3500
const app = express()

const server = new ApolloServer({
  typeDefs,
  resolvers,
  playground: true
})

server.applyMiddleware({ app })

app.get('/', (req, res) => {
  res.send({ hello: 'there!' })
})

app.listen(PORT, () =>
  console.log(`Listening at http://localhost:${PORT}/graphql`)
)

Затем, внутри нового файла, который я позвоню schema.js , вставлять:

import { gql } from 'apollo-server-express'

export const typeDefs = gql`
  type Query {
    users: String
  }
`

export const resolvers = {
  Query: {
    users() {
      return "This will soon return users!"
    }
  }
}

Резольверы и схема (определения типа)

Здесь, если вы новичок в работе с GraphQL, вы увидите этот забавный синтаксис, который мы присваиваем на Typedefs Отказ

В ES6 JavaScript мы можем вызвать функцию, используя backticks, как мы с GQL Отказ С точки зрения ванильного JavaScript вы можете прочитать это так:

gql.apply(null, ["type Query {\n users: String \n }"])

По сути, это называет GQL С массивом аргументов. Просто так бывает, что запись многослойных строк удобно при выражении запроса на JSON.

Если вы все еще запускаете сервер, отправляйтесь на http://localhost: 3500/graphql Отказ Здесь вы сможете увидеть фантастический интерфейс для тестирования наших запросов.

Это верно, больше не подбрасывает кудри на неясной конечной точке, мы можем проверить наши запросы с автозаполной, претенцией и встроенной документацией. Это также не имеет поле с Apollo, поэтому вам не нужно устанавливать дополнительные пакеты или приложения.

Теперь давайте сделаем этот запрос немного более интересным.

Реализация запроса Real-World GraphQL: Список пользователей

Перед погружением слишком глубоко в этот раздел, обязательно скопируйте DB.JSON от Этот репозиторий в ваш рабочий каталог наряду с index.js и stchema.js.

Затем обновите скрипты в Package.json :

"scripts": {
  "dev": "npm-run-all --parallel dev:*",
  "dev:api": "nodemon --exec 'babel-node index.js' --ignore db.json",
  "dev:json": "json-server --watch db.json"
}

Повторно запустите сервер с NPM запустить dev и нажмите дальше.

На сервере GraphQL есть концепция root Query Отказ Этот тип запроса – это точка входа для любых запросов Precte данных для нашей схемы GraphQL. Для нас это выглядит так:

type Query {
  users: String
}

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

type Query {
  users: [User] # here the "[]"s mean these are returning lists
  posts: [Post]
  airplanes: [Airplane]
}

Например, если мы хотели определить новый запрос на нашем сервере, нам придется обновить как минимум два места.

  1. Добавьте запрос под типом запроса в определениях нашего типа.
  2. Добавьте функцию Resolver в объекте запроса в наших резольвестах.

Затем нам нужно будет убедиться, что у нас есть правильный тип возврата данных. Для списков пользователей, это означает возвращение массива объектов, каждый с именем, электронной почтой, возрастом, друзьями и идентификатором.

Наша текущая схема имеет запрос наших пользователей, возвращая простую строку. Это не хорошо, как мы ожидаем Пользователь Данные вернется с этого маршрута.

Обновить schema.js следующим образом:

export const typeDefs = gql`
  type User {
    id: ID
    name: String
    age: Int
    email: String
    friends: [User]
  }

  type Query {
    users: [User]
  }
`

Отлично, у нас есть тип пользователя, а корневой запрос, который возвращает несколько списка пользователей.

Давайте обновим Resolver:

export const resolvers = {
  Query: {
    users() {
      return userModel.list()
    }
  }
}

Внутри нашего Resolver мы называем список из УММЕРМОДЕЛЬ , что нам еще предстоит определить.

Внутри нового файла под названием models.js Добавьте следующее:

import axios from 'axios'

class User {
  constructor() {
    this.api = axios.create({
      baseURL: 'http://localhost:3000' // json-server endpoint
    })
  }

  list() {
    return this.api.get('/users').then(res => res.data)
  }
}

export default new User()

Этот класс образует слой абстракции на логике, который напрямую обрабатывает наши данные.

Наконец, на вершине schema.js Добавьте этот импорт:

import userModel from './models'

Вернуться к http://localhost: 3500/graphql, Вставить и запустить этот запрос:

query Users {
  users {
    id
    name
    email
  }
}

Запрос пользователя теперь выглядит немного более захватывающим! Для каждого пользователя в нашем DB.JSON Файл, мы вернули свой идентификатор, имя и адрес электронной почты.

Поскольку мы используем JSON-сервер, размещенный на локальном порту, мы используем модель как если бы она была извлечь данные с удаленного API.

Во многих случаях наша модель будет создавать вызовы базы данных или извлекать данные из магазина клавиш, как Firebase.

Однако с точки зрения клиента они не представляют, как модель получает данные – они знают только о форме данных.

Эта абстракция делает GraphQL идеальным инструментом для разрешения данных из нескольких источников в один запрос.

Друзья друзей: более интенсивный запрос

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

Что если вы хотите получить пользователей, а также всех друзей для конкретного пользователя? Мы хотим запустить такое запрос:

query UsersAndFriends {
  users {
    id
    name
    friends {
      id
      name
    }
  }
}

Для этого обратите внимание на форму данных в нашем DB.JSON Файл: у каждого пользователя есть поле друзей, которое является массивом объектов, которые ключятся по ID.

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

Это звучит как интенсивный вычислений?

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

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

В models.js и это Найти Метод пользовательского класса:

class User {
  constructor() {
    this.api = axios.create({
      baseURL: 'http://localhost:3000' // json-server endpoint
    })
  }

  list() {
    return this.api.get('/users').then(res => res.data)
  }

  find(id) {
    return this.api.get(`/users/${id}`).then(res => res.data)
  }
}

Теперь мы можем использовать этот метод в новом пользователе Resolver. Разница в этом Resolver заключается в том, что она используется, когда она пытается разрешить соединения с определенным типом, друзья здесь.

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

export const resolvers = {
  Query: {
    users() {
      return userModel.list()
    }
  },
  User: {
    friends(source) {
      if (!source.friends || !source.friends.length) {
        return
      }

      return Promise.all(
        source.friends.map(({ id }) => userModel.find(id))
      )
    }
  },
}

В способе друзей источник является родительским значением, с которым вызывается функция Resolver. То есть для пользователя с ID 0, peck montoya, значение источника – это весь объект со списком идентификаторов друзей.

Для корневых запросов источник чаще всего не определен, потому что корневой запрос не разрешен из определенного источника.

Метод друзей возвращает, когда все запросы находятся отдельных пользователей были решены.

Теперь попробуйте запустить этот запрос, если вы не пробовали раньше:

query UsersAndFriends {
  users {
    id
    name
    friends {
      id
      name
    }
  }
}

Мутации: создание пользователя

До сих пор мы только что получали данные. Что если мы хотели мутировать данные?

Давайте начнем с создания пользователя с именем и возрастом.

Посмотрите на эту мутацию:

mutation CreateUser($name: String!, $email: String, $age: Int) {
  createUser(name: $name, email: $email, age: $age) {
    name
    email
    age
  }
}

Некоторые различия на первый взгляд:

  • Обозначим этот код с «мутацией», а не «запрос»
  • мы проходим два набора подобных аргументов

Аргументы являются в основном тип объявлений для переменных, ожидаемых нашим запросом.

Если есть несоответствие между этими типами, и те, которые передаются клиентом, такими как веб или мобильное приложение, сервер GraphQL будет бросать ошибку.

Чтобы получить этот запрос на работу сейчас, давайте сначала обновите класс пользователей в Model.js :

create(data) {
  data.friends = data.friends 
    ? data.friends.map(id => ({ id })) 
    : []

  return this.api.post('/users', data).then(res => res.data)
}

Когда мы сжигаем этот запрос, JSON-Server добавит новый пользователь с данными, которые мы передали.

Теперь обновите schema.js к следующему:

export const typeDefs = gql`

  # other types...

  type Mutation {
    createUser(name: String!, email: String, age: Int): User
  }
`

export const resolvers = {
  // other resolvers...
  Mutation: {
    createUser(source, args) {
      return userModel.create(args)
    }
  }
}

На данный момент запрос должен работать. Но мы можем сделать немного лучше.

Упростите запрос и мутационные аргументы

Вместо того, чтобы выписать каждый лучший аргумент для мутации, мы можем определить Типы ввода Отказ Это сделает будущие мутации и запросы, которые мы пишем более композима.

export const typeDefs = gql`

  # other types...

  input CreateUserInput {
    id: Int
    name: String
    age: Int
    email: String
    friends: [Int]
  }

  type Mutation {
    createUser(input: CreateUserInput!): User
  }
`

export const resolvers = {
  // other resolvers...
  Mutation: {
    createUser(source, args) {
      return userModel.create(args.input)
    }
  }
}

Посмотрите, что если мы хотели реализовать мутацию UpdateUSER, мы, вероятно, могли воспользоваться этим новым типом ввода.

Теперь попробуйте эту мутацию:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    name
    email
    age
    friends {
      id
      name
    }
  }
}

Чтобы заполнить переменные, которые попадают в запрос, нажмите и разверните вкладку с надписью «Переменные запроса» в нижней левой части игровой площадки GraphQL.

Затем введите этот JSON:

{
  "input": {
    "name": "Indigo Montoya",
    "email": "indigomontoya@gmail.com",
    "age": 29,
    "id": 13,
    "friends": [1,2]
  }
}

Предполагая, что все прошло хорошо, вы должны увидеть ответ с пользователем, который мы только что создали. Вы также должны увидеть два пользователя с IDS 1 и 2.

Теперь наш метод создания не полностью завершен – друзья нашего недавно созданного пользователя понятия не имеют представления о том, что наш новый пользователь является их друзьями.

Чтобы создать пользователь со ссылками своим друзьям, нам нужно будет обновить список друзей пользователей, которые также были ссылаются.

Я решим оставить это как упражнение для читателя, если они настолько склонны.

Подключение точек (упаковка)

Обязательно проверьте Исходный код для этого репо Если вы хотите увидеть, как я реализовал Deleuseuser и UseUser Мутации.

Использование GraphQL с APOLLO в моих собственных проектах было взрывом. Я могу честно сказать, что все больше удовольствия для разработки схем и резольдентов GraphQL, чем для реализации HTTP-маршрутных обработчиков.

Если вы хотите узнать больше о GraphQL, проверьте эти публикации на среднем:

Если вы наслаждались этой статьей и хотели бы видеть больше в будущем, дайте мне знать в комментариях и дать мне последующуюся Twitter и Средний !