Update: There is a new version of Optics! Apollo Engine has all the features Optics does plus error tracking, query caching, improved tracing, and more. Engine has different requirements setup from Optics and in order use Engine you need to run the Engine Proxy, which can be easily done via the new Engine NPM Package. Already using Optics? Check out the migration guide for switching to Engine. Optics will continue to be available through early 2018.
Apollo Optics agent for GraphQL-js
Here are the steps to enable Apollo Optics agent in your app. See below for details on each step:
- Install the NPM package in your app:
npm install optics-agent --save
- Import the package in your main js file:
import OpticsAgent from 'optics-agent';
- Get an API key from the Optics web interface and configure the agent. Either:
- Set the
OPTICS_API_KEY
environment variable to your API key - Set the API key and more with
OpticsAgent.configureAgent({ options });
- Set the
- Instrument your app. In any order:
- Instrument your schema:
OpticsAgent.instrumentSchema(executableSchema);
- Add the middleware:
expressServer.use(OpticsAgent.middleware());
- Add to your GraphQL context object:
context.opticsContext = OpticsAgent.context(req);
- Instrument your schema:
Apollo Optics Agent supports:
- Node 4, 5, 6 and 7
- graphql-js: 0.6.2 to 0.10.0.
First, install the package
npm install optics-agent --save
Next, set up the agent in your main server file.
var OpticsAgent = require('optics-agent');
or in ES2015+
import OpticsAgent from 'optics-agent';
OpticsAgent.configureAgent({ configOptions })
Normally you do not need to call this function -- just set the OPTICS_API_KEY
environment variable. Call this function if you set the API key in code instead of through the environment variable, or if you need to set specific non-default values for other options. Call this before any calls to instrumentation functions below.
Options include:
-
apiKey
: String. Your API key for the Optics service. This defaults to theOPTICS_API_KEY
environment variable, but can be overridden here. -
reportTraces
: Boolean. Send detailed traces along with usage reports. Defaults to true. -
reportVariables
: Boolean. Send the query variables along with traces. Defaults to true. -
printReports
: Boolean. Print a JSON version of reports as they are sent. This may be useful for debugging. Defaults to false. -
normalizeQuery
: Function(GraphQLResolveInfo)โString. Called to determine the query shape for for a GraphQL query. You shouldn't need to set this unless you are debugging. -
endpointUrl
: String. Where to send the reports. Defaults to the production Optics endpoint, or theOPTICS_ENDPOINT_URL
environment variable if it is set. You shouldn't need to set this unless you are debugging. -
proxyUrl
: String. HTTP proxy to use when sending reports. Default to no proxying, or theHTTPS_PROXY
environment variable if it is set. You should only set this when your servers cannot connect directly to the Optics service. -
reportIntervalMs
: Number. How often to send reports in milliseconds. Defaults to 1 minute. Minimum 10 seconds. You shouldn't need to set this unless you are debugging. -
shutdownGracefully
: Boolean. Send statistics when the process exits. Defaults to true.
Call instrumentSchema
on the same executable schema object you pass to the graphql
function from graphql-js
:
OpticsAgent.instrumentSchema(executableSchema);
You should only call this once per agent. If you have multiple or dynamic schemas, create a separate agent per schema (see below).
Set up middleware:
Tell your server to run the Optics Agent middleware:
expressServer.use(OpticsAgent.middleware());
This must run before the handler that actually executes your GraphQL queries. For the most accurate timings, avoid inserting unnecessary middleware between the Optics Agent middleware and your GraphQL middleware.
OpticsAgent.instrumentHapiServer(hapiServer);
Koa is not officially supported, but thanks to community contributions should work with:
const schema = OpticsAgent.instrumentSchema(executableSchema);
app.use(OpticsAgent.koaMiddleware());
router.post(
'/graphql',
graphqlKoa(async ctx => {
// create an optic context
const opticsContext = OpticsAgent.context(ctx.request);
// create a context for each request
const context = { opticsContext };
return {
schema,
context,
};
})
);
Inside your request handler, if you are calling graphql
directly, add a new
field to the context
object sent to graphql
:
{ opticsContext: OpticsAgent.context(req) }
If you are using apolloExpress
, this will be a field on
the
context
object on the ApolloOptions
value that you return.
If you are using HAPI you must explicitly use the raw request object:
{ opticsContext: OpticsAgent.context(request.raw.req) }
Here's an example diff:
https://github.com/apollostack/GitHunt-API/compare/nim/optics-agent
diff --git a/api/index.js b/api/index.js
index 43ee586..f1a27a6 100644
--- a/api/index.js
+++ b/api/index.js
@@ -19,6 +19,11 @@ import { subscriptionManager } from './subscriptions';
import schema from './schema';
+import OpticsAgent from 'optics-agent';
+
+OpticsAgent.instrumentSchema(schema);
+
+
let PORT = 3010;
if (process.env.PORT) {
PORT = parseInt(process.env.PORT, 10) + 100;
@@ -33,6 +38,7 @@ app.use(bodyParser.json());
setUpGitHubLogin(app);
+app.use('/graphql', OpticsAgent.middleware());
app.use('/graphql', apolloExpress((req) => {
// Get the query, the same way express-graphql does it
// https://github.com/graphql/express-graphql/blob/3fa6e68582d6d933d37fa9e841da5d2aa39261cd/src/index.js#L257
@@ -70,6 +76,7 @@ app.use('/graphql', apolloExpress((req) => {
Users: new Users({ connector: gitHubConnector }),
Entries: new Entries(),
Comments: new Comments(),
+ opticsContext: OpticsAgent.context(req),
},
};
}));
diff --git a/package.json b/package.json
index 98df047..b110fac 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"graphql-tools": "0.7.2",
"knex": "0.12.3",
"lodash": "4.16.4",
+ "optics-agent": "0.0.33",
"passport": "0.3.2",
"passport-github": "1.1.0",
"request-promise": "4.1.1",
If you need to have more than one Agent per process, you can manually construct an Agent object instead of using the default global Agent. Call new OpticsAgent.Agent(options)
to instantiate the object, and then call methods directly on the object instead of on OpticsAgent
. Here is an example:
var OpticsAgent = require('optics-agent');
var agent = new OpticsAgent.Agent({ apiKey: '1234' });
agent.instrumentSchema(schema);
The Optics agent is designed to allow your application to continue working, even if the agent is not configured properly.
If there is no data being sent to Optics, check your application logs to look for the following messages:
Solution: Get a valid API key from Optics and configure it in your GraphQL server.
Solution: Check the API key provided for this endpoint in Optics, and set it in your GraphQL server configuration
Solution: In your server code, call instrumentSchema on the schema object you pass to the graphql function from graphql-js.
OpticsAgent.instrumentSchema(executableSchema);
Solution: In your server code, set up the Optics middleware.
app.use(OpticsAgent.middleware())