-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
createExpressServer.ts
119 lines (104 loc) · 3.69 KB
/
createExpressServer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { createServer, type Server } from 'http'
import cors from 'cors'
import { json } from 'body-parser'
import { expressMiddleware } from '@apollo/server/express4'
import express from 'express'
import {
type GraphQLFormattedError,
} from 'graphql'
import { ApolloServer, type ApolloServerOptions } from '@apollo/server'
import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled'
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default'
// @ts-expect-error
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js'
import {
type KeystoneContext,
type __ResolvedKeystoneConfig,
} from '../types'
/*
NOTE: This creates the main Keystone express server, including the
GraphQL API, but does NOT add the Admin UI middleware.
The Admin UI takes a while to build for dev, and is created separately
so the CLI can bring up the dev server early to handle GraphQL requests.
*/
function formatError (graphqlConfig: __ResolvedKeystoneConfig['graphql']) {
return (formattedError: GraphQLFormattedError, error: unknown) => {
let debug = graphqlConfig.debug
if (debug === undefined) {
debug = process.env.NODE_ENV !== 'production'
}
if (!debug && formattedError.extensions) {
// Strip out any `debug` extensions
delete formattedError.extensions.debug
delete formattedError.extensions.exception
}
if (graphqlConfig.apolloConfig?.formatError) {
return graphqlConfig.apolloConfig.formatError(formattedError, error)
}
return formattedError
}
}
export async function createExpressServer (
config: Pick<__ResolvedKeystoneConfig, 'graphql' | 'server' | 'storage'>,
context: KeystoneContext
): Promise<{
expressServer: express.Express
apolloServer: ApolloServer<KeystoneContext>
httpServer: Server
}> {
const expressServer = express()
const httpServer = createServer(expressServer)
if (config.server.cors !== null) {
expressServer.use(cors(config.server.cors))
}
await config.server.extendExpressApp(expressServer, context)
await config.server.extendHttpServer(httpServer, context)
if (config.storage) {
for (const val of Object.values(config.storage)) {
if (val.kind !== 'local' || !val.serverRoute) continue
expressServer.use(
val.serverRoute.path,
express.static(val.storagePath, {
setHeaders (res) {
if (val.type === 'file') {
res.setHeader('Content-Type', 'application/octet-stream')
}
},
index: false,
redirect: false,
lastModified: false,
})
)
}
}
const apolloConfig = config.graphql.apolloConfig
const serverConfig = {
formatError: formatError(config.graphql),
includeStacktraceInErrorResponses: config.graphql.debug,
...apolloConfig,
schema: context.graphql.schema,
plugins:
config.graphql.playground === 'apollo'
? apolloConfig?.plugins
: [
config.graphql.playground
? ApolloServerPluginLandingPageLocalDefault()
: ApolloServerPluginLandingPageDisabled(),
...(apolloConfig?.plugins ?? []),
],
} as ApolloServerOptions<KeystoneContext> // TODO: satisfies
const apolloServer = new ApolloServer({ ...serverConfig })
const maxFileSize = config.server.maxFileSize
expressServer.use(graphqlUploadExpress({ maxFileSize }))
await apolloServer.start()
expressServer.use(
config.graphql.path,
json(config.graphql.bodyParser),
expressMiddleware(apolloServer, {
context: async ({ req, res }) => {
return await context.withRequest(req, res)
},
})
)
return { expressServer, apolloServer, httpServer }
}