Skip to content

Fragments

Fragments in GQLB allow you to create reusable query parts that can be shared across multiple operations. GQLB supports both defined fragments and inline fragments with full type safety.

Defined Fragments

Create reusable fragments using b.fragment():

Basic Fragment

typescript
import { b } from './generated'

// Define a fragment for user information
const USER_INFO_FRAGMENT = b.fragment('UserInfo', 'User', (b) => [
  b.id(),
  b.name(),
  b.email(),
  b.avatar(),
])

Using Fragments in Queries

Include fragments using b.__fragment():

typescript
const USER_QUERY = b.query('GetUser', { id: 'ID!' }, (b, v) => [
  b.user({ id: v.id }, b => [
    b.__fragment(USER_INFO_FRAGMENT)
  ])
])

const USERS_LIST = b.query('GetUsers', (b) => [
  b.users(b => [
    b.__fragment(USER_INFO_FRAGMENT)
  ])
])

Generated GraphQL:

graphql
fragment UserInfo on User {
  id
  name
  email
  avatar
}

query GetUser($id: ID!) {
  user(id: $id) {
    __typename
    ...UserInfo
  }
}

query GetUsers {
  users {
    __typename
    ...UserInfo
  }
}

Complex Fragments

Build fragments with nested objects and arguments:

typescript
const POST_DETAILS_FRAGMENT = b.fragment('PostDetails', 'Post', (b) => [
  b.id(),
  b.title(),
  b.content(),
  b.publishedAt(),
  b.author(b => [
    b.id(),
    b.name(),
    b.avatar(),
  ]),
  b.comments({ limit: 5 }, b => [
    b.id(),
    b.content(),
    b.author(b => [
      b.name(),
    ])
  ])
])

Fragment Composition

Combine fragments within other fragments:

typescript
// Base user fragment
const USER_BASE = b.fragment('UserBase', 'User', (b) => [
  b.id(),
  b.name(),
  b.email(),
])

// Extended user fragment that includes the base
const USER_EXTENDED = b.fragment('UserExtended', 'User', (b) => [
  b.__fragment(USER_BASE),
  b.bio(),
  b.website(),
  b.joinedAt(),
])

// Use in query
const USER_PROFILE_QUERY = b.query('GetUserProfile', { id: 'ID!' }, (b, v) => [
  b.user({ id: v.id }, b => [
    b.__fragment(USER_EXTENDED),
    b.posts({ limit: 3 }, b => [
      b.id(),
      b.title(),
    ])
  ])
])

Fragment with Variables

Create fragments that accept variables:

typescript
const PAGINATED_POSTS_FRAGMENT = b.fragment('PaginatedPosts', 'User', (b) => [
  b.posts({ limit: 10, offset: 0 }, b => [
    b.id(),
    b.title(),
    b.publishedAt(),
  ])
])

// Note: Currently GQLB doesn't support variables in fragments directly.
// You would handle this by creating the fragment in the context where variables are available.

Type-Safe Fragment Output

Extract types from fragments:

typescript
import { OutputOf } from './generated'

const USER_FRAGMENT = b.fragment('UserFragment', 'User', (b) => [
  b.id(),
  b.name(),
  b.email(),
])

type UserFragmentData = OutputOf<typeof USER_FRAGMENT>
// Result:
// {
//   readonly id: string
//   readonly name: string
//   readonly email: string | null
//   readonly __typename: "User"
// }

// Use in component props
interface UserCardProps {
  user: UserFragmentData
}

function UserCard({ user }: UserCardProps) {
  return (
    <div>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  )
}

Fragments for Different Types

Create fragments for different GraphQL types:

typescript
// Fragment for Post type
const POST_FRAGMENT = b.fragment('PostInfo', 'Post', (b) => [
  b.id(),
  b.title(),
  b.excerpt(),
])

