Java for Beginner
775 subscribers
749 photos
220 videos
12 files
1.26K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что такое GraphQL и зачем он появился

Почему REST перестал устраивать и как Facebook придумал альтернативу

Современные приложения всё чаще строятся вокруг API.
Клиентам — будь то браузеры, мобильные приложения или IoT-устройства — нужны данные с сервера.
Долгое время стандартом для этого был REST, но со временем его ограничения стали очевидны.
Именно в ответ на эти ограничения в 2012 году в Facebook родился
GraphQL — язык запросов для API, который изменил способ взаимодействия клиента и сервера.


1. Почему REST перестал всем подходить

REST долгое время считался идеальным способом построения API: простые URL, понятные методы (GET, POST, PUT, DELETE), формат JSON, читаемость.
Но со временем приложения стали сложнее, особенно на фронтенде.


Проблема 1: избыточные данные (overfetching).
REST-эндпоинт /users/42 возвращает всю информацию о пользователе — даже если на экране нужно только имя и аватар.
Клиент не может сказать серверу: “дай только эти два поля”.
В итоге трафик растёт, запросы замедляются, особенно на мобильных устройствах.


Проблема 2: недостаток данных (underfetching).
Обратная ситуация — когда данных не хватает.
Например, чтобы показать пользователя вместе с его постами и комментариями, REST-клиенту придётся сделать три запроса: /users/42, /users/42/posts, /posts/{id}/comments.
Каждый запрос — это отдельное соединение, время, накладные расходы.


Проблема 3: разные клиенты — разные потребности.

Мобильное приложение хочет меньше данных (экономия трафика), веб-клиент — больше.
REST вынуждает сервер создавать несколько версий эндпоинтов:
/users, /users/details, /users/short, и всё это быстро превращается в хаос.


Проблема 4: сложная эволюция.
Добавление новых полей или изменение структуры часто ломает старых клиентов.
Нужны версии: /v1/users, /v2/users, /v3/users.
С ростом проекта версионность превращается в отдельную боль.


Все эти проблемы — прямое следствие того, что REST навязывает структуру данных с серверной стороны.
Клиент получает то, что сервер решил вернуть.
GraphQL переворачивает это с ног на голову.


2. История появления GraphQL

GraphQL был создан внутри Facebook в 2012 году, когда компания столкнулась с резким ростом мобильных клиентов.
REST больше не справлялся: каждое обновление приложения требовало всё больше запросов к серверу, а мобильный интернет не прощал лишних килобайт.

Команда Facebook решила, что сервер не должен диктовать, какие данные возвращать.
Вместо этого клиент должен описывать свои потребности декларативно — “мне нужны вот такие данные, вот в такой структуре”.

В 2015 году Facebook открыл исходный код
GraphQL, и он быстро стал стандартом в индустрии.
Сегодня его используют GitHub, Shopify, Twitter, Netflix, Pinterest, Airbnb и тысячи других компаний.



3. Основная идея GraphQL

Главная концепция
GraphQL проста, но революционна:
клиент сам определяет, какие данные ему нужны.

Клиент не делает серию запросов к разным ресурсам, как в REST.
Он отправляет один запрос на сервер с точным описанием нужных полей, а сервер возвращает данные в том же формате.


Пример запроса:
query {
user(id: 42) {
name
avatar
posts(limit: 3) {
title
likes
}
}
}


Ответ сервера:
{
"data": {
"user": {
"name": "Den",
"avatar": "https://example.com/den.jpg",
"posts": [
{ "title": "gRPC и Java", "likes": 120 },
{ "title": "GraphQL под капотом", "likes": 80 },
{ "title": "Spring Boot и микросервисы", "likes": 95 }
]
}
}
}


Клиент получает ровно то, что запросил — ни байта больше, ни меньше.
Больше нет проблемы overfetching или underfetching.



#Java #middle #GraphQL
👍3
4. Как это работает

