Начать работу с Apollo, Express и MongoDB

В этой статье мы будем настраивать сервер Apollo GraphQL с помощью Express и сохранять данные с помощью MongoDB.

Создайте экземпляр MongoDB

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

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

Инициализировать проект

Нам нужна среда для разработки сервера. Я предполагаю, что вы используете Node.js версии 12 или новее. Мы будем использовать Babel для транскомпиляции кода. Старые версии могут работать, но я их не тестировал.

Создайте новый каталог для проекта. Войдите в каталог и инициализируйте новое приложение.

$ mkdir ~/graphql-server
$ cd ~/graphql-server
$ npm init -y

Теперь нам нужно установить наши зависимости.

$ npm i -D nodemon @babel/cli @babel/core @babel/node @babel/preset-env
$ npm i apollo-server-express express mongoose

Создайте несколько базовых моделей и схем

Мы будем использовать Mongoose для моделирования наших данных и взаимодействия с MongoDB. А пока мы создадим две модели: одну для Author и одну для Book.

./models/Author.js

import mongoose from ‘mongoose’
export const Author = mongoose.model(‘Author’, {
 name: String
})

./models/Book.js

import mongoose from 'mongoose'
export const Book = mongoose.model('Book', {
  name: String,
  pages: Number
})

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

Эта схема описана в приведенном ниже файле как typeDefs, определяя набор типов и их взаимосвязей, и как resolvers, которые подключаются к данным, соответствующим типам схемы (typeDefs) при обработке клиентских запросов. На данный момент, пока мы просто пытаемся заставить все работать, мы определим запрос, который возвращает статическое сообщение. Вскоре мы разберемся с моделями.

./schema/index.js

import { gql } from 'apollo-server-express'
const typeDefs = gql`
  type Query {
    message: String!
  }
`
const resolvers = {
  Query: {
    message: () => 'hello world',
  },
}
export {
  typeDefs,
  resolvers
}

Создайте и запустите сервер

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

Для этого я оборачиваю выражение ожидания в асинхронную функцию. Замените mongodb://localhost:27017/test строкой подключения MongoDB, которую вы сохранили ранее.

./index.js

import { ApolloServer } from 'apollo-server-express'
import express from 'express'
import mongoose from 'mongoose'
import { typeDefs, resolvers } from './schema'
const start= async () => {
  const app = express()
  const server = new ApolloServer({ typeDefs, resolvers })
  server.applyMiddleware({ app })
  await mongoose.connect('mongodb://localhost:27017/test', {
    useNewUrlParser: true
  })
  app.listen({ port: 4000 }, () =>
    console.log(
      `listening: http://localhost:4000${server.graphqlPath}`
    )
  )
}
start()

Мы добавим сценарий запуска в package.json файл.

...
"scripts": {
    "start": "nodemon --exec babel-node index.js"
},
...

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

./.babelrc

{
  "presets": ["@babel/preset-env"]
}

Теперь мы готовы запустить сервер.

$ npm start

Протестируйте конечную точку

Пока для переменной среды NODE_ENV не задано значение production, у вас будет GraphQL Playground (графическая интерактивная IDE GraphQL в браузере), автоматически обслуживающая графический интерфейс для веб-браузеров.

Откройте в браузере http://localhost:4000/graphql.

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

Добавить мутации

Теперь мы подключим наш преобразователь к базовым моделям, предоставив доступ к ним через сервер. Мы используем тип Query для реализации read операций.

Для реализации операций записи нам нужно использовать тип Mutation. Первым аргументом функции Mutation является родительский элемент, и он не используется в этом примере.

./schema/index.js (обновлено)

import { gql } from 'apollo-server-express'
import { Author } from '../models/Author'
import { Book } from '../models/Book'
const typeDefs = gql`
  type Query {
    message: String!
    authors: [Author!]!
    books: [Book!]!
  }
  type Author {
    id: ID!
    name: String!
  }
  type Book {
    id: ID!
    name: String!
    pages: Int
  }
  type Mutation {
    createAuthor(name: String!): Author!
    createBook(name: String!, pages: Int): Book!
  }
`
const resolvers = {
  Query: {
    message: () => 'hello world',
    authors: () => Author.find(),
    books: () => Book.find()
  },
  Mutation: {
    createAuthor: async (_, { name }) => {
      const author = new Author({ name });
      await author.save();
      return author;
    },
    createBook: async (_, { name, pages }) => {
      const book = new Book({ name, pages });
      await book.save();
      return book;
    }
  }
}
export {
  typeDefs,
  resolvers
}

После сохранения файла сервер автоматически перезагружается (благодаря nodemon), и изменения можно наблюдать в браузере (для автозаполнения требуется обновление).

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

Я буду использовать этот пример в других статьях, чтобы обсудить

  • Добавление отношений
  • Аутентификация, авторизация и охрана

Полный код и ссылки