// Fragment for Comment type  
const COMMENT_FRAGMENT = b.fragment('CommentInfo', 'Comment', (b) => [
  b.id(),
  b.content(),
  b.createdAt(),
])

// Use both in a query
const FEED_QUERY = b.query('GetFeed', (b) => [
  b.posts(b => [
    b.__fragment(POST_FRAGMENT),
    b.comments({ limit: 3 }, b => [
      b.__fragment(COMMENT_FRAGMENT)
    ])
  ])
])

Conditional Fragment Usage

Use fragments conditionally based on your application logic:

typescript
const BASIC_USER = b.fragment('BasicUser', 'User', (b) => [
  b.id(),
  b.name(),
])

const DETAILED_USER = b.fragment('DetailedUser', 'User', (b) => [
  b.id(),
  b.name(),
  b.email(),
  b.bio(),
  b.website(),
])

// Function to build query with different detail levels
function createUserQuery(includeDetails: boolean) {
  return b.query('GetUser', { id: 'ID!' }, (b, v) => [
    b.user({ id: v.id }, b => [
      b.__fragment(includeDetails ? DETAILED_USER : BASIC_USER)
    ])
  ])
}

Fragment Best Practices

1. Naming Conventions

Use descriptive names that indicate the type and purpose:

typescript
const USER_CARD_FRAGMENT = b.fragment('UserCard', 'User', (b) => [
  // Fields for user card component
])

const USER_PROFILE_FRAGMENT = b.fragment('UserProfile', 'User', (b) => [
  // Fields for user profile page
])

2. Fragment Organization

Group related fragments in separate files:

typescript
// fragments/user.ts
export const USER_BASIC = b.fragment('UserBasic', 'User', (b) => [
  b.id(),
  b.name(),
])

export const USER_PROFILE = b.fragment('UserProfile', 'User', (b) => [
  b.__fragment(USER_BASIC),
  b.email(),
  b.bio(),
])

// fragments/post.ts
export const POST_SUMMARY = b.fragment('PostSummary', 'Post', (b) => [
  b.id(),
  b.title(),
  b.excerpt(),
])

3. Component Co-location

Define fragments near the components that use them:

typescript
// components/UserCard.tsx
const USER_CARD_FRAGMENT = b.fragment('UserCard', 'User', (b) => [
  b.id(),
  b.name(),
  b.avatar(),
])

type UserCardProps = {
  user: OutputOf<typeof USER_CARD_FRAGMENT>
}

export function UserCard({ user }: UserCardProps) {
  // Component implementation
}

export { USER_CARD_FRAGMENT }

Fragment Performance

Fragments provide several performance benefits:

1. Code Reuse

Fragments eliminate duplicate field selections across queries.

2. Bundle Size

Shared fragments reduce the overall size of generated GraphQL documents.

3. Maintainability

Changes to fragments automatically propagate to all queries that use them.

Working with GraphQL Clients

Apollo Client

typescript
import { useQuery } from '@apollo/client'

const { data } = useQuery(USER_QUERY.document(), {
  variables: { id: "user-123" }
})

// data is fully typed including fragment fields

Relay

typescript
// Fragments work seamlessly with Relay's fragment system
export const UserCard = createFragmentContainer(UserCardComponent, {
  user: USER_CARD_FRAGMENT.document()
})

Migration from Raw GraphQL

Before: Raw GraphQL Fragments

graphql
fragment UserInfo on User {
  id
  name
  email
}

query GetUser($id: ID!) {
  user(id: $id) {
    ...UserInfo
  }
}

After: GQLB Fragments

typescript
const USER_INFO = b.fragment('UserInfo', 'User', (b) => [
  b.id(),
  b.name(),
  b.email(),
])

const GET_USER = b.query('GetUser', { id: 'ID!' }, (b, v) => [
  b.user({ id: v.id }, b => [
    b.__fragment(USER_INFO)
  ])
])

Next Steps

Released under the MIT License.