В
GraphQL всё держится на схеме (schema).
Она описывает, какие типы данных доступны, какие поля у них есть и какие операции разрешены.


Пример схемы:

type User {
id: ID!
name: String!
avatar: String
posts(limit: Int): [Post]
}

type Post {
id: ID!
title: String!
likes: Int!
}

type Query {
user(id: ID!): User
}


type — описание структуры данных, аналог класса в Java.

Query — “входная точка”, через которую клиент получает данные.

! — обязательное поле.

[Post] — список постов.


GraphQL строго типизирован.
Клиент знает, какие поля доступны и какого они типа, а сервер может валидировать запрос до его выполнения.



5. Ключевые преимущества GraphQL

1. Гибкость запросов
Клиент формирует запрос под себя:

на мобильном — только name и avatar,
в вебе — name, posts, comments.
Сервер не меняется, меняется лишь запрос.


2. Один запрос — много данных
GraphQL позволяет собрать связанные сущности за один вызов.
В REST для этого нужно было несколько запросов, теперь достаточно одного.


3. Строгая типизация
Схема описана декларативно, и клиент может проверять запросы ещё до выполнения.
Ошибки, вроде “такого поля нет” или “тип не совпадает”, ловятся на этапе компиляции.


4. Эволюция без версий
GraphQL не требует /v1, /v2, /v3.
Добавил поле — клиенты, которые его не запрашивают, даже не заметят изменений.
Старые клиенты продолжают работать, новые используют расширенные возможности.


5. Самодокументируемость
GraphQL-сервер знает всю структуру данных.
Это позволяет автоматически генерировать документацию и визуальные IDE вроде GraphiQL или Apollo Sandbox, где можно исследовать схему и писать запросы интерактивно.


6. Почему GraphQL особенно популярен во фронтенде
Фронтенд-разработка — динамичная среда:
разные страницы требуют разные наборы данных, часто меняются требования, нужны быстрые итерации.


GraphQL идеально вписывается в этот процесс, потому что:
Разработчик фронтенда сам решает, какие поля ему нужны.
Не нужно ждать, пока backend добавит новый эндпоинт.
Данные приходят в предсказуемом виде, строго по типам.
Поддерживаются инструменты вроде Apollo Client или Relay, которые кэшируют и синхронизируют данные автоматически.


Именно поэтому GraphQL сегодня стал де-факто стандартом для фронтенд-команд крупных проектов.
REST чаще используется для внешних публичных API, gRPC — для связи микросервисов,
а
GraphQL стал интерфейсом между фронтом и внутренним миром данных.


#Java #middle #GraphQL
👍4
Архитектура и принципы работы GraphQL
Что происходит между клиентом, схемой и источниками данных


1. Главная идея архитектуры

GraphQL-сервер — это прослойка, которая принимает декларативные запросы, проверяет их на соответствие схеме, исполняет нужные резолверы и возвращает результат.

Он не хранит данные сам по себе.
GraphQL — это не база данных, а универсальный интерфейс к любому источнику данных: SQL, NoSQL, микросервисы, REST, файлы, внешние API.
Его задача — связать клиентскую структуру запроса со структурой данных в бэкенде.


Схематично:
Клиент → GraphQL сервер → Источники данных (DB, REST, gRPC, API)


2. Schema — сердце GraphQL

Schema (схема) — это основной контракт между клиентом и сервером.

Она описывает:
какие данные доступны;
какие поля у этих данных есть;
какие операции можно выполнять.


Схема написана на SDL (Schema Definition Language), декларативном языке, напоминающем описание классов.

Пример:
type User {
id: ID!
name: String!
posts: [Post!]!
}

type Post {
id: ID!
title: String!
content: String
}

type Query {
user(id: ID!): User
allUsers: [User!]!
}


Ключевые понятия:
type — определяет структуру данных (аналог Java-класса или DTO).
! — обязательное поле (non-null).
[User!]! — список пользователей, где ни один элемент не равен null.
Query — специальный тип, описывающий операции чтения.


