Skip to content

Add graphQL landingPage configuration and updates Playground → Sandbox #2343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 15, 2025
155 changes: 137 additions & 18 deletions docusaurus/docs/dev-docs/plugins/graphql.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,35 +53,51 @@ npm install @strapi/plugin-graphql

</Tabs>

Then, start your app and open your browser at [http://localhost:1337/graphql](http://localhost:1337/graphql). You should now be able to access the **GraphQL Playground** that will help you to write your GraphQL queries and mutations.
Then, start your app and open your browser at [http://localhost:1337/graphql](http://localhost:1337/graphql). You should now be able to access the **GraphQL Sandbox** that will help you to write your GraphQL queries and mutations.

:::note
The GraphQL Playground is enabled by default for both the development and staging environments, but disabled in production environments. Set the `playgroundAlways` configuration option to `true` to also enable the GraphQL Playground in production environments (see [plugins configuration documentation](/dev-docs/configurations/plugins#graphql)).
The GraphQL Sandbox is enabled by default in all environments except production. Set the `landingPage` configuration option to `true` to also enable the GraphQL Playground in production environments (see [plugins configuration documentation](/dev-docs/configurations/plugins#graphql)).
:::

## Configuration

Plugins configuration are defined in the `config/plugins.js` file. This configuration file can include a `graphql.config` object to define specific configurations for the GraphQL plugin (see [plugins configuration documentation](/dev-docs/configurations/plugins#graphql)).

[Apollo Server](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#apolloserver) options can be set with the `graphql.config.apolloServer` [configuration object](/dev-docs/configurations/plugins#graphql). Apollo Server options can be used for instance to enable the [tracing feature](https://www.apollographql.com/docs/federation/metrics/), which is supported by the GraphQL playground to track the response time of each part of your query. From `Apollo Server` version 3.9 default cache option is `cache: 'bounded'`. You can change it in the `apolloServer` configuration. For more information visit [Apollo Server Docs](https://www.apollographql.com/docs/apollo-server/performance/cache-backends/).
[Apollo Server](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#apolloserver) options can be passed directly to Apollo with the `graphql.config.apolloServer` [configuration object](/dev-docs/configurations/plugins#graphql). Apollo Server options can be used for instance to enable the [tracing feature](https://www.apollographql.com/docs/federation/metrics/), which is supported by the GraphQL Sandbox to track the response time of each part of your query. The `Apollo Server` default cache option is `cache: 'bounded'`. You can change it in the `apolloServer` configuration. For more information visit [Apollo Server Docs](https://www.apollographql.com/docs/apollo-server/performance/cache-backends/).

:::caution
The maximum number of items returned by the response is limited to 100 by default. This value can be changed using the `amountLimit` configuration option, but should only be changed after careful consideration: a large query can cause a DDoS (Distributed Denial of Service) and may cause abnormal load on your Strapi server, as well as your database server.
:::

## GraphQL Configuration Options

The following configuration options are supported by the GraphQL plugin and can be defined in the `config/plugins` file:

| Option | Type | Description | Default Value | Notes |
| ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------------------------------------------- |
| `endpoint` | String | Sets the GraphQL endpoint path. | `'/graphql'` | Example: `/custom-graphql` |
| `shadowCRUD` | Boolean | Enables or disables automatic schema generation for content types. | `true` | |
| `depthLimit` | Number | Limits the depth of GraphQL queries to prevent excessive nesting. | `10` | Use this to mitigate potential DoS attacks. |
| `amountLimit` | Number | Limits the maximum number of items returned in a single response. | `100` | Use cautiously to avoid performance issues. |
| `playgroundAlways` | Boolean | [Deprecated] Enables GraphQL Playground in all environments (deprecated). | `false` | Prefer using `landingPage` instead. |
| `landingPage` | Boolean \| Function | Enables or disables the landing page for GraphQL. Accepts a boolean or a function returning a boolean or an ApolloServerPlugin implementing `renderLandingPage`. | | `false` in production, `true` in other environments |
| `apolloServer` | Object | Passes configuration options directly to Apollo Server. | `{}` | Example: `{ tracing: true }` |

### Example

The following is an example of how to use these options in a Strapi configuration file:

<Tabs groupId="js-ts">

<TabItem value="javascript" label="JavaScript">

```js title="./config/plugins.js"

```javascript title="./config/plugins.js"
module.exports = {
//
graphql: {
config: {
endpoint: '/graphql',
shadowCRUD: true,
playgroundAlways: false,
landingPage: false, // disable Sandbox everywhere
depthLimit: 7,
amountLimit: 100,
apolloServer: {
Expand All @@ -97,18 +113,63 @@ module.exports = {
<TabItem value="typescript" label="TypeScript">

```ts title="./config/plugins.ts"

export default {
//
graphql: {
config: {
endpoint: '/graphql',
shadowCRUD: true,
playgroundAlways: false,
landingPage: false, // disable Sandbox everywhere
depthLimit: 7,
amountLimit: 100,
apolloServer: {
tracing: false,
},
},
};
```

</TabItem>

</Tabs>

Here is an example of using a function to dynamically enable it:

<Tabs groupId="js-ts">

<TabItem value="javascript" label="JavaScript">

```javascript title="./config/plugins.js"
module.exports = ({ env }) => {
graphql: {
config: {
endpoint: '/graphql',
shadowCRUD: true,
landingPage: (strapi) => {
if (env("NODE_ENV") !== "production") {
return true;
} else {
return false;
}
},
},
},
};
```

</TabItem>

<TabItem value="typescript" label="TypeScript">

```ts title="./config/plugins.ts"
export default ({ env }) => {
graphql: {
config: {
endpoint: '/graphql',
shadowCRUD: true,
landingPage: (strapi) => {
if (env("NODE_ENV") !== "production") {
return true;
} else {
return false;
}
},
},
},
Expand All @@ -119,6 +180,66 @@ export default {

</Tabs>

### CORS exceptions for Landing Page

If the landing page is enabled in production environments (which is not recommended), CORS headers for the Apollo Server landing page must be added manually.

To add them globally, you can merge the following into your middleware configuration:

```
{
name: "strapi::security",
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
"connect-src": ["'self'", "https:", "apollo-server-landing-page.cdn.apollographql.com"],
"img-src": ["'self'", "data:", "blob:", "apollo-server-landing-page.cdn.apollographql.com"],
"script-src": ["'self'", "'unsafe-inline'", "apollo-server-landing-page.cdn.apollographql.com"],
"style-src": ["'self'", "'unsafe-inline'", "apollo-server-landing-page.cdn.apollographql.com"],
"frame-src": ["sandbox.embed.apollographql.com"]
}
}
}
}
```

To add these exceptions only for the `/graphql` path (recommended), you can create a new middleware to handle it. For example:

<Tabs groupId="js-ts">

<TabItem value="javascript" label="JavaScript">

```javascript title="./middlewares/graphql-security.js"
module.exports = (config, { strapi }) => {
return async (ctx, next) => {
if (ctx.request.path === '/graphql') {
ctx.set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net apollo-server-landing-page.cdn.apollographql.com; connect-src 'self' https:; img-src 'self' data: blob: apollo-server-landing-page.cdn.apollographql.com; media-src 'self' data: blob: apollo-server-landing-page.cdn.apollographql.com; frame-src sandbox.embed.apollographql.com; manifest-src apollo-server-landing-page.cdn.apollographql.com;");
}
await next();
};
};
```

</TabItem>

<TabItem value="typescript" label="TypeScript">

```ts title="./middlewares/graphql-security.ts"
export default (config, { strapi }) => {
return async (ctx, next) => {
if (ctx.request.path === '/graphql') {
ctx.set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net apollo-server-landing-page.cdn.apollographql.com; connect-src 'self' https:; img-src 'self' data: blob: apollo-server-landing-page.cdn.apollographql.com; media-src 'self' data: blob: apollo-server-landing-page.cdn.apollographql.com; frame-src sandbox.embed.apollographql.com; manifest-src apollo-server-landing-page.cdn.apollographql.com;");
}
await next();
};
};
```

</TabItem>

</Tabs>

## Shadow CRUD

To simplify and automate the build of the GraphQL schema, we introduced the Shadow CRUD feature. It automatically generates the type definitions, queries, mutations and resolvers based on your models.
Expand Down Expand Up @@ -940,15 +1061,14 @@ mutation {

</Request>

Then on each request, send along an `Authorization` header in the form of `{ "Authorization": "Bearer YOUR_JWT_GOES_HERE" }`. This can be set in the HTTP Headers section of your GraphQL Playground.

Then on each request, send along an `Authorization` header in the form of `{ "Authorization": "Bearer YOUR_JWT_GOES_HERE" }`. This can be set in the HTTP Headers section of your GraphQL Sandbox.

## API tokens

To use API tokens for authentication, pass the token in the `Authorization` header using the format `Bearer your-api-token`.

:::note
Using API tokens in the the GraphQL playground requires adding the authorization header with your token in the `HTTP HEADERS` tab:
Using API tokens in the the GraphQL Sandbox requires adding the authorization header with your token in the `HTTP HEADERS` tab:

```http
{
Expand All @@ -963,10 +1083,9 @@ Replace `<TOKEN>` with your API token generated in the Strapi Admin panel.

GraphQL is a query language allowing users to use a broader panel of inputs than traditional REST APIs. GraphQL APIs are inherently prone to security risks, such as credential leakage and denial of service attacks, that can be reduced by taking appropriate precautions.

### Disable introspection and Sandbox in production

### Disable introspection and playground in production

In production environments, disabling the GraphQL Playground and the introspection query is recommended.
In production environments, disabling the GraphQL Sandbox and the introspection query is strongly recommended.
If you haven't edited the [configuration file](/dev-docs/configurations/plugins#graphql), it is already disabled in production by default.

### Limit max depth and complexity
Expand Down