docs/source/data/operation-best-practices.mdx
When creating queries and mutations, follow these best practices to get the most out of both GraphQL and Apollo tooling.
These two queries fetch the same data:
# Recommended ✅
query GetBooks {
books {
title
}
}
# Not recommended ❌
query {
books {
title
}
}
The first query is named GetBooks. The second query is anonymous.
You should define a name for every GraphQL operation in your application. Doing so provides the following benefits:
These two queries can both fetch a Dog object with ID "5":
# Recommended ✅
query GetDog($dogId: ID!) {
dog(id: $dogId) {
name
breed
}
}
# Not recommended ❌
query GetDog {
dog(id: "5") {
name
breed
}
}
The first query uses a variable ($dogId) for the value of the dog field's required argument. This means you can use the query to fetch a Dog object with any ID, making it much more reusable.
You pass variable values to useQuery (or useMutation) like so:
const GET_DOG = gql`
query GetDog($dogId: ID!) {
dog(id: $dogId) {
name
breed
}
}
`;
function Dog({ id }) {
const { loading, error, data } = useQuery(GET_DOG, {
variables: {
dogId: id,
},
});
// ...render component...
}
Beyond reusability, hardcoded arguments have other disadvantages relative to variables:
If two otherwise identical queries have different hardcoded argument values, they're considered entirely different operations by your GraphQL server's cache. The cache enables your server to skip parsing and validating operations that it's encountered before, improving performance.
The server-side cache also powers features like automatic persisted queries and query plans in a federated gateway. Hardcoded arguments reduce the performance gains of these features and take up useful space in the cache.
The value of a GraphQL argument might include sensitive information, such as an access token or a user's personal info. If this information is included in a query string, it's cached with the rest of that query string.
Variable values are not included in query strings. You can also specify which variable values (if any) are included in metrics reporting to Studio.
One of GraphQL's biggest advantages over a traditional REST API is its support for declarative data fetching. Each component can (and should) query exactly the fields it requires to render, with no superfluous data sent over the network.
If instead your root component executes a single, enormous query to obtain data for all of its children, it might query on behalf of components that aren't even rendered given the current state. This can result in a delayed response, and it drastically reduces the likelihood that the query's result can be reused by a server-side response cache.
In the large majority of cases, a query such as the following should be divided into multiple queries that are distributed among the appropriate components:
<ExpansionPanel title="Click to expand"># Not recommended ❌
query GetGlobalStatus {
stores {
id
name
address {
street
city
}
employees {
id
}
manager {
id
}
}
products {
id
name
price {
amount
currency
}
}
employees {
id
role
name {
firstName
lastName
}
store {
id
}
}
offers {
id
products {
id
}
discount {
discountType
amount
}
}
}
Some fields return the exact same data regardless of which user queries them:
# Returns all elements of the periodic table
query GetAllElements {
elements {
atomicNumber
name
symbol
}
}
Other fields return different data depending on which user queries them:
# Returns the current user's documents
query GetMyDocuments {
myDocuments {
id
title
url
updatedAt
}
}
To improve the performance of your server-side response cache, fetch these two types of fields in separate queries whenever possible. By doing so, your server can cache just a single response for a query like GetAllElements above, while caching separate responses for each user that executes GetMyDocuments.
name and version for metrics reporting (paid)This recommendation is most pertinent to Studio organizations with a paid plan, however it can be helpful for all apps.
</Note>The constructor of ApolloClient accepts the clientAwareness option with name and version properties:
const client = new ApolloClient({
link: new HttpLink({ uri: "http://localhost:4000/graphql" }),
cache: new InMemoryCache(),
clientAwareness: {
name: "MarketingSite",
version: "1.2",
},
});
If you specify these values, Apollo Client automatically adds them to each operation request as HTTP headers (apollographql-client-name and apollographql-client-version).
Then if you've configured metrics reporting in Studio, Apollo Server includes your app's name and version in the operation traces it reports to Studio. This enables you to segment metrics by client.