Схема — это не код, а декларация.
На её основе
GraphQL сервер автоматически понимает, как валидировать запросы, какие поля допустимы и какие возвращать ошибки.


3. Query, Mutation, Subscription — три типа операций

GraphQL разделяет все действия на три категории.

1. Query — запрос данных (аналог GET в REST)
Используется для чтения.

Клиент описывает, какие сущности и поля нужны:
query {
user(id: 42) {
name
posts {
title
}
}
}


Сервер вернёт данные в том же формате:
{
"data": {
"user": {
"name": "Den",
"posts": [
{ "title": "gRPC и Java" },
{ "title": "GraphQL под капотом" }
]
}
}
}


2. Mutation — изменение данных (аналог POST/PUT/DELETE)
Mutation обозначает действия, которые модифицируют состояние системы.
Они могут создавать, обновлять или удалять записи.

mutation {
createUser(input: { name: "Alex" }) {
id
name
}
}


GraphQL возвращает результат — обновлённые данные или подтверждение операции.

3. Subscription — подписка на события (реактивный поток)
Subscription создаёт постоянное соединение между клиентом и сервером (обычно через WebSocket).
Когда на сервере происходят изменения, клиент получает уведомления в реальном времени.

subscription {
userCreated {
id
name
}
}


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


4. Как работает запрос GraphQL: путь от клиента до данных

Чтобы понять механику, посмотрим на полный цикл обработки запроса.

Шаг 1. Клиент отправляет запрос

Клиент (например, браузер) отправляет HTTP POST на /graphql с JSON-телом:
{
"query": "query { user(id: 42) { name posts { title } } }"
}

GraphQL-запрос — это декларативное описание структуры данных, не код и не SQL.


Шаг 2. Сервер парсит и валидирует запрос

GraphQL-сервер:
Парсит текст запроса.
Проверяет, что все поля и типы существуют в схеме.
Проверяет типы аргументов (id действительно ID!).
Отклоняет запрос, если нарушен контракт схемы.


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



#Java #middle #GraphQL
👍2
Шаг 3. Сервер вызывает резолверы

Резолвер (resolver) — это функция, которая знает, как получить данные для конкретного поля.


Например, в Java через библиотеку graphql-java:
GraphQLObjectType userType = newObject()
.name("User")
.field(field -> field
.name("id")
.type(Scalars.GraphQLID))
.field(field -> field
.name("name")
.type(Scalars.GraphQLString))
.field(field -> field
.name("posts")
.type(new GraphQLList(postType))
.dataFetcher(env -> postService.getByUser(env.getSource())))
.build();



Резолверы вызываются рекурсивно:

Сначала выполняется user(id: 42).
Затем для каждого пользователя — posts.
Затем для каждого поста — title.

GraphQL умеет оптимизировать выполнение: например, группировать одинаковые вызовы (DataLoader паттерн).

Шаг 4. Формирование ответа

После выполнения всех резолверов GraphQL собирает результат в структуру, повторяющую форму запроса, и отправляет JSON-ответ клиенту:
{
"data": {
"user": {
"name": "Den",
"posts": [{ "title": "gRPC и Java" }]
}
}
}


Если на каком-то шаге произошла ошибка, она не прерывает всё выполнение.

GraphQL вернёт частичные данные + список ошибок:
{
"data": { "user": null },
"errors": [{ "message": "User not found" }]
}



5. Почему GraphQL — не база данных

Эта путаница встречается часто.
GraphQL не хранит и не управляет данными.
Он не заменяет SQL или ORM.


GraphQL — это API-уровень, который:
принимает запросы,
интерпретирует их,
вызывает нужные источники данных,
собирает и возвращает результат в едином формате.


Под капотом это может быть:
JDBC-запросы в PostgreSQL,
вызовы REST API других микросервисов,
gRPC-вызовы,
кэш Redis,
файловая система или внешние API.

GraphQL — унифицированный интерфейс доступа к любым данным, независимо от их источника.


