An extension of the graphql-request package, providing the configuration and additional logic needed to connect with Optimizely Graph.
The package contains the following services:
- The configuration logic, to read and validate the configuration. Supports both runtime configuration objects as well a reading from environment variables.
- The GraphQL Client, based upon graphql-request, supporting the following authentication schemes for Optimizely Graph:
- Public access (e.g. using the Single key)
- HMAC Signed messages
- Basic Authentication
- Token pass-through (for Optimizely CMS in context editing)
- A RouteResolver, which simplifies handling routes when using Optimizely CMS, using the Optimizely Graph as datasource.
- A ChannelRepository, which simplifies reading channel (website) configuration from the Optimizely CMS, using the Optimizely Graph as datasource.
- An OpenAPI Client for the administrative REST services of Optimizely Graph. This follows the service specification and is based upon the code generated by openapi-typescript-codegen from this service specification.
// Import the library
import { gql } from 'graphql-request'
import createClient, { AuthMode } from '@remkoj/optimizely-graph-client/client'
import Router from '@remkoj/optimizely-graph-client/router'
import ChannelRepository from '@remkoj/optimizely-graph-client/channels'
// Prepare the query
const document = gql`query {
Content {
items {
Name
ContentLink {
GuidValue
}
Language {
Name
}
}
}
}`
// Create an instance of the client, the configuration object may be omitted
// when executing on Node.JS. If no configuration is provided, it will be read
// from the environment variables.
// The variable client will be of type: GraphQLClient
const client = createClient({
single_key: "your_single_key"
})
// By default the client will always use AuthMode.Public, unless overridden by the
// second parameter of createClient. Use the updateAuthentication to change the
// authentication mode after creation
client.updateAuthentication(AuthMode.Public)
// Execute a GraphQL query, the second paramer can be used to send in variables
const result = await client.request(document)
// Use the Router to get all routes registered by Optimizely CMS
const router = new Router(client)
const allPaths = await router.getRoutes()
// Use the ChannelRepository to get all registered channels within Optimizely CMS
const channels = new ChannelRepository(client)
const allChannels = await channels.getAll()
Optimizely Graph allows restricted content to be queried, using either of the following patterns:
- Graph Keys: Admin authentication using Graph Sercet Key and App Key
- Token passthrough: Used by CMS preview/edit mode to provide a short lived access token to query the content to be previewed/edited. Read more
- OpenID Connect: Used by the frontend to pass through OpenID Connect based JWT to authorize content. Read more
- Generic Login: Used by the fronend to pass through the username/roles from a trusted identity provider. Read more
The Graph Keys authentication mechanism can be enabled by providing the App Secret and App Key as part of the configuration. Then depending on your security policy, use either:
- Basic Authentication:
client.updateAuthentication(AuthMode.Basic)
- HMAC Authentication:
client.updateAuthentication(AuthMode.HMAC)
HMAC provides slightly better security as it both ensures message integrity and never transmits the App Secret in a decryptable form.
This method of authentication is specific to Optimizely CMS, where the edit UI will provide a short-lived token that allows access to the content that is being edited. This only requires the Single Key to be known.
const tokenValue: string = ''; //Your token value here
client.updateAuthentication(tokenValue)
This method requires two steps, first, the client must be configured with the Turnstile Tenant ID, secondly the JWT Token of the OIDC provider must be added to the client.
const client = createClient({
single_key: "your_single_key",
tenant_id: "your_turnstile_tenant_id"
})
const tokenValue: string = ''; //Your OIDC JWT Token
client.updateAuthentication(tokenValue)
This method works with non-OIDC compliant identity providers, but requires a trusted connection with Optimizely Graph. As such, it requires the Graph Keys to ensure that the client is authorized to pass in the username and roles directly.
The roles must be provided as a comma separated list, without spaces around the values. Both the username and roles must exactly match the roles and usernames defined in Optimizely CMS.
const client = createClient({
single_key: "your_single_key",
app_key: "your_app_key",
secret: "your_secret"
})
client.setFrontendUser({
username: "your_username",
roles: "list_of_roles"
})
Warning: The AdminAPI of Optimizely Graph requires your App Key & Secret as it allows full management of the service. When used in a browser, take all needed steps to prevent leaking of these credentials.
import createAdminApi from '@remkoj/optimizely-graph-client/admin'
// Create an instance of the client, the configuration object may be omitted
// when executing on Node.JS. If no configuration is provided, it will be read
// from the environment variables.
const api = createAdminApi({
single_key: "your single key",
app_key: "your app key",
secret: "your secret"
})
// Use the webhooks endpoint to list all registered webhooks
const hooks = await api.webhooks.listWebhookHandler()
The following configuration options are available, the table lists both the environment variable as well as property within the configuration object.
Environment | Configuration object | Required | Usage |
---|---|---|---|
OPTIMIZELY_GRAPH_GATEWAY |
gateway | no | The endpoint for Optimizely Graph |
OPTIMIZELY_GRAPH_SINGLE_KEY |
single_key | yes | The key needed for public, read-only access |
OPTIMIZELY_GRAPH_SECRET |
secret | no | The Optimizely Graph secret for write access, this value must never be made available in a browser |
OPTIMIZELY_GRAPH_APP_KEY |
app_key | no | The Optimizely Graph app_key for write access, this value must never be made available in a browser |
OPTIMIZELY_GRAPH_TENANT_ID |
tenant_id | no | The Optimizely Graph Tenant ID. Only required when using OIDC Authentication |
SITE_DOMAIN |
deploy_domain | no | The domain of the frontend |
OPTIMIZELY_CMS_URL |
dxp_url | yes | The domain where the CMS has been installed |
OPTIMIZELY_CMS_SCHEMA |
opti_cms_schema | no | The marker for the CMS schema version, which can be used by services and the implementation. Valid values are: OPTI-CMS-12 and OPTI-CMS-13 . The default value is OPTI-CMS-13
|
OPTIMIZELY_GRAPH_QUERY_LOG |
query_log | no | Set to "true" to enable query output to the console |
OPTIMIZELY_DEBUG |
debug | no | Set to "true" to enable verbose debug output to the console |
The Optimizely Graph Client has support for a number of the feature flags in Optimizely Graph and can enable/disable those on the fly.
The following features can currently be toggled:
- Caching [Default: enabled]: Provides control over the service output cache, setting this to false will make all requests be evaluated in real time instead of being cached.
- Stored queries [Default: enabled]: This will store the query to the underlying data store, so even when the output cache is invalidated, this will prevent the query to the data store to be regenerated. Warning: This feature does not work with cursors, so either disable it temporary or completely if you need cursors in you queries.
-
Recursive queries [Default: disabled]: When enabled it enables the execution of the
@recursive(depth: n)
directive. If compatible with the queries used, this can significantly reduce the number of queries needed to build a page.
The flags are controlled by the following instance methods & fields on the Client object:
-
updateFlags(newFlags, temporary)
: Update the flags for the instance. If there's roll-back information it will throw an Error. When temporary is enabled it will create a roll-back copy of the current flags prior to updating. -
restoreFlags()
: Restore the flags to the stored 'roll-back' copy, if there's no roll-back copy, no action is taken and no Error will be thrown.
For server-side code, with access to environment variables, adjust according to your case.
// Import client & create instance
import createClient from '@remkoj/optimizely-graph-client/client';
const client = createClient();
// Example: Set the default for this client to add support for recursive queries
client.updateFlags({ recursive: true });
// Example: Temporary disable the query cache to allow queries with cursor
client.updateFlags({ queryCache: false }, true);
client.request(/* arguments */) // Or use - for example - an SDK / Function generated by GraphQL-Codegen
client.restoreFlags();