• /
  • EnglishEspañolFrançais日本語한국어Português
  • 로그인지금 시작하기

Apollo Server plugin and Node.js

중요

Starting with 14.0.0 of the Node.js agent, Apollo Server customers no longer need to install and use @newrelic/apollo-server-plugin. The instrumentation is automatically applied to every Apollo Server instance.

See our Apollo Server Migration Guide for more details.

The New Relic Apollo Server plugin instruments your Apollo Server applications to give visibility into your GraphQL payloads. This helps you uncover and diagnose the cause of your slow GraphQL queries. The supported Apollo Server version is 2.14 or later.

The plugin records overall timings for queries and uses distributed tracing to uncover route problems. Use this instrumentation to see whether the problem arises from resolving a piece of requested data or from work done on other services or databases.

Compatibility

The New Relic plugin works with the following Apollo Server modules:

  • @apollo/server
  • @apollo/gateway
  • @apollo/subgraph
  • apollo-server (>= 2.14)
  • apollo-server-express
  • apollo-server-hapi
  • apollo-server-koa
  • apollo-server-fastify
  • apollo-server-lambda

Other plugins may work, depending on their underlying implementation, but they have not been verified.

Metrics

Two new metrics have been introduced to understand the behavior of your GraphQL operations within and across transactions. Read more on those below or jump down to the Visualizations section to see some recommended ways to use this data.

For more information on querying metrics and creating charts, see the Resources section.

Operation Metrics

/GraphQL/operation/ApolloServer/[operation-type]/[operation-name]/[deepest-unique-path]

Operation metrics include the operation type, operation name, and deepest path. These metrics represent the durations of individual queries or mutations. Use them to compare performance outside the context of individual transactions, which may contain multiple queries.

Operation Type: Indicates if the operation was a query or a mutation.

Operation Name: The operation name when provided or <anonymous>.

Deepest Unique Path: The deepest path included in the selection set of a query where only one field was selected at each level. Since operation names may be reused, this helps further determine uniqueness of a given operation. See the description on the transactions section for more details.

Field Resolve Metrics

/GraphQL/resolve/ApolloServer/[parent-type].[field-name]

Resolve metrics capture the duration spent resolving a particular piece of requested GraphQL data. Use them to find specific resolvers that may contribute to slowing down incoming queries, to distinguish field resolvers with the same name on different types, or to identify the same resolver applied across different types.

These differ slightly in naming from their segment and span counterparts. To better visualize relationships, the full path to a field is represented in segments and spans (for example, libraries.books.title). To understand the duration aggregated across all usages and transactions, these metrics use the field name without the full path.

Field and Argument Metrics

/GraphQL/field/ApolloServer/[parent-type].[field-name] /GraphQL/arg/ApolloServer/[parent-type].[field-name]/[arg-name]

Field metrics are only captured when config.apollo_server.field_metrics is true. Unlike field resolve metrics, this captures every time a field or resolver argument is seen. Use these metrics to determine whether a field in a GraphQL schema is still in use and safe to remove.

All fields and args that have been requested within the last day

FROM Metric SELECT count(newrelic.timeslice.value) where appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/{kind}/ApolloServer/{field}' where kind = 'arg' or kind = 'field' FACET kind, field limit max since 1 day ago

Visualizations

The following queries use these metrics to help you understand the behavior of your Apollo GraphQL applications.

Top 10 Operations

To get a list of the top 10 slowest operations, use the following query on demand or as part of a dashboard.

FROM Metric SELECT average(newrelic.timeslice.value) * 1000 WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/operation/ApolloServer/{operation}' FACET operation LIMIT 10

The Bar chart type gives a visualization similar to the transaction overview.

A Table chart type is also useful for showing a breakdown of operations. Use the METRIC_FORMAT for additional sorting and visualization flexibility. The following query generates columns for operation type, operation name, deepest path, and AVG Duration (MS).