6. Интеграция GraphQL в бэкенд

GraphQL не заменяет ваш backend — он становится поверх него.

В Java это обычно выглядит так:
Spring Boot + graphql-spring-boot-starter — стандартный способ поднять GraphQL-сервер.
Схема (.
graphqls) описывается декларативно.
Резолверы реализуются как обычные Spring-бины.


Пример:
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserService userService;
public UserResolver(UserService userService) { this.userService = userService; }

public User user(Long id) {
return userService.findById(id);
}

public List<User> allUsers() {
return userService.findAll();
}
}


GraphQL сам вызывает нужный метод в зависимости от запроса клиента.

Таким образом,
GraphQL интегрируется поверх существующего слоя сервисов и репозиториев, не требуя переписывания бизнес-логики.

#Java #middle #GraphQL
👍2
Определение схемы в GraphQL (SDL)

SDL (Schema Definition Language) — это декларативный язык описания GraphQL-схем.
Он задаёт структуру данных и операций, доступных клиенту.
SDL не содержит логики — это контракт, который определяет «форму» API: какие типы есть, какие поля у них доступны, какие аргументы принимаются.

GraphQL-сервер использует SDL как единый источник правды для валидации запросов и построения выполнения.


1. Основные виды типов в SDL

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

1. type — объектный тип (основа схемы)

Используется для описания сущностей:
type User {
id: ID!
name: String!
posts: [Post!]!
}

Типы описывают структуру данных — аналог классов/DTO.


2. input — тип вводимых данных

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

input CreateUserInput {
name: String!
age: Int
}


3. enum — перечисления

Хороший способ ограничить варианты.
enum Role {
ADMIN
USER
GUEST
}


4. interface — абстрактный тип

Позволяет описывать общий контракт нескольких типов.
interface Entity {
id: ID!
}

type User implements Entity {
id: ID!
name: String!
}

type Post implements Entity {
id: ID!
title: String!
}

Клиент может запросить id для любого, кто реализует Entity.


5. union — объединение разных типов

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

union SearchResult = User | Post | Comment



2. Как выглядит схема
GraphQL

В GraphQL схема определяется набором корневых операций:
Query — чтение
Mutation — изменение
Subscription — события/стриминг


Пример минимальной структуры:
schema {
query: Query
mutation: Mutation
}

На практике «schema {}» часто опускают — сервер выводит её автоматически.


3. Полный пример схемы: пользователи, посты и комментарии

Ниже — типичная схема блог-платформы, написанная в чистом SDL.
Сущности
type User {
id: ID!
name: String!
role: Role!
posts: [Post!]!
}

type Post {
id: ID!
title: String!
content: String
author: User!
comments: [Comment!]!
}

type Comment {
id: ID!
text: String!
author: User!
}

Перечисления
enum Role {
ADMIN
USER
GUEST
}

Входные данные
input CreatePostInput {
title: String!
content: String
}

Запросы
type Query {
user(id: ID!): User
users: [User!]!

post(id: ID!): Post
posts(limit: Int, authorId: ID): [Post!]!
}

Мутации
type Mutation {
createPost(input: CreatePostInput!): Post!
deletePost(id: ID!): Boolean!
}

Подписки
type Subscription {
postCreated: Post!
}



#Java #middle #GraphQL
👍3
4. Как схема связана с кодом

SDL — декларация.
Сервер должен сопоставить поля типам данных и резолверам.


Java (graphql-java, Spring for GraphQL)

SDL:
type Query {
user(id: ID!): User
}


Резолвер:
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserService service;

public UserResolver(UserService service) {
this.service = service;
}

public User user(Long id) {
return service.findById(id);
}
}


GraphQL автоматически соединяет:
поле user → метод user класса UserResolver
аргумент id → параметр метода


На что важно смотреть в связке схема - код

