Skip to content

Commit 1e98e41

Browse files
committed
Generalize Playground support to a simple "HTML page" plugin API
Apollo Server has graphql-playground built in, to the degree that the playground-html renderPlaygroundPage options are a top-level key in Apollo Server config. This has been very useful, but the theme of Apollo Server 3 is to keep its API lean and disentangled from other packages. Every single Apollo Server integration package has the same copy-and-paste code that threads playground options directly into the playground package. Additionally, graphql-playground has is retiring (see graphql/graphql-playground#1143) and merging back into GraphiQL. So it especially doesn't make sense for Apollo Server's API to continue to be defined by Playground. We could just switch back to GraphiQL like in Apollo Server 1, but instead, let's move UI configuration out of the top-level ApolloServer API and move it into our plugin system. And let's make sure that the per-web-framework integration packages don't need to care what UI system you're using. This PR adds a very very very simple static HTML serving API to our plugin system. The point of this system is to support plugins that serve a bit of HTML to load a full UI from CDN, like for Playground, GraphiQL, Explorer, etc, or to serve a splash page linking to other UIs. The point of this system is not to be a convenient way to add app-specific HTML to your app. If you want to do that, just use middleware in your web framework! The point is to make UI plugins that work out of the box with every Apollo Server integration. Because of that, the plugins can do very little: they can serve a single static HTML page. That's it! And you can only install one in your app. We add ApolloServerPluginUIGraphQLPlayground in core, as well as ApolloServerPluginUIDisabled. Like other plugins, if you don't set some UI plugin of your own and don't set the disabled plugin, Apollo Server will auto-install a plugin. In this case it installs the playground plugin though we may change that before 3.0.0. (The mechanism for implicitly installing ApolloServerPluginUIGraphQLPlayground is a bit more complex than for our other built-in plugins because we want user plugins to be able to override the UI, not just other built-in plugins. So ApolloServerPluginUIDisabled prevents ApolloServerPluginUIGraphQLPlayground from being installed at all, but other plugins defining renderUIPage get installed alongside ApolloServerPluginUIGraphQLPlayground and there's logic to make sure they take precedence.) We remove the `playground` constructor option (you can pass configuration options to ApolloServerPluginUIGraphQLPlayground instead), as well as three playground-related symbols exported from all the main packages. While we're at it, we simplify how we set up the Playground HTML: - There was some confusing logic in playground.ts where `defaultPlaygroundOptions.settings` listed some setting overrides... which were not actually used by default... only if you specified something like `playground: {settings: {}}`! We remove these and let the default settings in `@apollographql/graphql-playground-react` be the defaults *always* instead of *most of the time*. - Instead of trying to figure out the path to the GraphQL endpoint via a variety of different mechanisms (including "in the docs, tell you to edit the source to specify `endpoint` yourself), just leave `endpoint` empty by default. This makes the React app choose `location.href` for the endpoint, which should be fine! (Upgrade `@apollographql/graphql-playground-html` to not log a warning when `endpoint` isn't provided, and upgrade `@apollographql/graphql-playground-react` to preserve `code` query parameters on the endpoint so that the Azure Functions docs work automatically.) Also: - Tests that set environment variables (as some of the playground ones do) make me uncomfortable. Added `__testing__nodeEnv` to the ApolloServer and GraphQLOptions interfaces that can be used to test how `process.env.NODE_ENV` affects AS without actually changing the environment. - Inline fastifyApollo into fastify's ApolloServer. - I ended up rewriting a bunch of tests that used `request` to use `supertest` instead (though for some reason we access it via the symbol `request`). Many of these tests had been copied and pasted between packages but I did not consolidate them. Fixes #5159.
1 parent cf4a95a commit 1e98e41

File tree

61 files changed

+3762
-9654
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3762
-9654
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The version headers in this history reflect the versions of Apollo Server itself
3434

3535
- We no longer re-export the entirety of `graphql-tools` (including `makeExecutableSchema`) from all Apollo Server packages. If you'd like to continue using them, install [`graphql-tools`](https://www.graphql-tools.com/) or one of its sub-packages yourself.
3636
- The `Upload` scalar is no longer exported. (See above as to why.)
37+
- Support for serving an HTML UI has been generalized. While servers in dev mode still default to serving GraphQL Playground (note: this may change before v3.0.0), plugins can define a new `renderUIPage` event which returns an HTML page that is served to browsers. The `playground` option to `new ApolloServer` has been removed; customizing Playground or making it run in production mode can be done by installing the new `ApolloServerPluginUIGraphQLPlayground` plugin yourself. To disable Playground, either install the new `ApolloServerPluginUIDisabled` plugin or install any other plugin that implements `renderUIPage`. Packages no longer export `defaultPlaygroundOptions`, `PlaygroundConfig`, and `PlaygroundRenderPageOptions`. By default, no GraphQL Playground settings are overridden, including the endpoint, which now just defaults to `window.location.href` (with most query parameters removed); this means you typically don't have to manually configure the endpoint.
3738

3839
## v2.24.0
3940

Diff for: docs/source/api/apollo-server.md

+1-19
Original file line numberDiff line numberDiff line change
@@ -359,24 +359,6 @@ The default value is `10_000` (10 seconds).
359359
</td>
360360
</tr>
361361

362-
<tr>
363-
<td>
364-
365-
###### `playground`
366-
367-
`Boolean` or `Object`
368-
</td>
369-
<td>
370-
371-
If truthy, the server hosts [GraphQL Playground](../testing/graphql-playground) from its URL. Can be an object to pass [configuration options](https://github.com/prismagraphql/graphql-playground/#usage) to the playground.
372-
373-
The default value is `true`, **unless** the `NODE_ENV` environment variable is set to `production`.
374-
375-
Note that [`introspection`](#introspection) must be enabled for GraphQL Playground to function properly.
376-
</td>
377-
</tr>
378-
379-
380362
<tr>
381363
<td>
382364

@@ -784,7 +766,7 @@ The default value is `true`.
784766

785767
#### `getMiddleware`
786768

787-
Returns an array of the middlewares that together form a complete instance of Apollo Server. Includes middleware for HTTP body parsing, GraphQL Playground, file uploads, and subscriptions.
769+
Returns an array of the middlewares that together form a complete instance of Apollo Server. Includes middleware for HTTP body parsing, health checks, setting CORS headers, and serving a static UI, as well as actually executing GraphQL operations.
788770

789771
Unlike [`applyMiddleware`](#applymiddleware), `getMiddleware` does _not_ automatically apply Apollo Server middlewares to your application. Instead, this method enables you to apply or omit individual middlewares according to your use case. For an Express or Koa application, you can apply a particular middleware by calling `app.use`.
790772

Diff for: docs/source/deployment/azure-functions.md

+6-20
Original file line numberDiff line numberDiff line change
@@ -155,35 +155,35 @@ Before deploying, a new application must be setup. To do this, we need to create
155155
az group create --name apollo-examples --location eastus
156156
```
157157

158-
After creating a resource group, we need to create a storage account to store our code on Azure.
158+
After creating a resource group, we need to create a storage account to store our code on Azure. You will need to choose a unique name.
159159

160160
```shell
161161
az storage account create \
162-
--name apolloexample \
162+
--name apolloexampleYOURNAME \
163163
--location eastus \
164164
--resource-group apollo-examples \
165165
--sku Standard_LRS
166166
```
167167

168168
We will publish our application to Azure now using the CLI as well. We need to create a `functionapp` running the following command.
169169

170-
Note: The your function name must be unique.
170+
Note: The function name must be unique.
171171

172172
```shell
173173
az functionapp create \
174174
--resource-group apollo-examples \
175-
--name apollo-example \
175+
--name apollo-example-YOURNAME \
176176
--consumption-plan-location eastus \
177177
--runtime node \
178-
--storage-account apolloexample
178+
--storage-account apolloexampleYOURNAME
179179
```
180180

181181
### Publishing our project to the function app
182182

183183
After creating a functionapp, it is just to publish our function to azure. The command below could be used to perform releases to all of your functions.
184184

185185
```shell
186-
func azure functionapp publish apollo-example
186+
func azure functionapp publish apollo-example-YOURNAME
187187
```
188188

189189
```shell
@@ -200,14 +200,6 @@ Functions in apollo-example:
200200
201201
Finally, going to the Invoke URL shown at the output above, we will see our result.
202202
203-
Note: When GraphQL Playground starts, It won't have the correct URL containing the security `code`, and a message **"Server cannot be reached"** as shown at your browser.
204-
205-
![Apollo server running on azure with error](../images/deployment/azure-functions/apollo-server-on-azure.png)
206-
207-
We just need to put the full URL that includes the security `code` in the Playground url box. The background polling should refresh the screen momentarily. Click the **Schema** button to see if the docs are loaded correctly as shown in the image below.
208-
209-
![Apollo server running on azure with success](../images/deployment/azure-functions/apollo-server-on-azure-sucess.png)
210-
211203
### Cleaning Up
212204
213205
After completing this tutorial, you can delete all the resources you created during this example from your Azure account by removing the Azure Resource Group called **apollo-examples** with the **az group** commmand. We can manually delete each resource using the following commands:
@@ -233,12 +225,6 @@ It is also possible to publish your project from VS Code using the Azure Functio
233225
234226
Once deployment is complete, view the output in VS Code and you should be able to see the url of your GraphQL endpoint. It will look something like **https://our-graphql-project.azurewebsites.net/api/graphql**. Navigate to the url and you should find GraphQL Playground.
235227
236-
Note: When GraphQL Playground starts, It won't have the correct URL containing the security `code`, and a message **"Server cannot be reached"** as shown at your browser.
237-
238-
![Apollo server running on azure with error](../images/deployment/azure-functions/apollo-server-on-azure.png)
239-
240-
We just need to put the full URL that includes the security `code` in the Playground url box. The background polling should refresh the screen momentarily. Click the **Schema** button to see if the docs are loaded correctly as the image below.
241-
242228
Need more details? See the [Docs](https://www.npmjs.com/package/apollo-server-azure-functions)
243229
at the NPM repository.
244230

Diff for: docs/source/deployment/lambda.md

+1-21
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ For the sake of this example, the following file can just be copied and pasted i
7474
service: apollo-lambda
7575
provider:
7676
name: aws
77-
runtime: nodejs12.x
77+
runtime: nodejs14.x
7878
functions:
7979
graphql:
8080
# this is formatted as <FILENAME>.<HANDLER>
@@ -252,23 +252,3 @@ exports.graphqlHandler = server.createHandler({
252252
},
253253
});
254254
```
255-
256-
## Setting up GraphQL Playground
257-
258-
By default, `serverless` will deploy to AWS with the `stage` set to `development` resulting in an API endpoint at `/dev/graphql`.
259-
260-
To allow GraphQL Playground to correctly use the `dev` endpoint, add a new `endpoint` configuration within the `playground` option to the `ApolloServer` instantiation options:
261-
262-
```js
263-
const server = new ApolloServer({
264-
typeDefs,
265-
resolvers,
266-
// highlight-start
267-
playground: {
268-
endpoint: "/dev/graphql"
269-
}
270-
// highlight-end
271-
});
272-
```
273-
274-
For information on additional configuration options, see [GraphQL Playground](https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/).
Binary file not shown.
Binary file not shown.

Diff for: docs/source/integrations/middleware.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,6 @@ async function startApolloServer() {
6060

6161
The parameter you provide to `applyMiddleware` is your middleware's top-level representation of your application. In Express applications, this variable is commonly named `app`.
6262

63-
When you pass your app to `applyMiddleware`, Apollo Server automatically configures various middleware (including body parsing, the GraphQL Playground frontend, and CORS support), so you don't need to apply them with a mechanism like `app.use`.
63+
When you pass your app to `applyMiddleware`, Apollo Server automatically configures various middleware (including body parsing, an HTML UI, and CORS support), so you don't need to apply them with a mechanism like `app.use`.
6464

6565
> **Note:** When integrating with hapi, call `applyMiddleware` with `await`.

Diff for: docs/source/integrations/plugins.md

+26
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,32 @@ const server = new ApolloServer({
298298
})
299299
```
300300

301+
### `renderUIPage`
302+
303+
The `renderUIPage` event is fired once by Apollo Server after all `serverWillStart` events run.
304+
You define your `renderUIPage` handler in the object returned by your [`serverWillStart`](#serverwillstart) handler, which allows it to read the values passed to `serverWillStart`. **At most one plugin in your server may define a `renderUIPage` handler.** If your server has a `renderUIPage` handler, it should return a `UIPage`, which is just an object with a string `html` field. The value of that field will be served as HTML for any requests with `accept: text/html` headers. (By default, Apollo Server installs `ApolloServerPluginUIGraphQLPlayground` which serves GraphQL Playground as a UI page.)
305+
306+
#### Example
307+
308+
```js
309+
const server = new ApolloServer({
310+
/* ... other necessary configuration ... */
311+
312+
plugins: [
313+
{
314+
serverWillStart() {
315+
return {
316+
renderUIPage() {
317+
return { html: `<html><body>Welcome to your server!</body></html>` };
318+
}
319+
}
320+
}
321+
}
322+
]
323+
})
324+
```
325+
326+
301327
### `requestDidStart`
302328

303329
The `requestDidStart` event fires whenever Apollo Server begins fulfilling a GraphQL request.

Diff for: docs/source/testing/graphql-playground.mdx

+40-24
Original file line numberDiff line numberDiff line change
@@ -20,51 +20,67 @@ To try out dev graphs:
2020

2121
</ExpansionPanel>
2222

23-
[GraphQL Playground](https://github.com/prismagraphql/graphql-playground) is a graphical, interactive, in-browser GraphQL IDE, created by [Prisma](https://www.prisma.io/) and based on [GraphiQL](https://github.com/graphql/graphiql).
23+
[GraphQL Playground](https://github.com/graphql/graphql-playground) is a graphical, interactive, in-browser GraphQL IDE, created by [Prisma](https://www.prisma.io/) and based on [GraphiQL](https://github.com/graphql/graphiql).
2424

25-
In development, Apollo Server enables GraphQL Playground on the same URL as the GraphQL server itself (e.g. `http://localhost:4000/graphql`) and automatically serves the GUI to web browsers. When `NODE_ENV` is set to `production`, GraphQL Playground (as well as introspection) is disabled as a production best-practice.
25+
In development, Apollo Server enables GraphQL Playground on the same URL as the GraphQL server itself (e.g. `http://localhost:4000/graphql`) and automatically serves the GUI to web browsers. When `NODE_ENV` is set to `production`, GraphQL Playground (as well as introspection) is disabled as a production best-practice. It does this by installing the `ApolloServerPluginUIGraphQLPlayground` plugin with default settings.
2626

2727
![GraphQL Playground](../images/graphql-playground.png)
2828

2929
## Configuring Playground
3030

31-
The Apollo Server constructor contains the ability to configure GraphQL Playground with the `playground` configuration option. The options can be found on GraphQL Playground's [documentation](https://github.com/prismagraphql/graphql-playground/#usage).
31+
If you'd like to configure playground, you can add `ApolloServerPluginUIGraphQLPlayground` to your Apollo Server yourself. contains the ability to configure GraphQL Playground with the `playground` configuration option. The options can be found on GraphQL Playground's [documentation](https://github.com/graphql/graphql-playground/#usage).
3232

3333
```js
34+
const { ApolloServer } = require('apollo-server');
35+
const { ApolloServerPluginUIGraphQLPlayground } = require('apollo-server-core');
36+
3437
new ApolloServer({
35-
typeDefs,
36-
resolvers,
37-
playground: {
38-
settings: {
39-
'editor.theme': 'light',
40-
},
41-
tabs: [
42-
{
43-
endpoint,
44-
query: defaultQuery,
45-
},
38+
typeDefs,
39+
resolvers,
40+
plugins: [
41+
ApolloServerPluginUIGraphQLPlayground({
42+
settings: {
43+
'editor.theme': 'light',
44+
},
45+
tabs: [
46+
{
47+
endpoint,
48+
query: defaultQuery,
49+
},
50+
],
51+
}),
4652
],
47-
},
4853
});
4954
```
5055

51-
## Enabling GraphQL Playground in production
56+
## Disabling Playground in development
5257

53-
To enable GraphQL Playground in production, introspection and the playground can be enabled explicitly in the following manner.
58+
If you don't want to run GraphQL Playground at all, you have two choices. First, you can replace it with a different UI by installing a plugin that implements the [`renderUIPage` event](../integrations/plugins/#renderuipage). Alternatively, you can install the `ApolloServerPluginUIDisabled` plugin which stops Apollo Server from serving any HTML UI.
5459

55-
```js{7-8}
60+
```js
5661
const { ApolloServer } = require('apollo-server');
57-
const { typeDefs, resolvers } = require('./schema');
62+
const { ApolloServerPluginUIDisabled } = require('apollo-server-core');
5863

59-
const server = new ApolloServer({
64+
new ApolloServer({
6065
typeDefs,
6166
resolvers,
62-
introspection: true,
63-
playground: true,
67+
plugins: [ApolloServerPluginUIDisabled()],
6468
});
69+
```
6570

71+
## Enabling GraphQL Playground in production
6672

67-
server.listen().then(({ url }) => {
68-
console.log(`🚀 Server ready at ${url}`);
73+
To enable GraphQL Playground in production, just install GraphQL Playground yourself, without any special configuration. You should also enable introspection so that GraphQL Playground can load your graph's schema.
74+
75+
```js
76+
const { ApolloServer } = require('apollo-server');
77+
const { ApolloServerPluginUIGraphQLPlayground } = require('apollo-server-core');
78+
79+
new ApolloServer({
80+
typeDefs,
81+
resolvers,
82+
introspection: true,
83+
plugins: [ApolloServerPluginUIGraphQLPlayground()],
6984
});
85+
7086
```

0 commit comments

Comments
 (0)