-
-
Notifications
You must be signed in to change notification settings - Fork 363
/
Copy pathserver.js
158 lines (123 loc) · 4.69 KB
/
server.js
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict'
const micro = require('micro')
const { resolve } = require('path')
const { readFile } = require('fs').promises
const { send, createError } = require('micro')
const { router, get, post, put, patch, del } = require('microrouter')
const { ApolloServer } = require('apollo-server-micro')
const KnownError = require('./utils/KnownError')
const signale = require('./utils/signale')
const config = require('./utils/config')
const findMatchingOrigin = require('./utils/findMatchingOrigin')
const customTracker = require('./utils/customTracker')
const createApolloServer = require('./utils/createApolloServer')
const { createMicroContext } = require('./utils/createContext')
const index = readFile(resolve(__dirname, '../dist/index.html')).catch(signale.fatal)
const favicon = readFile(resolve(__dirname, '../dist/favicon.ico')).catch(signale.fatal)
const styles = readFile(resolve(__dirname, '../dist/index.css')).catch(signale.fatal)
const scripts = readFile(resolve(__dirname, '../dist/index.js')).catch(signale.fatal)
const tracker = readFile(resolve(__dirname, '../dist/tracker.js')).catch(signale.fatal)
const handleMicroError = (err, res) => {
// This part is for micro errors and errors outside of GraphQL.
// Most errors won't be caught here, but some error can still
// happen outside of GraphQL. In this case we distinguish
// between unknown errors and known errors. Known errors are
// created with the createError function while unknown errors
// are simply errors thrown somewhere in the application.
const isUnknownError = err.statusCode == null
const hasOriginalError = err.originalError != null
// Only log the full error stack when the error isn't a known response
if (isUnknownError === true) {
signale.fatal(err)
return send(res, 500, err.message)
}
signale.warn(hasOriginalError === true ? err.originalError.message : err.message)
send(res, err.statusCode, err.message)
}
const handleGraphError = (err) => {
// This part is for error that happen inside GraphQL resolvers.
// All known errors should be thrown as a KnownError as those
// errors will only show up in the response and as a warning
// in the console output.
const suitableError = err.originalError || err
const isKnownError = suitableError instanceof KnownError
// Only log the full error stack when the error isn't a known response
if (isKnownError === false) {
signale.fatal(suitableError)
return err
}
signale.warn(suitableError.message)
return err
}
const catchError = (fn) => async (req, res) => {
try {
return await fn(req, res)
} catch (err) {
handleMicroError(err, res)
}
}
const attachCorsHeaders = (fn) => async (req, res) => {
const matchingOrigin = findMatchingOrigin(req, config.allowOrigin)
if (matchingOrigin != null) {
res.setHeader('Access-Control-Allow-Origin', matchingOrigin)
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Time-Zone')
res.setHeader('Access-Control-Allow-Credentials', 'true')
}
return fn(req, res)
}
const notFound = async (req) => {
const err = new Error(`\`${ req.url }\` not found`)
throw createError(404, 'Not found', err)
}
const apolloServer = createApolloServer(ApolloServer, {
formatError: handleGraphError,
context: createMicroContext
})
const graphqlPath = '/api'
const graphqlHandler = apolloServer.createHandler({ path: graphqlPath })
const routes = [
get('/', async (req, res) => {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(await index)
}),
get('/index.html', async (req, res) => {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(await index)
}),
get('/favicon.ico', async (req, res) => {
res.setHeader('Content-Type', 'image/vnd.microsoft.icon')
res.end(await favicon)
}),
get('/index.css', async (req, res) => {
res.setHeader('Content-Type', 'text/css; charset=utf-8')
res.end(await styles)
}),
get('/index.js', async (req, res) => {
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
res.end(await scripts)
}),
get('/tracker.js', async (req, res) => {
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
res.end(await tracker)
}),
customTracker.exists === true ? get(customTracker.url, async (req, res) => {
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
res.end(await tracker)
}) : undefined,
post(graphqlPath, graphqlHandler),
get(graphqlPath, graphqlHandler),
get('/.well-known/apollo/server-health', graphqlHandler),
get('/*', notFound),
post('/*', notFound),
put('/*', notFound),
patch('/*', notFound),
del('/*', notFound)
].filter(Boolean)
module.exports = micro(
attachCorsHeaders(
catchError(
router(...routes)
)
)
)