SDL описывает только форму данных, не логику.
Все поля объектных типов должны иметь резолверы (кроме простых полей, которые
GraphQL просто читает из объекта).
input использует DTO-классы.
enum маппится на Enum-класс.
union и interface требуют регистрации type resolver-а.



5. Эволюция схемы: изменение и поддержка версий

GraphQL спроектирован так, чтобы обеспечивать обратную совместимость.
Важный принцип: старые клиенты должны продолжать работать после обновлений.


1. Добавление полей — безопасная операция

Можно безболезненно добавлять:
новые поля в типы
новые аргументы с default value
новые типы
новые enum-значения (если клиенты готовы к неизвестным значениям)


Пример:
type User {
id: ID!
name: String!
email: String # новое поле
}


2. Деприкация полей

Поле нельзя удалить сразу — сначала его помечают как
@deprecated.
type User {
id: ID!
name: String!

# старое поле
username: String @deprecated(reason: "Use 'name' instead")
}


Клиентский код и инструменты разработчика (GraphiQL, GraphQL Codegen) предупредят о деприкации.

3. Удаление полей

Удалять поле можно только после:
уведомления клиентов,
завершения миграции SDK/фронта,
истечения grace-period'a.


4. Эволюция enum-ов

Добавлять значения — безопасно.
Удалять — нет.


5. Миграции input-типов

Если нужно изменить входные данные:
Старое:
input CreatePostInput {
title: String!
}

Новое:
input CreatePostInput {
title: String!
content: String @deprecated(reason: "Use body instead")
body: String
}


#Java #middle #GraphQL
👍2
Запросы и мутации в GraphQL

GraphQL опирается на две основные операции:
Query — безопасные операции чтения данных.
Mutation — операции изменения состояния (создание, обновление, удаление).


Обе операции используют один и тот же язык запросов, отличаются только семантикой:
Query не влияет на состояние системы, Mutation — влияет.


1. Как выглядит запрос (Query)

GraphQL-запрос — декларативное описание структуры данных, которую клиент хочет получить.

Простой пример:
query {
user(id: 42) {
name
avatar
}
}


Здесь:
query — тип операции (можно опустить: GraphQL сам определит, что это запрос).
user(id: 42) — вызов поля корневого типа Query.
{ name, avatar } — конкретные поля, которые клиент хочет получить.

GraphQL не вернёт дополнительные поля и не допустит отсутствующих — запрос полностью определяет форму ответа.

Ответ будет ровно такой:
{
"data": {
"user": {
"name": "Den",
"avatar": "https://example.com/den.png"
}
}
}

Никаких “лишних” полей.



2. Как работает передача аргументов

Аргументы передаются в круглых скобках и могут быть любого типа, определённого в схеме:
скаляры, enum, input-объекты.

Пример схемы:
type Query {
posts(limit: Int, authorId: ID): [Post!]!
}


Пример запроса:
query {
posts(limit: 5, authorId: 42) {
id
title
}
}


Ответ будет:
{
"data": {
"posts": [
{ "id": "101", "title": "GraphQL SDL" },
{ "id": "102", "title": "gRPC vs GraphQL" }
]
}
}


2.1. Переменные запроса (не хардкодим аргументы)

GraphQL поддерживает variables, что особенно важно для фронтенда.

Запрос:

query GetUser($id: ID!) {
user(id: $id) {
name
avatar
}
}


Передаваемые переменные:
{
"id": 42
}


Сервер объединяет запрос с переменными и выполняет его.

Преимущества переменных:
запрос можно кэшировать,
тело операции не меняется,
безопаснее, чем вставлять значения в строку.



3. Что делает Mutation

Mutation изменяет данные.

Пример схемы:
type Mutation {
createPost(input: CreatePostInput!): Post!
}


Пример запроса:
mutation {
createPost(input: { title: "GraphQL", content: "SDL explained" }) {
id
title
}
}


Mutation возвращает объект результата, содержащий новое состояние или подтверждение.

Ответ:
{
"data": {
"createPost": {
"id": "501",
"title": "GraphQL"
}
}
}


