Skip to Content
03 Queries MutationsBasic Queries

Basic Queries

Learn how to write GraphQL queries with Zeus’s type-safe API.

Your First Query

The simplest Zeus query:

import { Chain } from './zeus'; const chain = Chain('https://api.com/graphql'); const result = await chain('query')({ user: [ { id: '123' }, // Arguments { // Selected fields id: true, name: true, email: true, }, ], }); console.log(result.user.name); // Fully typed!

Query Anatomy

Every Zeus query has three parts:

await chain('query')({ fieldName: [ { /* arguments */ }, { /* field selections */ }, ], });

1. Operation Type

// Query - read data chain('query')({ /* ... */ }); // Mutation - modify data chain('mutation')({ /* ... */ }); // Subscription - real-time updates chain('subscription')({ /* ... */ });

2. Arguments

Pass arguments in the first array element:

{ user: [ { id: '123' }, // Arguments object { name: true }, ]; }

3. Field Selection

Select fields in the second array element:

{ user: [ { id: '123' }, { // Select these fields id: true, name: true, email: true, }, ]; }

Field Selection

Scalar Fields

Select scalar fields with true:

const result = await chain('query')({ user: [ { id: '123' }, { id: true, // String name: true, // String age: true, // Number active: true, // Boolean createdAt: true, // DateTime (string) }, ], });

No Arguments

For fields without arguments, omit the array syntax:

const result = await chain('query')({ currentUser: { // No arguments needed id: true, name: true, email: true, }, });

Nested Objects

Select fields on nested objects:

const result = await chain('query')({ user: [ { id: '123' }, { name: true, profile: { // Nested object avatar: true, bio: true, location: { // Deeply nested city: true, country: true, }, }, }, ], }); console.log(result.user.profile.location.city);

Multiple Fields

Query multiple root fields:

const result = await chain('query')({ user: [{ id: '123' }, { name: true, email: true }], posts: [{ first: 10 }, { title: true, content: true }], categories: { name: true, slug: true, }, }); // Access all results console.log(result.user.name); console.log(result.posts); console.log(result.categories);

Arrays and Lists

Query list fields:

const result = await chain('query')({ users: { // Returns array of users id: true, name: true, email: true, }, }); // Type: { users: Array<{ id: string; name: string; email: string }> } result.users.forEach((user) => { console.log(user.name); });

With Arguments

const result = await chain('query')({ users: [ { first: 10, // Pagination orderBy: 'CREATED_AT', filter: { active: true }, }, { id: true, name: true, }, ], });

Pagination

Offset-Based Pagination

const result = await chain('query')({ posts: [ { limit: 20, offset: 40, // Skip first 40 }, { id: true, title: true, }, ], });

Cursor-Based Pagination (Relay)

const result = await chain('query')({ posts: [ { first: 20, after: 'cursor-string', }, { edges: { cursor: true, node: { id: true, title: true, content: true, }, }, pageInfo: { hasNextPage: true, endCursor: true, }, }, ], }); // Check for more pages if (result.posts.pageInfo.hasNextPage) { const nextPage = await chain('query')({ posts: [ { first: 20, after: result.posts.pageInfo.endCursor, }, { /* ... same selections ... */ }, ], }); }

Load All Pages

async function fetchAllPosts() { const allPosts = []; let hasMore = true; let cursor = null; while (hasMore) { const result = await chain('query')({ posts: [ { first: 100, after: cursor, }, { edges: { cursor: true, node: { id: true, title: true, }, }, pageInfo: { hasNextPage: true, endCursor: true, }, }, ], }); allPosts.push(...result.posts.edges.map((e) => e.node)); hasMore = result.posts.pageInfo.hasNextPage; cursor = result.posts.pageInfo.endCursor; } return allPosts; }

Filtering

Simple Filters

const result = await chain('query')({ users: [ { where: { role: 'ADMIN', active: true, }, }, { id: true, name: true, }, ], });

Complex Filters

const result = await chain('query')({ posts: [ { where: { AND: [ { status: 'PUBLISHED' }, { OR: [{ featured: true }, { viewCount_gt: 1000 }], }, ], }, }, { id: true, title: true, }, ], });

Sorting

const result = await chain('query')({ posts: [ { orderBy: 'CREATED_AT_DESC', }, { id: true, title: true, createdAt: true, }, ], }); // Multiple sort fields const result2 = await chain('query')({ users: [ { orderBy: ['ROLE_ASC', 'NAME_ASC'], }, { name: true, role: true, }, ], });
const result = await chain('query')({ searchPosts: [ { query: 'GraphQL Zeus', }, { id: true, title: true, excerpt: true, score: true, // Relevance score }, ], });

Autocomplete

const suggestions = await chain('query')({ searchUsers: [ { query: 'joh', // Partial match limit: 5, }, { id: true, name: true, avatar: true, }, ], });

Aggregations

Count

const result = await chain('query')({ postsCount: true, // Simple count users: [ { where: { active: true } }, { totalCount: true, // Connection count edges: { node: { id: true }, }, }, ], }); console.log(result.postsCount); console.log(result.users.totalCount);

Statistics

const result = await chain('query')({ postStats: { count: true, avg: { viewCount: true }, sum: { viewCount: true }, min: { createdAt: true }, max: { createdAt: true }, }, }); console.log('Average views:', result.postStats.avg.viewCount); console.log('Total views:', result.postStats.sum.viewCount);

Conditional Fields

Select fields conditionally:

const includeProfile = true; const result = await chain('query')({ user: [ { id: '123' }, { id: true, name: true, ...(includeProfile && { profile: { avatar: true, bio: true, }, }), }, ], });

Reusing Queries

Function Wrapper

async function getUser(userId: string) { return chain('query')({ user: [ { id: userId }, { id: true, name: true, email: true, }, ], }); } const user = await getUser('123');

With Options

type UserQueryOptions = { includeProfile?: boolean; includePosts?: boolean; }; async function getUser(userId: string, options: UserQueryOptions = {}) { return chain('query')({ user: [ { id: userId }, { id: true, name: true, email: true, ...(options.includeProfile && { profile: { avatar: true, bio: true }, }), ...(options.includePosts && { posts: [{ first: 10 }, { title: true }], }), }, ], }); } const user = await getUser('123', { includeProfile: true, includePosts: true, });

Error Handling

Try-Catch

try { const result = await chain('query')({ user: [{ id: '123' }, { name: true }], }); console.log(result.user.name); } catch (error) { console.error('Query failed:', error); }

With Retry

async function queryWithRetry<T>(queryFn: () => Promise<T>, maxRetries = 3): Promise<T> { for (let i = 0; i < maxRetries; i++) { try { return await queryFn(); } catch (error) { if (i === maxRetries - 1) throw error; await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, i))); } } throw new Error('Max retries exceeded'); } const result = await queryWithRetry(() => chain('query')({ user: [{ id: '123' }, { name: true }], }), );

Performance Tips

1. Request Only Needed Fields

// ❌ Over-fetching const result = await chain('query')({ user: [ { id: '123' }, { // Requesting everything when you only need name id: true, name: true, email: true, profile: { avatar: true, bio: true, // ... many more fields }, }, ], }); // ✅ Request only what you need const result = await chain('query')({ user: [{ id: '123' }, { name: true }], });
// ❌ Multiple requests const user = await chain('query')({ user: [{ id: '123' }, { name: true }] }); const posts = await chain('query')({ posts: { title: true } }); // ✅ Single request const result = await chain('query')({ user: [{ id: '123' }, { name: true }], posts: { title: true }, });

3. Use Pagination

// ❌ Fetch all at once const result = await chain('query')({ posts: { title: true, content: true }, // Could be thousands }); // ✅ Use pagination const result = await chain('query')({ posts: [{ first: 20 }, { title: true, content: true }], });

Next Steps

Learn About Mutations →

Last updated on