FROM Metric SELECT average(newrelic.timeslice.value) * 1000 as 'AVG Duration (MS)' WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/operation/ApolloServer/{type}/{name}/{deepest-path}' FACET type, name, `deepest-path` LIMIT 20

Average Operation Time

To track the average duration over time for operations, use a similar query with TIMESERIES.

FROM Metric SELECT average(newrelic.timeslice.value) WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/operation/ApolloServer/{operation}' TIMESERIES FACET operation

View this with the Line chart type, which lets you see all operations or toggle individual ones.

Top 10 Resolvers

To get a list of the top 10 slowest resolvers, use the following query on demand or as part of a dashboard.

FROM Metric
SELECT average(newrelic.timeslice.value) * 1000 as 'Average Duration (MS)' WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/resolve/ApolloServer/{type}.{field}' FACET field LIMIT 20

If you would like to include the parent type:

FROM Metric
SELECT average(newrelic.timeslice.value) * 1000 as 'Average Duration (MS)' WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/resolve/ApolloServer/{field}' FACET field LIMIT 20

The Bar chart type gives a visualization similar to the transaction overview. The Table chart type is also useful for showing a breakdown of field and Average Duration (MS).

Average Resolver Time

To track the average duration over time for resolvers, use a similar query with TIMESERIES.

FROM Metric
SELECT average(newrelic.timeslice.value) * 1000 as 'Average Duration (MS)' TIMESERIES WHERE appName = 'YOUR_APP_NAME' WITH METRIC_FORMAT 'GraphQL/resolve/ApolloServer/{field}' FACET field

View this with the Line chart type, which lets you see all resolvers or toggle individual ones.

Resources

Segments and Spans

Segments and spans (when distributed tracing is enabled) are captured for GraphQL operations, field resolution, and additional instrumented work that occurs as part of field resolution, such as making a query to a database.

Operation Segments/Spans

/GraphQL/operation/ApolloServer/[operation-type]/[operation-name]/[deepest-unique-path]

Operation segments and spans include the operation type, operation name, and deepest unique path. These represent the individual duration and attributes of a specific invocation within a transaction or trace.

For more details on the parts, see the transactions section.

Attributes

Name

Description

Default

graphql.operation.type

query or mutation

Included

graphql.operation.name

Name given to the operation or anonymous

Included

graphql.operation.query

The original GraphQL query with arguments obfuscated

Included

To exclude the query attribute (or any attribute), add the attribute name to the attributes exclude list or to the segment and span attributes exclude lists individually.

For more information on including and excluding attributes, see the attributes documentation.

Field Resolve Segments/Spans

/GraphQL/resolve/ApolloServer/[path]

Resolve segments and spans use the resolution path of the individual field to best differentiate within a given trace or transaction. For example, the path libraries.books is used instead of just books. These represent the individual duration and attributes of a specific field being resolved as part of the GraphQL operation.

Attributes

Name

Description

Default

graphql.field.name

Name of the resolved field

Included

graphql.field.returnType

Return type (Book!, [String], etc.) of the resolved field

Included

graphql.field.parentType

Type of the parent of this field ([Book])

Included

graphql.field.path

Full resolve path of the field (libraries.books)

Included

graphql.field.args

Arg passed to the GraphQL query or mutation for this resolver captured as key/value pairs

Excluded

To capture args attributes, add graphql.field.args.* to the attributes include list or to the segment and span attributes include lists individually.

For more information on including and excluding attributes, see the attributes documentation.

Transactions

query {
libraries {
books {
title
author {
name
}
}
}
}

post /query/<anonymous>/libraries.books

Transactions are captured as web transactions, associated with the underlying framework (Express, Koa, etc.), and named based on the GraphQL operations executed.

The agent uses several details to group unique query representations in a transaction name: HTTP method, operation type, operation name, and the deepest path resolved (the first, if multiple).

The raw representation of a transaction looks like the following: /WebTransaction/{framework-name}/POST//{operation-type}/{operation-name}/{deepest-unique-path}

For an Express usage of Apollo Server, that may look like: /WebTransaction/Expressjs/POST//query/<anonymous>/libraries.books

