...
1 package handler
2
3 import (
4 "context"
5 "net/http"
6 "time"
7
8 "github.com/gorilla/websocket"
9
10 "github.com/99designs/gqlgen/graphql"
11 "github.com/99designs/gqlgen/graphql/handler"
12 "github.com/99designs/gqlgen/graphql/handler/extension"
13 "github.com/99designs/gqlgen/graphql/handler/lru"
14 "github.com/99designs/gqlgen/graphql/handler/transport"
15 "github.com/99designs/gqlgen/graphql/playground"
16 )
17
18
19 func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc {
20 var cfg Config
21 cfg.cacheSize = 1000
22
23 for _, option := range options {
24 option(&cfg)
25 }
26
27 srv := handler.New(exec)
28
29 srv.AddTransport(transport.Websocket{
30 Upgrader: cfg.upgrader,
31 InitFunc: cfg.websocketInitFunc,
32 KeepAlivePingInterval: cfg.connectionKeepAlivePingInterval,
33 PingPongInterval: cfg.connectionPingPongInterval,
34 })
35 srv.AddTransport(transport.Options{})
36 srv.AddTransport(transport.GET{})
37 srv.AddTransport(transport.POST{})
38 srv.AddTransport(transport.MultipartForm{
39 MaxUploadSize: cfg.uploadMaxSize,
40 MaxMemory: cfg.uploadMaxMemory,
41 })
42
43 if cfg.cacheSize != 0 {
44 srv.SetQueryCache(lru.New(cfg.cacheSize))
45 }
46 if cfg.recover != nil {
47 srv.SetRecoverFunc(cfg.recover)
48 }
49 if cfg.errorPresenter != nil {
50 srv.SetErrorPresenter(cfg.errorPresenter)
51 }
52 for _, hook := range cfg.fieldHooks {
53 srv.AroundFields(hook)
54 }
55 for _, hook := range cfg.requestHooks {
56 srv.AroundResponses(hook)
57 }
58 if cfg.complexityLimit != 0 {
59 srv.Use(extension.FixedComplexityLimit(cfg.complexityLimit))
60 } else if cfg.complexityLimitFunc != nil {
61 srv.Use(&extension.ComplexityLimit{
62 Func: func(ctx context.Context, rc *graphql.OperationContext) int {
63 return cfg.complexityLimitFunc(graphql.WithOperationContext(ctx, rc))
64 },
65 })
66 }
67 if !cfg.disableIntrospection {
68 srv.Use(extension.Introspection{})
69 }
70 if cfg.apqCache != nil {
71 srv.Use(extension.AutomaticPersistedQuery{Cache: apqAdapter{cfg.apqCache}})
72 }
73 return srv.ServeHTTP
74 }
75
76
77 type Config struct {
78 cacheSize int
79 upgrader websocket.Upgrader
80 websocketInitFunc transport.WebsocketInitFunc
81 connectionKeepAlivePingInterval time.Duration
82 connectionPingPongInterval time.Duration
83 recover graphql.RecoverFunc
84 errorPresenter graphql.ErrorPresenterFunc
85 fieldHooks []graphql.FieldMiddleware
86 requestHooks []graphql.ResponseMiddleware
87 complexityLimit int
88 complexityLimitFunc func(ctx context.Context) int
89 disableIntrospection bool
90 uploadMaxMemory int64
91 uploadMaxSize int64
92 apqCache PersistedQueryCache
93 }
94
95
96 type Option func(cfg *Config)
97
98
99 func WebsocketUpgrader(upgrader websocket.Upgrader) Option {
100 return func(cfg *Config) {
101 cfg.upgrader = upgrader
102 }
103 }
104
105
106 func RecoverFunc(recover graphql.RecoverFunc) Option {
107 return func(cfg *Config) {
108 cfg.recover = recover
109 }
110 }
111
112
113
114
115
116 func ErrorPresenter(f graphql.ErrorPresenterFunc) Option {
117 return func(cfg *Config) {
118 cfg.errorPresenter = f
119 }
120 }
121
122
123
124
125 func IntrospectionEnabled(enabled bool) Option {
126 return func(cfg *Config) {
127 cfg.disableIntrospection = !enabled
128 }
129 }
130
131
132
133
134 func ComplexityLimit(limit int) Option {
135 return func(cfg *Config) {
136 cfg.complexityLimit = limit
137 }
138 }
139
140
141
142
143
144 func ComplexityLimitFunc(complexityLimitFunc func(ctx context.Context) int) Option {
145 return func(cfg *Config) {
146 cfg.complexityLimitFunc = complexityLimitFunc
147 }
148 }
149
150
151
152
153 func ResolverMiddleware(middleware graphql.FieldMiddleware) Option {
154 return func(cfg *Config) {
155 cfg.fieldHooks = append(cfg.fieldHooks, middleware)
156 }
157 }
158
159
160
161
162 func RequestMiddleware(middleware graphql.ResponseMiddleware) Option {
163 return func(cfg *Config) {
164 cfg.requestHooks = append(cfg.requestHooks, middleware)
165 }
166 }
167
168
169
170
171 func WebsocketInitFunc(websocketInitFunc transport.WebsocketInitFunc) Option {
172 return func(cfg *Config) {
173 cfg.websocketInitFunc = websocketInitFunc
174 }
175 }
176
177
178
179
180 func CacheSize(size int) Option {
181 return func(cfg *Config) {
182 cfg.cacheSize = size
183 }
184 }
185
186
187
188
189 func UploadMaxSize(size int64) Option {
190 return func(cfg *Config) {
191 cfg.uploadMaxSize = size
192 }
193 }
194
195
196
197
198
199 func UploadMaxMemory(size int64) Option {
200 return func(cfg *Config) {
201 cfg.uploadMaxMemory = size
202 }
203 }
204
205
206
207
208
209
210 func WebsocketKeepAliveDuration(duration time.Duration) Option {
211 return func(cfg *Config) {
212 cfg.connectionKeepAlivePingInterval = duration
213 }
214 }
215
216 func WebsocketPingPongDuration(duration time.Duration) Option {
217 return func(cfg *Config) {
218 cfg.connectionPingPongInterval = duration
219 }
220 }
221
222
223
224 func EnablePersistedQueryCache(cache PersistedQueryCache) Option {
225 return func(cfg *Config) {
226 cfg.apqCache = cache
227 }
228 }
229
230 func GetInitPayload(ctx context.Context) transport.InitPayload {
231 return transport.GetInitPayload(ctx)
232 }
233
234 type apqAdapter struct {
235 PersistedQueryCache
236 }
237
238 func (a apqAdapter) Get(ctx context.Context, key string) (value interface{}, ok bool) {
239 return a.PersistedQueryCache.Get(ctx, key)
240 }
241
242 func (a apqAdapter) Add(ctx context.Context, key string, value interface{}) {
243 a.PersistedQueryCache.Add(ctx, key, value.(string))
244 }
245
246 type PersistedQueryCache interface {
247 Add(ctx context.Context, hash string, query string)
248 Get(ctx context.Context, hash string) (string, bool)
249 }
250
251
252 func Playground(title string, endpoint string) http.HandlerFunc {
253 return playground.Handler(title, endpoint)
254 }
255
256
257 type InitPayload = transport.InitPayload
258
View as plain text