Skip to main content
The Attio App SDK exposes record and workspace data through a GraphQL API. This gives you a typed, flexible way to request exactly the data your app needs.

Two query functions

The SDK provides two functions for running GraphQL queries. Which you use depends on how your component is structured:
  • useQuery() — A React hook that suspends the component while the query is in flight. Best suited for React components that render data directly. Multiple useQuery() calls in the same component run in sequence.
  • runQuery() — An async function you can call imperatively, anywhere you’d await a Promise. Best suited for event handlers and useAsyncCache() definitions. Multiple runQuery() calls within a single useAsyncCache() run in parallel.

Writing queries with GraphQL

When running npm run dev, the development server prompts you to press o to open the GraphQL Explorer — an embedded instance of GraphQL. GraphQL lets you explore the schema and compose queries with autocompletion.
The GraphQL Explorer is useful for writing and validating queries, also the autocompletion and schema introspection work fully. However, it is not connected to any live data source, so executing a query returns null unless there are validation errors.

TypeScript code generation

Both runQuery() and useQuery() accept a plain string as the query argument. However, writing your query in a .graphql or .gql file and importing it unlocks full TypeScript types on the result. The SDK generates a .d.ts declaration file for each query file automatically while npm run dev is running. Do not edit or commit these generated files.
query getCurrentUser {
  currentUser {
    id
    name
    email
  }
}

Custom attributes

Standard object fields are available directly by name in your query. Custom attributes — those defined in Workspace Settings — are accessed via the attribute(slug: $slug) field, using the attribute’s slug. Find an attribute’s slug under the kebab menu for that attribute in Workspace Settings. Because a custom attribute can be any type, the GraphQL schema returns a union. You must include __typename and an inline fragment to narrow the type before reading its value:
get-bluesky.graphql
query getBlueSky($recordId: String!, $slug: String!) {
  person(id: $recordId) {
    attribute(slug: $slug) {
      __typename
      ... on TextValue {
        value
      }
    }
  }
}
You must include __typename and the ... on syntax to match the type of the attribute you are querying.
TypeScript also requires a type guard before you can read the value:
const {person} = useQuery(getBlueSky, {recordId, slug: "bluesky"})

const blueSkyValue = person?.attribute?.__typename === "TextValue" ? person.attribute.value : null

Polymorphic results and fragments

Some queries return a union of object types. The record query, for example, can return a Person, Company, Deal, UserRecord, or WorkspaceRecord. GraphQL handles this with fragments: you declare type-specific fields per type, then switch on __typename at runtime. The record query’s id and object arguments map directly to the recordId and object parameters that record action onTrigger() handlers receive.
query getRecord($recordId: String!, $object: String!) {
  record(id: $recordId, object: $object) {
    id
    __typename
    url
    ...PersonFragment
    ...CompanyFragment
    ...DealFragment
    ...UserFragment
    ...WorkspaceFragment
  }
}

fragment PersonFragment on Person {
  personName: name {
    full_name
  }
}

fragment CompanyFragment on Company {
  companyName: name
}

fragment DealFragment on Deal {
  dealName: name
}

fragment UserFragment on UserRecord {
  person {
    ...PersonFragment
  }
}

fragment WorkspaceFragment on WorkspaceRecord {
  workspaceName: name
}

Example queries

query getCurrentUser {
  currentUser {
    id
    name
    email
  }
}
query getPersonEmailAddresses($recordId: String!) {
  person(id: $recordId) {
    email_addresses
  }
}
query getTeam($recordId: String!) {
  company(id: $recordId) {
    team {
      name {
        full_name
      }
      email_addresses
    }
  }
}