Что такое GraphQL
▸Определение
GraphQL — язык запросов для API и runtime для выполнения этих запросов. Клиент запрашивает только те данные, которые нужны.
▸GraphQL vs REST
Apollo Server
▸Установка
1npm install @apollo/server graphql
▸Создание сервера
1import { ApolloServer } from '@apollo/server';2import { startStandaloneServer } from '@apollo/server/standalone';34const server = new ApolloServer({5 typeDefs,6 resolvers,7});89const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
Schema Definition Language (SDL)
▸Типы
1type User {2 id: ID!3 name: String!4 email: String!5 posts: [Post!]!6}78type Post {9 id: ID!10 title: String!11 content: String!12 author: User!13}1415type Query {16 users: [User!]!17 user(id: ID!): User18 posts: [Post!]!19}2021type Mutation {22 createUser(name: String!, email: String!): User!23 updateUser(id: ID!, name: String): User!24 deleteUser(id: ID!): Boolean!25}2627type Subscription {28 postCreated: Post!29}
Резолверы
▸Базовые резолверы
1const resolvers = {2 Query: {3 users: async () => {4 return await prisma.user.findMany();5 },6 user: async (_, { id }) => {7 return await prisma.user.findUnique({ where: { id: parseInt(id) } });8 },9 },10 Mutation: {11 createUser: async (_, { name, email }) => {12 return await prisma.user.create({ data: { name, email } });13 },14 },15 User: {16 posts: async (parent) => {17 return await prisma.post.findMany({18 where: { authorId: parent.id },19 });20 },21 },22};
▸Резолверы связей
1const resolvers = {2 Post: {3 author: async (parent) => {4 return await prisma.user.findUnique({5 where: { id: parent.authorId },6 });7 },8 },9 User: {10 posts: async (parent) => {11 return await prisma.post.findMany({12 where: { authorId: parent.id },13 });14 },15 },16};
Запросы и мутации
▸Запрос
1query {2 users {3 id4 name5 email6 posts {7 id8 title9 }10 }11}
▸Мутация
1mutation {2 createUser(name: "Иван", email: "ivan@example.com") {3 id4 name5 }6}
Аргументы и фильтры
▸Схема
1type Query {2 users(filter: UserFilter, limit: Int, offset: Int): [User!]!3}45input UserFilter {6 name: String7 email: String8 role: Role9}
▸Резолвер
1Query: {2 users: async (_, { filter, limit, offset }) => {3 return await prisma.user.findMany({4 where: filter,5 take: limit,6 skip: offset,7 });8 },9},
Подписки (Subscriptions)
▸WebSocket сервер
1import { WebSocketServer } from 'ws';2import { useServer } from 'graphql-ws/lib/use/ws';34const wsServer = new WebSocketServer({ server: httpServer });5useServer({ schema }, wsServer);
▸Резолвер подписки
1import { PubSub } from 'graphql-subscriptions';23const pubsub = new PubSub();45const resolvers = {6 Subscription: {7 postCreated: {8 subscribe: () => pubsub.asyncIterator(['POST_CREATED']),9 },10 },11 Mutation: {12 createPost: async (_, args) => {13 const post = await prisma.post.create({ data: args });14 pubsub.publish('POST_CREATED', { postCreated: post });15 return post;16 },17 },18};
Middleware и аутентификация
▸Context
1const server = new ApolloServer({2 typeDefs,3 resolvers,4 context: ({ req }) => {5 const token = req.headers.authorization || '';6 const user = getUser(token);7 return { user };8 },9});
▸Защита резолверов
1const resolvers = {2 Mutation: {3 deleteUser: async (_, { id }, context) => {4 if (!context.user || context.user.role !== 'ADMIN') {5 throw new Error('Не авторизован');6 }7 return await prisma.user.delete({ where: { id } });8 },9 },10};
DataLoader
▸Проблема N+1
1// Плохо: N+1 запросов2const users = await prisma.user.findMany();3for (const user of users) {4 user.posts = await prisma.post.findMany({5 where: { authorId: user.id },6 });7}
▸DataLoader решение
1import DataLoader from 'dataloader';23const postLoader = new DataLoader(async (ids) => {4 const posts = await prisma.post.findMany({5 where: { authorId: { in: ids } },6 });7 return ids.map(id => posts.filter(p => p.authorId === id));8});910// Использование11User: {12 posts: (parent) => postLoader.load(parent.id),13},
Заключение
GraphQL предоставляет гибкость для клиентов и единую точку входа. Apollo Server — зрелое решение для Node.js. Используйте DataLoader для оптимизации, subscriptions для real-time и middleware для аутентификации.