Skip to content
Snippets Groups Projects
Commit b1d40312 authored by Simao Gomes Viana's avatar Simao Gomes Viana :rainbow:
Browse files

Add handler config, some other stuff, and docs

parent a4234414
No related branches found
No related tags found
No related merge requests found
# Go + GraphQL + PostgreSQL (ORM)
Generators that provide a GraphQL API to your
PostgreSQL database using your models defined in Go.
Creates the schema for you, sets up a GraphQL API and
handles everything from getting one or many entries,
creating, updating and deleting.
This makes everything really easy to use and provides
a quick way to get started while still giving you control over
everything.
......@@ -12,12 +12,94 @@ import (
"github.com/graphql-go/graphql"
)
// MainContextKey is used to provide a structured context object
// to all GraphQL resolvers
const MainContextKey = "mainContext"
func GenerateGraphQLAPIHandler(schema *graphql.Schema) http.HandlerFunc {
// HTTPHandlerFuncWithError is the type used for middlewares
type HTTPHandlerFuncWithError func(w http.ResponseWriter, r *http.Request) error
// HandlerConfig allows you to attach middlewares
// and configure the behavior as you like
type HandlerConfig struct {
// Runs before any processing is done for requests
OnRequest HTTPHandlerFuncWithError
// Runs before processing the GraphQL query but after
// doing some initial processing
PreProcess HTTPHandlerFuncWithError
// Runs right after the GraphQL query has been processed
// and before the result is checked for errors
PostProcess HTTPHandlerFuncWithError
// Runs after checking for errors in the GraphQL query result
// and before writing the result to the client. Only runs if
// no errors were encountered.
PreWriteResult HTTPHandlerFuncWithError
// Runs after writing the result to the client. This is the last
// of all handlers that is run.
PostWriteResult HTTPHandlerFuncWithError
// Specifying `true` will disallow using the URL query parameter
// as a means of transporting the GraphQL query
DisableURLQuery bool
// Specifying `true` will disallow using the POST body
// as a means of transporting the GraphQL query
DisableBodyQuery bool
}
type requestError struct {
error string
}
// runMiddleware runs the handler and uses the encoder to
// write errors as responses. Parameters w and r are both
// passed to the handler. In case of an error, HTTP status
// code 500 will be sent to the client along with an error
// message inside a JSON
func runMiddleware(handler HTTPHandlerFuncWithError, encoder *json.Encoder, w http.ResponseWriter, r *http.Request) {
if handler != nil {
if err := handler(w, r); err != nil {
w.WriteHeader(500)
if encoder != nil {
_ = encoder.Encode(requestError{
error: err.Error(),
})
}
}
}
}
// GenerateGraphQLAPIHandler generates a HTTP handler func that
// prepares and processes requests to the GraphQL API in an
// automated fashion
func GenerateGraphQLAPIHandler(schema *graphql.Schema, config *HandlerConfig, contextObj interface{}) http.HandlerFunc {
// Check if the config is valid
if config.DisableBodyQuery && config.DisableURLQuery {
// Only one can be disabled, otherwise requests wouldn't work
panic("GenerateGraphQLAPIHandler: can't disable both body and URL queries")
}
// In case no context was passed, create a default one
// just so that we have something to pass around
contextToUse := contextObj
if contextToUse == nil {
contextToUse = &ctx.Context{}
}
// This is the actual function that runs when a request comes in
return func(w http.ResponseWriter, r *http.Request) {
responseEncoderWriter := json.NewEncoder(w)
runMiddleware(config.OnRequest, responseEncoderWriter, w, r)
requestString := ""
if r.Method == "POST" {
if config.DisableBodyQuery {
w.WriteHeader(400)
_ = responseEncoderWriter.Encode(requestError{
error: "body queries are disabled, please use URL queries (.../?query=...)",
})
return
}
bytes, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err.Error())
......@@ -25,20 +107,38 @@ func GenerateGraphQLAPIHandler(schema *graphql.Schema) http.HandlerFunc {
}
requestString = string(bytes)
} else {
if config.DisableURLQuery {
w.WriteHeader(400)
_ = responseEncoderWriter.Encode(requestError{
error: "URL queries are disabled, please use body queries (query in a POST body)",
})
return
}
requestString = r.URL.Query().Get("query")
}
runMiddleware(config.PreProcess, responseEncoderWriter, w, r)
result := graphql.Do(graphql.Params{
Schema: *schema,
RequestString: requestString,
Context: context.WithValue(context.Background(), MainContextKey, &ctx.Context{}),
Context: context.WithValue(context.Background(), MainContextKey, contextToUse),
})
runMiddleware(config.PostProcess, responseEncoderWriter, w, r)
if len(result.Errors) > 0 {
log.Printf("wrong result, unexpected errors: %v\n", result.Errors)
return
}
if err := json.NewEncoder(w).Encode(result); err != nil {
runMiddleware(config.PreWriteResult, responseEncoderWriter, w, r)
if err := responseEncoderWriter.Encode(result); err != nil {
log.Println(err.Error())
}
runMiddleware(config.PostWriteResult, responseEncoderWriter, w, r)
}
}
......@@ -100,6 +100,14 @@ func main() {
})
http.Handle("/graphql", h)
http.HandleFunc("/api/v1", api.GenerateGraphQLAPIHandler(schema))
http.HandleFunc("/api/v1", api.GenerateGraphQLAPIHandler(
schema,
&api.HandlerConfig{
OnRequest: nil,
PreProcess: nil,
PostProcess: nil,
PreWriteResult: nil,
PostWriteResult: nil,
}, nil))
panic(http.ListenAndServe(":8080", nil).Error())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment