Использование Apollo Server вместе с TypeScript
В этом небольшом руководстве мы рассмотрим использование TypeScript вместе с Apollo Server Express
Содержание
- Описание списка зависимостей
- Структура проекта
- Предварительная настройка
- Создание Type Definitions
- Создание Resolvers
- Создание схемы
- Использование Apollo Server Express
- Заключение
💡 Данное руководство расчитано на тех разработчиков, кто уже имел небольшой опыт с использованием технологий Apollo Server и TypeScript.
Описание списка зависимостей
Для установки необходимых npm пакетов используйте команды ниже, но перед этим создайте папку проекта с любым названием и инициализируйте npm вместе с конфигом typescript:
mkdir typescript-apollo-server
cd ./typescript-apollo-server
// инициализация npm
npm init -y
// установка dev зависимостей
npm install -D @types/node prettier ts-node ts-node-dev typescript
// установка основных зависимостей
npm install @types/graphql-depth-limit apollo-server-express cors dotenv express graphql graphql-depth-limit graphql-tools
// инициализировать файл с настройками typescript
npx tsc --init
Теперь по порядку узнаем для чего нужны эти пакеты:
devDependencies:
- @typesnode - устанавливаем type definitions node.js для нашего typescript ~717 kB
- prettier - этот пакет нужен для форматирования вашего кода, вы можете установить его по желанию ~9.08 MB
- ts-node - данные пакет предназначен для транспиляции typescript кода в javascript ~377 kB
- ts-node-dev - отладка кода typescript. этот пакет будет следить за вашими файлами .ts и перезагружать сервер при их изменении (можно использовать nodemon) ~57 kB
- typescript - сам typescript ~56.8 MB
dependencies:
- apollo-server-express - GraphQL сервер вместе в веб-фреймворком Express.js ~70.1 kB
- express - веб-фреймворк Express.js ~208 kB
- graphql - среда выполнения запросов к нашей API ~1.92 MB
- graphql-depth-limit - защита он неограниченных запросов в API, подробнее здесь
- @types/graphql-depth-limit - type definitions graphql-depth-limit
- graphql-tools - пакет, который будет создавать нашу GraphQL схему ~18 kB
- dotenv - пакет, который будет загружать переменные среды из файла .env в process.env - ~23.1 kB
- cors - для включения кросс доменных запросов - ~20 kB
Структура проекта
Теперь после того как все пакеты установлены, определимся со структурой проекта:
typescript-apollo-server
├── graphql
│ ├── resolvers - обработчики запросов
│ │ ├── Books
│ │ │ └── Books.ts
│ │ └── Users
│ │ └── Users.ts
│ ├── typeDefs - типы
│ │ ├── Book.graphql
│ │ ├── User.graphql
│ │ └── schema.graphql
│ └── schema.ts
├── index.ts - сервер
├── package.json
└── tsconfig.json
В качестве простого примера GraphQL сервера, будем создавать несколько resolvers - это Books и Users.
Предварительная настройка
Перед тем как начать разрабатывать наш сервер, зайдем в package.json и установим скрипты запуска:
"scripts": {
"start": "ts-node index.ts",
"server": "ts-node-dev --respawn index.ts"
},
Теперь необходимо создать схему, создаем папку graphql и в ней также создаем еще две папки resolvers и typeDefs, также не забываем создать файл schema.ts:
mkdir graphql
cd ./graphql
mkdir resolvers
mkdir typeDefs
touch schema.ts
Создание Type Definitions
Далее определим type definitions, перейдем в папку typeDefs и создадим 3 файла schema.graphql, User.graphql и Book.graphql.
cd ./typeDefs
touch schema.graphql
touch User.graphql
touch Book.graphql
Далее напишем наши типы. Обратите внимание что для удобства и разделения кода мы будем использовать импорты. Возможность использовать импорты нам предоставляет пакет graphql-tools. Для того чтобы импортировать файл .graphql ставится знак # и указывается путь.
Главный файл schema.graphql:
# ./graphql/typeDefs/schema.graphql
# import Book from './Book.graphql'
# import User from './User.graphql'
type Query {
books: [Book]
users: [User]
}
Файл Book.graphql:
# ./graphql/typeDefs/Book.graphql
type Book {
id: String
title: String
}
Файл User.graphql:
# ./graphql/typeDefs/User.graphql
type User {
id: String
name: String
}
Создание Resolvers
С типами определились, теперь напишем простые резолверы для наших запросов, в качестве БД будем использовать простой массив с данными прямо в файле .ts для быстроты (вы например можете подключить любую БД и уже с ней взаимодействовать).
Заходим в папку resolvers и создаем файл с названием index.ts и две папки Books и Users, далее в этих папках создаем соответствующий файл.
cd ./resolvers
touch index.ts
mkdir Books
mkdir Users
cd ./Books
touch Books.ts
cd ..
cd ./Users
touch Users.ts
cd ..
Наконец прописываем обработчики:
Файл Books.ts:
// ./graphql/resolvers/Books/Books.ts
import { IResolvers } from "graphql-tools"
interface BookType {
id: string;
title: string;
}
const books = [
{ id: "1", title: "First Book" },
{ id: "2", title: "Second Book" },
]
const booksResolver: IResolvers = {
Query: {
books: (): BookType[] => books,
},
}
export default booksResolver
Файл Users.ts:
// ./graphql/resolvers/Users/Users.ts
import { IResolvers } from "graphql-tools"
interface UserType {
id: string;
name: string;
}
const users = [
{ id: "1", name: "John" },
{ id: "2", name: "Ivan" },
]
const usersResolver: IResolvers = {
Query: {
users: (): UserType[] => users,
},
}
export default usersResolver
Резолверы получились довольно простые и понятные, теперь нужно собрать их в один массив для дальнейшего экспорта. В файле index.ts прописываем следующее:
// ./graphql/resolvers/index.ts
import { IResolvers } from "graphql-tools"
import booksResolver from "./Books/Books"
import usersResolver from "./Users/Users"
const resolvers: IResolvers[] = [usersResolver, booksResolver]
export default resolvers
Создание схемы
Как говорилось ранее, пакет graphql-tools предназначен для создания схемы. Теперь перейдем в наш файл schema.ts и соберем нашу схему.
Раньше для того чтобы работали наши graphql импорты мы применяли пакет graphql-import, но теперь его не нужно устанавливать так как он уже входит в состав graphql-tools. Подробнее об этом можно прочитать здесь. С помощью функций loadSchemaSync, loadFilesSync, и addResolversToSchema мы собираем нашу схему. Функция mergeResolvers предназначена для объединения наших резолверов. Подробнее о "Resolvers merging" здесь. Далее всё что нам остается сделать - это экспортировать нашу схему и написать сервер.
// ./graphql/schema.ts
import {
mergeResolvers,
loadSchemaSync,
loadFilesSync,
GraphQLFileLoader,
addResolversToSchema,
} from "graphql-tools"
import { GraphQLSchema } from "graphql"
const schema = loadSchemaSync(`${__dirname}/typeDefs/schema.graphql`, {
loaders: [new GraphQLFileLoader()],
})
const resolvers = loadFilesSync(`${__dirname}/resolvers`)
const schemaWithResolvers: GraphQLSchema = addResolversToSchema({
schema,
resolvers: mergeResolvers(resolvers),
})
export default schemaWithResolvers
Использование Apollo Server Express
Создаем в корне проекта файл с названием index.ts.
touch index.ts
Импортируем все необходимые пакеты и напишем функцию startServer, которая будет запускать наш сервер. Сделаем это согласно документации к пакету apollo-server-express.
// ./index.ts
import express, { Application, Request, Response } from "express"
import cors from "cors"
import depthLimit from "graphql-depth-limit"
import { ApolloServer } from "apollo-server-express"
import schema from "./graphql/schema"
const PORT = process.env.PORT || 4000
const startServer = (): void => {
try {
const app: Application = express()
const server: ApolloServer = new ApolloServer({
schema,
validationRules: [depthLimit(10)], // выставляем правило deph limit = 10
playground: true,
})
app.use("*", cors())
app.get("/", (req: Request, res: Response) => res.send("GraphQL API"))
server.applyMiddleware({ app })
app.listen({ port: PORT }, () =>
console.log(`🚀 Server ready on port: ${PORT}`)
)
} catch (err) {
console.log(`❌ Something went wrong: \n ${err}`)
}
}
startServer()
Как видим, ничего сложного нет, просто применяем функцию applyMiddleware для того чтобы соединить Apollo Server с фрэймворком Express.js. Далее можно протестировать наш сервер, запускаем команду npm run start и переходим по адресу /graphql. Для тестирования можно выполнить вот такой запрос:
{
books {
id
title
}
users {
id
name
}
}
Ответ мы получаем в таком виде:
{
"data": {
"books": [
{
"id": "1",
"title": "First Book"
},
{
"id": "2",
"title": "Second Book"
}
],
"users": [
{
"id": "1",
"name": "John"
},
{
"id": "2",
"name": "Ivan"
}
]
}
}
Заключение
Как видим, использование Apollo Server вместе с TypeScript оказалась совсем не сложной задачей, так как и Apollo Server и пакет graphql-import имеют поддержку typescript из коробки.