...

Source file src/github.com/99designs/gqlgen/graphql/handler/server.go

Documentation: github.com/99designs/gqlgen/graphql/handler

     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  }
   188  

View as plain text