Важный принцип:
Mutation считается одиночной транзакцией.
Даже если внутри происходит много действий,
GraphQL гарантирует их упорядоченное выполнение.


4. Возврат данных в нужной форме

GraphQL всегда возвращает данные:
в структуре, которую запросил клиент
в иерархии, описанной в запросе
строго тех типов, которые указаны в схеме


Пример: вложенная выборка.
query {
user(id: 1) {
name
posts(limit: 2) {
title
comments {
text
}
}
}
}


Ответ:
{
"data": {
"user": {
"name": "Den",
"posts": [
{
"title": "GraphQL Basics",
"comments": [
{ "text": "Отличная статья" }
]
},
{
"title": "SDL Tutorial",
"comments": []
}
]
}
}
}

Сервер не может изменить структуру ответа — она определяется клиентом.



#Java #middle #GraphQL #Query #Mutation
👍2
5. Ошибки и partial responses

GraphQL всегда возвращает JSON-объект с двумя ключами:
data

errors


Что важно:
GraphQL может вернуть часть данных, даже если произошла ошибка.

пример: запрос
query {
user(id: 1) {
name
posts {
title
likes
}
}
}


допустим, поле posts упало из-за ошибки в БД.

Ответ:
{
"data": {
"user": {
"name": "Den",
"posts": null
}
},
"errors": [
{
"message": "Database timeout",
"path": ["user", "posts"]
}
]
}


GraphQL:
не останавливает выполнение всего запроса,
возвращает то, что смог,
указывает путь к проблемному полю.
Это концепция partial response, которой нет ни в REST, ни в gRPC.



6. Как Mutation обрабатывает ошибки

Mutation также может вернуть частичный результат, но чаще ошибка означает, что произошло отклонение операции:

{
"data": {
"createPost": null
},
"errors": [
{
"message": "User not authorized",
"path": ["createPost"]
}
]
}


GraphQL намеренно не использует коды HTTP-статуса, кроме:
200 — запрос обработан
400 — синтаксическая ошибка запроса
500 — ошибка самого сервера
GraphQL (не бизнес-ошибка)


#Java #middle #GraphQL #Query #Mutation
👍1
Фрагменты, директивы и переменные

GraphQL — декларативный язык, но его мощь раскрывается по-настоящему, когда вы начинаете использовать фрагменты, директивы и переменные.

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



1. Фрагменты

Фрагмент (fragment) — это именованный блок запроса, который описывает набор полей.
Он позволяет вынести общую часть и использовать её в нескольких местах.


Главная цель: избежать повторов полей.

Пример без фрагментов:
query {
user(id: 1) {
id
name
avatar
}
post(id: 10) {
author {
id
name
avatar
}
}
}

Повторяются три поля id, name, avatar.


Версия с фрагментом
fragment UserBasic on User {
id
name
avatar
}

query {
user(id: 1) {
...UserBasic
}
post(id: 10) {
author {
...UserBasic
}
}
}

Теперь изменения структуры (например, добавление role) делаются в одном месте — фрагменте.


Как работает фрагмент
fragment UserBasic on User — объявление фрагмента.
User — тип, которому фрагмент принадлежит.
...UserBasic — вставка фрагмента.
Фрагменты можно вкладывать друг в друга и использовать с union и interface.


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

Сложные запросы с глубокой вложенностью
Общие части выносятся в фрагменты.

Работа с interface и union
Позволяют описывать поля для нескольких типов.

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



2. Директивы

Директивы — это аннотации, которые модифицируют выполнение запроса.
Они влияют на структуру данных, которые вернёт сервер.


GraphQL имеет встроенные директивы и позволяет создавать свои.

2.1 Директива @include

Поле будет включено только если условие истинно.
query GetUser($showAvatar: Boolean!) {
user(id: 1) {
name
avatar @include(if: $showAvatar)
}
}

Переменные:

{ "showAvatar": true }

