Перейти к содержимому

13. Variables и Arguments

GraphQL Variables

Variables и Arguments — способы передавать динамические данные в GraphQL запросы.

Каждое поле в схеме может принимать аргументы:

type Query {
user(id: ID!): User
posts(
limit: Int = 20
offset: Int = 0
status: PostStatus
orderBy: String
order: SortOrder = DESC
): PostConnection!
search(query: String!, types: [SearchType!]): [SearchResult!]!
}
type User {
posts(limit: Int = 10, published: Boolean = true): [Post!]!
avatar(size: Int = 200): String! # Аргумент на обычном поле
}

Variables позволяют параметризировать запросы:

# Объявляем переменные в сигнатуре операции
query GetUser($id: ID!) {
user(id: $id) {
name
}
}
# Аргументы с дефолтными значениями
query GetPosts($limit: Int = 10, $offset: Int = 0, $status: PostStatus) {
posts(limit: $limit, offset: $offset, status: $status) {
items {
id
title
}
total
}
}

Передача переменных (отдельный JSON объект):

{
"id": "user-123"
}

Почему не интерполяция?

# ❌ Никогда так не делай (XSS, инъекции)
query {
user(id: "${userId}") { name }
}
# ✅ Правильно — переменные
query GetUser($id: ID!) {
user(id: $id) { name }
}
query Examples(
$id: ID! # Скаляр, обязательный
$name: String # Скаляр, необязательный (null)
$count: Int = 10 # Скаляр с дефолтом
$ids: [ID!]! # Список обязательных ID
$input: UserInput! # Input объект
$role: UserRole # Enum
) {
...
}
// useQuery с переменными
const { data, loading } = useQuery(GET_POSTS, {
variables: {
limit: 10,
offset: 0,
status: 'PUBLISHED',
},
});
// useMutation с переменными
const [createPost] = useMutation(CREATE_POST);
const handleSubmit = () => {
createPost({
variables: {
input: {
title: 'My Post',
body: 'Content...',
},
},
});
};
function PostFilter() {
const [filters, setFilters] = useState({
status: 'PUBLISHED',
orderBy: 'createdAt',
order: 'DESC',
});
const { data, loading } = useQuery(GET_POSTS, {
variables: filters,
// При изменении variables Apollo автоматически делает новый запрос
});
return (
<div>
<select
value={filters.status}
onChange={e => setFilters({ ...filters, status: e.target.value })}
>
<option value="PUBLISHED">Опубликованные</option>
<option value="DRAFT">Черновики</option>
</select>
<select
value={filters.order}
onChange={e => setFilters({ ...filters, order: e.target.value })}
>
<option value="DESC">Сначала новые</option>
<option value="ASC">Сначала старые</option>
</select>
{loading ? <Spinner /> : data?.posts.items.map(/* ... */)}
</div>
);
}
input CreateUserInput {
name: String!
email: String!
password: String!
role: UserRole = READER
}
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
// Передача input объекта
await createUser({
variables: {
input: {
name: 'Иван Иванов',
password: 'securePass123',
role: 'EDITOR',
},
},
});
query ComplexQuery(
$userId: ID!
$postLimit: Int = 5
$postStatus: PostStatus
$commentLimit: Int = 3
) {
user(id: $userId) {
name
posts(limit: $postLimit, status: $postStatus) {
title
comments(limit: $commentLimit) {
body
author {
name
}
}
}
}
}
const resolvers = {
Query: {
posts: async (_, args, { db }) => {
const {
limit = 20,
offset = 0,
status,
orderBy = 'createdAt',
order = 'DESC',
filters = {},
} = args;
const where = {};
if (status) where.status = status;
if (filters.authorId) where.authorId = filters.authorId;
if (filters.tag) where.tags = { contains: filters.tag };
const [items, total] = await Promise.all([
db.posts.findMany({
where,
take: limit,
skip: offset,
orderBy: { [orderBy]: order.toLowerCase() },
}),
db.posts.count({ where }),
]);
return { items, total, hasMore: offset + limit < total };
},
},
User: {
// Аргумент на поле объекта
avatar: (parent, { size = 200 }) => {
return `https://cdn.example.com/avatars/${parent.id}?size=${size}`;
},
posts: (parent, { limit = 10, published = true }, { db }) => {
return db.posts.findMany({
where: {
authorId: parent.id,
...(published ? { status: 'PUBLISHED' } : {}),
},
take: limit,
});
},
},
};

GraphQL валидирует переменные автоматически:

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

Если передать переменные:

{ "id": null } # ❌ Ошибка: ID! не может быть null
{ } # ❌ Ошибка: ID! обязателен
{ "id": "123" } # ✅ OK
  1. Напиши запрос с 5 разными переменными разных типов
  2. Создай аргумент avatar(width: Int, height: Int) на типе User
  3. Реализуй фильтрацию продуктов с переменными (цена, категория, в наличии)
  4. Создай форму с фильтрами, где изменение любого поля запускает новый запрос
  5. Напиши resolver, который принимает сложный input объект и фильтрует БД
  • Arguments — параметры полей в схеме
  • Variables — параметры операции (передаются отдельно от запроса)
  • Всегда используй переменные для динамических данных
  • GraphQL автоматически валидирует типы переменных
  • Input объекты — для сложных аргументов

Следующий урокDirectives →