The transaction on New Relic One will ultimately display similar to: post /query/<anonymous>/libraries.books.

Details

HTTP method

The HTTP method for the web request. Requests can arrive via GET or POST, surfacing them similarly to other web transactions.

Operation Type

Indicates if the operation was a query or a mutation.

Operation Name

The operation name when provided or <anonymous>.

query { libraries } would use the operation name <anonymous> because a name was not provided.

A named query such as query GetLibraries { libraries } would use the operation name GetLibraries.

Deepest Unique Path

The deepest path included in the selection set of a query where only one field was selected at each level. Since operation names may be reused, this helps further determine uniqueness of a given operation.

The agent uses the deepest unique path (instead of the deepest path) to avoid making an arbitrary naming decision that might imply or hide details about what causes slowness in an application.

For the query:

query {
libraries {
branch
booksInStock {
isbn
title
author
}
magazinesInStock {
issue
title
}
}
}

The deepest unique path resolves to libraries because multiple fields are selected beyond that point. Any resolver executed beyond that point may contribute to the performance characteristics of the transaction.

If the query selects only one field per resolver, the full path is used because each selection set is unique.

The query:

query {
libraries {
booksInStock {
title
}
}
}

Results in the deepest unique path: libraries.booksInStock.title.

id and __typename fields are automatically excluded from the naming decision.

For example, a federated subgraph query:

query {
libraries {
branch
__typename
id
}
}

Results in the deepest unique path: libraries.branch.

Union Types and Inline Fragments

For union types that use inline fragments, the transaction name uses < ... > brackets to indicate the underlying selected field when only one result type is specified in the query.

For the following schema:

union SearchResult = Book | Author
type Book {
title: String!
}
type Author {
name: String!
}
type Query {
search(contains: String): [SearchResult!]
}

And the following query:

query example {
search(contains: "author") {
__typename
... on Author {
name
}
}
}

Results in the following transaction name:

post /query/example/search<Author>.name

However, if the query returns both Book and Author:

query example {
search(contains: "author") {
__typename
... on Author {
name
}
... on Book {
title
}
}
}

The resulting transaction name is:

post /query/example/search

Naming on Error

Errors parsing or validating a GraphQL request can impact transaction naming.

Validation Errors

If a request parsed but failed validation, the agent names the transaction based on what was attempted. For example, this occurs when a field in the incoming GraphQL query does not exist.

In this situation, the agent uses the parsed document to indicate each intended piece, including calculating the deepest intended path.

Here is an example of querying for a field that does not exist (doesnotexist) and what that looks like in NR One.

query GetBooksByLibrary {
libraries {
books {
doesnotexist {
name
}
}
}
}

post /query/GetBooksByLibrary/libraries.books.doesnotexist.name

Parsing Errors

If a requested operation can't be parsed, the agent names the transaction using a wildcard (*) in place of the usual operation pieces. In this situation, the query is invalid and there are no identifiable pieces to go on.

Here is an example missing a closing } that cannot parse and what that looks like in NR One.

query GetBooksByLibrary {
libraries {
books {
title
author {
name
}
}
}
// missing closing }

post /*

In these situations, the query attribute on the operation span associated with the error is the best way to identify the particular offender.

Batch Queries

Apollo Server supports batch queries. In these situations, there are multiple operations in play that affect naming.

To best identify transaction groupings, the agent aggregates operation names after an additional /batch indicator. These names can be quite long.

Here is an example of a batch query and what that looks like in NR One.

[
{
query: query GetBookForLibrary {
library(branch: "downtown") {
books {
title
author {
name
}
}
}
}
},
{
query: mutation {
addThing(name: "added thing!")
}
}
]

post /batch/query/GetBookForLibrary/library.books/mutation/<anonymous>/addThing

Here you see batch/ followed by query/GetBookForLibrary/library.books and mutation/<anonymous>/addThing.

Copyright © 2026 New Relic Inc.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.