Если showAvatar = false, поле avatar пропадёт из ответа целиком.


2.2 Директива
@skip

Обратная логика:
avatar @skip(if: $binaryMode)

Если binaryMode = true, поле будет пропущено.


2.3 Директива @deprecated

Помечает поле устаревшим.
type User {
username: String @deprecated(reason: "Use 'name' instead")
}

Эта директива влияет на документацию и IDE, но не на данные.


2.4 Кастомные директивы

Пример (схема):
directive @upper on FIELD_DEFINITION


Резолвер может модифицировать строку на лету (например, вернуть верхний регистр).
Это используется для логирования, авторизации, кеширования, маскирования данных.



3. Переменные в запросах

Переменные — это место, где GraphQL становится особенно полезным.
Они позволяют использовать один и тот же запрос с разными параметрами, не переписывая запрос.


3.1 Базовый пример

Запрос:
query GetPost($id: ID!) {
post(id: $id) {
title
content
}
}


Переменные:
{
"id": 100
}


Сервер соединяет тело запроса и переменные и выполняет.

3.2 Переменные для input-объектов (в мутациях)

Мутация:
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
}
}


Переменные:
{
"input": {
"title": "GraphQL",
"content": "Powerful queries"
}
}


Это безопаснее, чем писать input прямо в теле мутации.

3.3 Комбинирование переменных с директивами
query User(
$id: ID!,
$withPosts: Boolean!
) {
user(id: $id) {
name
posts @include(if: $withPosts) {
title
}
}
}


Комбинация:
один запрос,
два режима вывода данных,
ноль дублирования.



#Java #middle #GraphQL #Query
👍2
4. Реальные производственные кейсы

Кейc 1: мобильная и web-версия используют разные наборы полей
query User(
$id: ID!,
$isMobile: Boolean!
) {
user(id: $id) {
name
avatar @skip(if: $isMobile)
posts @include(if: $isMobile) {
title
}
}
}


Мобильный клиент получает минимум данных.
Web — максимум.


Кейc 2: переиспользование структуры пользователя

fragment UserCard on User {
id
name
avatar
}

query PageData {
recommendedUsers { ...UserCard }
recentVisitors { ...UserCard }
followers { ...UserCard }
}


Все блоки страницы используют одну структуру.
CRM-интерфейсы любят такое.


Кейc 3: миграции схемы через директивы

Старая модель:
username @deprecated(reason: "Use 'login' instead")


Frontend получает предупреждение в IDE — никаких тайных поломок.

Кейc 4: сложные фильтры в одном запросе
query FilterPosts($filter: PostFilter!) {
posts(filter: $filter) {
id
title
}
}


Переменные:
{
"filter": {
"authorId": 1,
"tags": ["graphql", "java"],
"minLikes": 10
}
}


#Java #middle #GraphQL #Query
👍2
Реализация GraphQL на сервере


1. Как сервер “понимает” запросы

GraphQL — это не просто формат данных. Это полноценный язык запросов, который сервер интерпретирует строжайшим образом.

На сервере присутствуют три ключевых компонента:

Схема (Schema) — декларативное описание типов, запросов, мутаций и подписок.
Исполнитель (Executor) — механизм, который умеет разбирать, валидировать и выполнять запросы.
Резолверы (resolvers) — функции, которые реально достают данные.


Процесс обработки запроса выглядит так:

Шаг 1. Парсинг
Запрос, полученный в виде строки, разбирается в AST (абстрактное синтаксическое дерево).
GraphQL понимает только корректный язык запросов, поэтому на этом этапе запрос может быть отклонён.

Шаг 2. Валидация
AST сравнивается со схемой.

Сервер проверяет:
существуют ли указанные поля;
правильно ли переданы аргументы;
корректны ли типы;
не запрошены ли деприкейтнутые или отсутствующие типы.
Если запрос не соответствует схеме, сервер вообще не перейдёт к резолверам.


