github.com/99designs/gqlgen@v0.17.45/graphql/handler/server.go (about) 1 package handler 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "time" 9 10 "github.com/vektah/gqlparser/v2/gqlerror" 11 12 "github.com/99designs/gqlgen/graphql" 13 "github.com/99designs/gqlgen/graphql/executor" 14 "github.com/99designs/gqlgen/graphql/handler/extension" 15 "github.com/99designs/gqlgen/graphql/handler/lru" 16 "github.com/99designs/gqlgen/graphql/handler/transport" 17 ) 18 19 type ( 20 Server struct { 21 transports []graphql.Transport 22 exec *executor.Executor 23 } 24 ) 25 26 func New(es graphql.ExecutableSchema) *Server { 27 return &Server{ 28 exec: executor.New(es), 29 } 30 } 31 32 func NewDefaultServer(es graphql.ExecutableSchema) *Server { 33 srv := New(es) 34 35 srv.AddTransport(transport.Websocket{ 36 KeepAlivePingInterval: 10 * time.Second, 37 }) 38 srv.AddTransport(transport.Options{}) 39 srv.AddTransport(transport.GET{}) 40 srv.AddTransport(transport.POST{}) 41 srv.AddTransport(transport.MultipartForm{}) 42 43 srv.SetQueryCache(lru.New(1000)) 44 45 srv.Use(extension.Introspection{}) 46 srv.Use(extension.AutomaticPersistedQuery{ 47 Cache: lru.New(100), 48 }) 49 50 return srv 51 } 52 53 func (s *Server) AddTransport(transport graphql.Transport) { 54 s.transports = append(s.transports, transport) 55 } 56 57 func (s *Server) SetErrorPresenter(f graphql.ErrorPresenterFunc) { 58 s.exec.SetErrorPresenter(f) 59 } 60 61 func (s *Server) SetRecoverFunc(f graphql.RecoverFunc) { 62 s.exec.SetRecoverFunc(f) 63 } 64 65 func (s *Server) SetQueryCache(cache graphql.Cache) { 66 s.exec.SetQueryCache(cache) 67 } 68 69 func (s *Server) Use(extension graphql.HandlerExtension) { 70 s.exec.Use(extension) 71 } 72 73 // AroundFields is a convenience method for creating an extension that only implements field middleware 74 func (s *Server) AroundFields(f graphql.FieldMiddleware) { 75 s.exec.AroundFields(f) 76 } 77 78 // AroundRootFields is a convenience method for creating an extension that only implements field middleware 79 func (s *Server) AroundRootFields(f graphql.RootFieldMiddleware) { 80 s.exec.AroundRootFields(f) 81 } 82 83 // AroundOperations is a convenience method for creating an extension that only implements operation middleware 84 func (s *Server) AroundOperations(f graphql.OperationMiddleware) { 85 s.exec.AroundOperations(f) 86 } 87 88 // AroundResponses is a convenience method for creating an extension that only implements response middleware 89 func (s *Server) AroundResponses(f graphql.ResponseMiddleware) { 90 s.exec.AroundResponses(f) 91 } 92 93 func (s *Server) getTransport(r *http.Request) graphql.Transport { 94 for _, t := range s.transports { 95 if t.Supports(r) { 96 return t 97 } 98 } 99 return nil 100 } 101 102 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 103 defer func() { 104 if err := recover(); err != nil { 105 err := s.exec.PresentRecoveredError(r.Context(), err) 106 gqlErr, _ := err.(*gqlerror.Error) 107 resp := &graphql.Response{Errors: []*gqlerror.Error{gqlErr}} 108 b, _ := json.Marshal(resp) 109 w.WriteHeader(http.StatusUnprocessableEntity) 110 w.Write(b) 111 } 112 }() 113 114 r = r.WithContext(graphql.StartOperationTrace(r.Context())) 115 116 transport := s.getTransport(r) 117 if transport == nil { 118 sendErrorf(w, http.StatusBadRequest, "transport not supported") 119 return 120 } 121 122 transport.Do(w, r, s.exec) 123 } 124 125 func sendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { 126 w.WriteHeader(code) 127 b, err := json.Marshal(&graphql.Response{Errors: errors}) 128 if err != nil { 129 panic(err) 130 } 131 w.Write(b) 132 } 133 134 func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { 135 sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) 136 } 137 138 type OperationFunc func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler 139 140 func (r OperationFunc) ExtensionName() string { 141 return "InlineOperationFunc" 142 } 143 144 func (r OperationFunc) Validate(schema graphql.ExecutableSchema) error { 145 if r == nil { 146 return fmt.Errorf("OperationFunc can not be nil") 147 } 148 return nil 149 } 150 151 func (r OperationFunc) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 152 return r(ctx, next) 153 } 154 155 type ResponseFunc func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response 156 157 func (r ResponseFunc) ExtensionName() string { 158 return "InlineResponseFunc" 159 } 160 161 func (r ResponseFunc) Validate(schema graphql.ExecutableSchema) error { 162 if r == nil { 163 return fmt.Errorf("ResponseFunc can not be nil") 164 } 165 return nil 166 } 167 168 func (r ResponseFunc) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 169 return r(ctx, next) 170 } 171 172 type FieldFunc func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) 173 174 func (f FieldFunc) ExtensionName() string { 175 return "InlineFieldFunc" 176 } 177 178 func (f FieldFunc) Validate(schema graphql.ExecutableSchema) error { 179 if f == nil { 180 return fmt.Errorf("FieldFunc can not be nil") 181 } 182 return nil 183 } 184 185 func (f FieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 186 return f(ctx, next) 187 }