Skip to content

Commit

Permalink
Add extendExpressApp config option (#6467)
Browse files Browse the repository at this point in the history
  • Loading branch information
JedWatson authored Sep 6, 2021
1 parent 3957c09 commit e0f935e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changeset/spicy-crews-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@keystone-next/website": patch
"@keystone-next/keystone": minor
---

Add extendExpressApp config option for configuring the express app that Keystone creates
43 changes: 39 additions & 4 deletions docs/pages/docs/apis/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ Options:
- `port` (default: `3000` ): The port your Express server will listen on.
- `maxFileSize` (default: `200 * 1024 * 1024`): The maximum file size allowed for uploads. If left undefined, defaults to `200 MiB`
- `healthCheck` (default: `undefined`): Allows you to configure a health check endpoint on your server.
- `extendExpressApp` (default: `undefined`): Allows you to extend the express app that Keystone creates.

```typescript
export default config({
Expand All @@ -211,6 +212,7 @@ export default config({
port: 3000,
maxFileSize: 200 * 1024 * 1024,
healthCheck: true,
extendExpressApp: (app) => { /* ... */ },
},
/* ... */
});
Expand All @@ -228,8 +230,8 @@ config({
healthCheck: {
path: '/my-health-check',
data: { status: 'healthy' },
}
}
},
},
})
```

Expand All @@ -245,11 +247,44 @@ config({
timestamp: Date.now(),
uptime: process.uptime(),
}),
}
}
},
},
})
```

### extendExpressApp

This lets you modify the express app that Keystone creates _before_ the Apollo Server and Admin UI Middleware are added to it (but after the `cors` and `healthcheck` options are applied).

For example, you could add your own request logging middleware:

```ts
export default config({
server: {
extendExpressApp: (app) => {
app.use((req, res, next) => {
console.log('A request!');
next();
});
},
},
});
```

Or add a custom route handler:

```ts
export default config({
server: {
extendExpressApp: (app) => {
app.get('/_version', (req, res) => {
res.send('v6.0.0-rc.2');
});
},
},
});
```

## session

```
Expand Down
4 changes: 4 additions & 0 deletions packages/keystone/src/lib/server/createExpressServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export const createExpressServer = async (

addHealthCheck({ config, server });

if (config.server?.extendExpressApp) {
config.server?.extendExpressApp(server);
}

addApolloServer({
server,
config,
Expand Down
5 changes: 4 additions & 1 deletion packages/keystone/src/types/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Config } from 'apollo-server-express';
import { CorsOptions } from 'cors';
import express from 'express';
import type { GraphQLSchema } from 'graphql';
import type { Config } from 'apollo-server-express';

import type { AssetMode, KeystoneContext } from '..';

Expand Down Expand Up @@ -131,6 +132,8 @@ export type ServerConfig = {
maxFileSize?: number;
/** Health check configuration. Set to `true` to add a basic `/_healthcheck` route, or specify the path and data explicitly */
healthCheck?: HealthCheckConfig | true;
/** Hook to extend the Express App that Keystone creates */
extendExpressApp?: (app: express.Express) => void;
};

// config.graphql
Expand Down
32 changes: 32 additions & 0 deletions tests/api-tests/extend-express-app.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createSchema, list } from '@keystone-next/keystone';
import { text } from '@keystone-next/keystone/fields';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import supertest from 'supertest';
import { apiTestConfig } from './utils';

const runner = setupTestRunner({
config: apiTestConfig({
lists: createSchema({ User: list({ fields: { name: text() } }) }),
server: {
extendExpressApp: app => {
app.get('/magic', (req, res) => {
res.json({ magic: true });
});
},
},
}),
});

test(
'basic extension',
runner(async ({ app }) => {
const { text } = await supertest(app)
.get('/magic')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
expect(JSON.parse(text)).toEqual({
magic: true,
});
})
);

0 comments on commit e0f935e

Please sign in to comment.