Шаг 3. Выполнение
GraphQL выполняет запрос сверху вниз, проходя по полям и вызывая резолверы там, где они определены.

GraphQL никогда не “догадывается”, где лежат данные.
Он лишь исполняет дерево запроса, вызывая привязанные функции.



2. Роль резолверов (resolvers)

Резолвер — это функция, которая отвечает на вопрос:
“Как получить данные для этого конкретного поля?”


В GraphQL у каждого поля может быть свой резолвер, хотя обычно определяют резолверы для корневых типов: Query, Mutation, Subscription.

Резолвер принимает три основных аргумента:
parent — результат предыдущего резолвера (нужен для вложенных структур);
args — аргументы, которые указал клиент;
context — общий контекст запроса (авторизация, транзакция, соединения с БД).

Пример простого резолвера на Java (Spring Boot + graphql-java-tools):
@Component
public class UserQueryResolver implements GraphQLQueryResolver {

private final UserService service;

public UserQueryResolver(UserService service) {
this.service = service;
}

public User userById(Long id) {
return service.findById(id);
}
}


Резолвер выполняет ровно одну задачу: достать данные.
GraphQL не хранит состояние, не кэширует данные, не соединяется с БД — всё это делает твой код через резолверы.


3. Маппинг схемы на реальные источники данных

GraphQL — это уровень между клиентом и данными.

Источники могут быть любыми:
реляционная БД (PostgreSQL);
документоориентированная БД (MongoDB);
REST API других микросервисов;
gRPC сервисы;
сообщения из Kafka;
кэш Redis.


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

Пример маппинга:


type Query {
user(id: ID!): User
}

type User {
id: ID!
name: String!
posts: [Post!]!
}


Резолверы:
@Component
public class UserResolver implements GraphQLResolver<User> {

private final PostService postService;

public UserResolver(PostService postService) {
this.postService = postService;
}

public List<Post> posts(User user) {
return postService.findByUserId(user.getId());
}
}

GraphQL вызывает posts() только если клиент запросит поле posts.
Если клиент запросит только user { id name }, резолвер posts не будет вызван вообще.


Отсюда следует ключевой принцип:
сервер выполняет только то, что клиент запросил — ни больше, ни меньше.


#Java #middle #GraphQL
👍1
4. Типичная архитектура GraphQL-сервера (Spring Boot)

Структура проекта в Java обычно такая:
/graphql
/schema
schema.graphqls

/resolvers
QueryResolver.java
MutationResolver.java
UserResolver.java
PostResolver.java

/services
UserService.java
PostService.java

/repositories
UserRepository.java
PostRepository.java


Описание этапов:

Схема
Файл .graphqls описывает типы и операции.

Резолверы
Связывают поля схемы с Java-методами.

Сервисы
Логика бизнес-операций.

Репозитории
SQL/NoSQL/REST/gRPC слой, через который сервер реально получает данные.

Context
Сюда кладут:
JWT токен
объект пользователя
транзакции
общие ресурсы



5. Пример полного цикла запроса

Клиент отправляет запрос:

query {
user(id: 10) {
id
name
posts {
id
title
}
}
}


Что делает сервер:
Парсит запрос → AST
Проверяет типы по схеме
Вызывает QueryResolver.user(id=10)
Получает объект User
Чтобы отдать поле posts, вызывает UserResolver.posts(user)
Формирует объект ответа, возвращает клиенту JSON

Клиент всегда получает именно ту форму данных, которую указал.
Сервер не отдаёт лишних полей.



6. Почему GraphQL — это слой поверх данных, а не база данных

GraphQL:
не знает SQL;
не оптимизирует запросы;
не управляет транзакциями;
не индексирует данные;
не проверяет связи между таблицами.


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

Он лишь обеспечивает:
строгую структуру данных (схему)
гибкие запросы от клиента
единый интерфейс ко множеству источников
оптимизацию на уровне поля (через резолверы)

Всё остальное — на стороне backend-логики.



#Java #middle #GraphQL
👍3