Skip to main content
Queries are used to fetch data from the Openlane GraphQL API. This guide covers common query patterns and real examples from the codebase.

Query Structure

Queries are defined using the gql template tag and exported as constants:
import { gql } from 'graphql-request'

export const GET_USER_PROFILE = gql`
  query GetUserProfile($userId: ID!) {
    user(id: $userId) {
      id
      firstName
      lastName
      email
    }
  }
`

Common Query Patterns

Single Item Query

Fetch a single item by ID:
packages/codegen/query/user.ts
export const GET_USER_PROFILE = gql`
  query GetUserProfile($userId: ID!) {
    user(id: $userId) {
      id
      firstName
      lastName
      displayName
      email
      avatarRemoteURL
      avatarFile {
        presignedURL
      }
      setting {
        id
        status
        tags
        isTfaEnabled
        isWebauthnAllowed
        defaultOrg {
          id
          displayName
        }
      }
    }
  }
`
Generated Hook:
const { data: userData } = useGetCurrentUser(userId)

List Query with Edges

Fetch a list of items using the connection pattern:
packages/codegen/query/organization.ts
export const GET_ALL_ORGANIZATIONS = gql`
  query GetAllOrganizations {
    organizations {
      edges {
        node {
          id
          name
          displayName
          avatarRemoteURL
          personalOrg
          avatarFile {
            id
            presignedURL
          }
          stripeCustomerID
          setting {
            identityProviderLoginEnforced
          }
        }
      }
    }
  }
`
Generated Hook:
const { data } = useGetAllOrganizations()
const organizations = data?.organizations?.edges?.map(edge => edge.node) ?? []

Paginated Query

Fetch paginated data with cursor-based pagination:
packages/codegen/query/organization.ts
export const GET_SINGLE_ORGANIZATION_MEMBERS = gql`
  query GetSingleOrganizationMembers(
    $organizationId: ID!
    $first: Int
    $after: Cursor
    $last: Int
    $before: Cursor
  ) {
    organization(id: $organizationId) {
      members(first: $first, after: $after, last: $last, before: $before) {
        edges {
          node {
            id
            createdAt
            role
            user {
              id
              displayName
              authProvider
              avatarRemoteURL
              email
              role
              createdAt
              avatarFile {
                id
                presignedURL
              }
            }
          }
        }
        pageInfo {
          endCursor
          startCursor
          hasPreviousPage
          hasNextPage
        }
        totalCount
      }
    }
  }
`
Usage with Pagination:
const { data } = useGetSingleOrganizationMembers({
  organizationId,
  pagination: {
    page: 1,
    pageSize: 20,
    query: {
      first: 20,
      after: null
    }
  }
})

const members = data?.organization?.members
const hasNextPage = members?.pageInfo?.hasNextPage
const totalCount = members?.totalCount

Filtered Query

Fetch items with filtering and ordering:
packages/codegen/query/organization.ts
export const GET_INVITES = gql`
  query GetInvites(
    $where: InviteWhereInput
    $orderBy: [InviteOrder!]
    $first: Int
    $after: Cursor
    $last: Int
    $before: Cursor
  ) {
    invites(
      where: $where
      orderBy: $orderBy
      first: $first
      after: $after
      last: $last
      before: $before
    ) {
      edges {
        node {
          id
          recipient
          status
          createdAt
          expires
          role
          sendAttempts
        }
      }
      pageInfo {
        startCursor
        endCursor
      }
      totalCount
    }
  }
`
Usage with Filters:
const { data } = useGetInvites({
  where: {
    status: 'PENDING',
    organizationId: orgId
  },
  orderBy: [{ field: 'CREATED_AT', direction: 'DESC' }],
  pagination: {
    page: 1,
    pageSize: 10,
    query: { first: 10 }
  },
  enabled: true
})

Nested Query

Fetch related data in a single query:
packages/codegen/query/organization.ts
export const GET_ALL_ORGANIZATIONS_WITH_MEMBERS = gql`
  query GetAllOrganizationsWithMembers($membersWhere: OrgMembershipWhereInput) {
    organizations {
      edges {
        node {
          id
          personalOrg
          displayName
          name
          avatarRemoteURL
          avatarFile {
            id
            presignedURL
          }
          members(where: $membersWhere) {
            edges {
              node {
                id
                role
                user {
                  id
                }
              }
            }
          }
        }
      }
    }
  }
`
Usage:
const { data } = useGetAllOrganizationsWithMembers({
  userId: currentUserId
})

Specific Field Query

Fetch only specific fields for performance:
packages/codegen/query/organization.ts
export const GET_ORGANIZATION_NAME_BY_ID = gql`
  query GetOrganizationNameByID($organizationId: ID!) {
    organization(id: $organizationId) {
      name
      displayName
    }
  }
`

Using Queries in Components

Here’s a real example from the codebase:
apps/console/src/components/pages/protected/profile/user-settings/profile-name-form.tsx
import { useGetCurrentUser, useUpdateUser } from '@/lib/graphql-hooks/user'
import { useSession } from 'next-auth/react'

const ProfileNameForm = () => {
  const { data: sessionData } = useSession()
  const userId = sessionData?.user.userId

  // Query user data
  const { data: userData } = useGetCurrentUser(userId)

  // Access the data
  const firstName = userData?.user.firstName || ''
  const lastName = userData?.user.lastName || ''
  const displayName = userData?.user.displayName || ''
  const email = userData?.user.email || ''

  return (
    <form>
      <input value={firstName} />
      <input value={lastName} />
      <input value={displayName} />
      <input value={email} />
    </form>
  )
}

Query Hook Options

Generated query hooks support React Query options:
const { data, isLoading, error, refetch } = useGetCurrentUser(userId, {
  enabled: !!userId,        // Only run when userId exists
  refetchOnMount: true,     // Refetch on component mount
  refetchOnWindowFocus: false,  // Don't refetch on window focus
  staleTime: 5000,          // Data is fresh for 5 seconds
})

Common Options

OptionDescription
enabledWhether the query should run
refetchOnMountRefetch when component mounts
refetchOnWindowFocusRefetch when window regains focus
staleTimeHow long data stays fresh (ms)
cacheTimeHow long to keep unused data (ms)
retryNumber of retry attempts on failure

Conditional Queries

Only fetch data when conditions are met:
const { data: orgData } = useGetOrganizationNameById(
  organizationId,
  {
    enabled: !!organizationId  // Only fetch if ID exists
  }
)

Query States

Handle different query states:
const { data, isLoading, isError, error } = useGetCurrentUser(userId)

if (isLoading) {
  return <Spinner />
}

if (isError) {
  return <ErrorMessage error={error} />
}

if (!data?.user) {
  return <NotFound />
}

return <UserProfile user={data.user} />

Cache Management

Queries are automatically cached by React Query using query keys:
// Query key: ['user', userId]
const { data } = useGetCurrentUser(userId)

// Query key: ['organizations']
const { data } = useGetAllOrganizations()

// Query key: ['invites', where, orderBy, page, pageSize]
const { data } = useGetInvites({ where, orderBy, pagination })

Manual Refetch

const { data, refetch } = useGetCurrentUser(userId)

// Manually refetch
await refetch()

Invalidate Cache

import { useQueryClient } from '@tanstack/react-query'

const queryClient = useQueryClient()

// Invalidate all user queries
queryClient.invalidateQueries({ queryKey: ['user'] })

// Invalidate specific user query
queryClient.invalidateQueries({ queryKey: ['user', userId] })

Best Practices

Request Only Needed Fields

Only query the fields you need to reduce payload size and improve performance.

Use Fragments for Reuse

Define GraphQL fragments for commonly requested field sets.

Enable Conditionally

Use the enabled option to prevent queries from running until dependencies are ready.

Handle Loading States

Always handle loading, error, and empty states in your components.

Leverage Caching

Use React Query’s caching to avoid unnecessary network requests.

Build docs developers (and LLMs) love