Skip to content

Allow custom endpoint URL. #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 93 additions & 34 deletions graphiql.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@ import (
"encoding/json"
"html/template"
"net/http"
"strings"

"github.com/graphql-go/graphql"
)

// page is the page data structure of the rendered GraphiQL page
type graphiqlPage struct {
GraphiqlVersion string
QueryString string
ResultString string
VariablesString string
OperationName string
GraphiqlVersion string
SubscriptionTransportVersion string
QueryString string
ResultString string
VariablesString string
OperationName string
EndpointURL template.URL
EndpointURLWS template.URL
SubscriptionsEndpoint template.URL
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only EndpointURLWS is being used in the template, SubscriptionsEndpoint isn't. I personally favor the name SubscriptionsEndpoint and would pass Endpoint and SubscriptionsEndpoint (falling back to Endpoint if not set) in to the template.

UsingHTTP bool
UsingWS bool
}

// renderGraphiQL renders the GraphiQL GUI
func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
func renderGraphiQL(w http.ResponseWriter, params graphql.Params, handler Handler) {
t := template.New("GraphiQL")
t, err := t.Parse(graphiqlTemplate)
if err != nil {
Expand Down Expand Up @@ -50,12 +57,30 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
resString = string(result)
}

endpointWS := strings.HasPrefix(handler.EndpointURL, "ws://")
UsingHTTP := !endpointWS
UsingWS := endpointWS || handler.SubscriptionsEndpoint != ""
EndpointURLWS := ""
if UsingWS {
if endpointWS {
EndpointURLWS = handler.EndpointURL
} else {
EndpointURLWS = handler.SubscriptionsEndpoint
}
}

p := graphiqlPage{
GraphiqlVersion: graphiqlVersion,
QueryString: params.RequestString,
ResultString: resString,
VariablesString: varsString,
OperationName: params.OperationName,
GraphiqlVersion: graphiqlVersion,
SubscriptionTransportVersion: subscriptionTransportVersion,
QueryString: params.RequestString,
ResultString: resString,
VariablesString: varsString,
OperationName: params.OperationName,
EndpointURL: template.URL(handler.EndpointURL),
EndpointURLWS: template.URL(EndpointURLWS),
SubscriptionsEndpoint: template.URL(handler.SubscriptionsEndpoint),
UsingHTTP: UsingHTTP,
UsingWS: UsingWS,
}

err = t.ExecuteTemplate(w, "index", p)
Expand All @@ -68,6 +93,9 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
// graphiqlVersion is the current version of GraphiQL
const graphiqlVersion = "0.11.10"

// subscriptionTransportVersion is the current version of the subscription transport of GraphiQL
const subscriptionTransportVersion = "0.8.2"

// tmpl is the page template to render GraphiQL
const graphiqlTemplate = `
{{ define "index" }}
Expand All @@ -94,10 +122,20 @@ add "&raw" to the end of the URL within a browser.
}
</style>
<link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" />
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.min.js"></script>

{{ if .UsingHTTP }}
<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>
{{ end }}
{{ if .UsingWS }}
<script src="//unpkg.com/subscriptions-transport-ws@{{ .SubscriptionTransportVersion }}/browser/client.js"></script>
{{ end }}
{{ if and .UsingWS .UsingHTTP }}
<script src="//unpkg.com/[email protected]/browser/client.js"></script>
{{ end }}

</head>
<body>
<script>
Expand Down Expand Up @@ -134,28 +172,49 @@ add "&raw" to the end of the URL within a browser.
otherParams[k] = parameters[k];
}
}
var fetchURL = locationQuery(otherParams);

// Defines a GraphQL fetcher using the fetch API.
function graphQLFetcher(graphQLParams) {
return fetch(fetchURL, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(graphQLParams),
credentials: 'include',
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}

{{ if .UsingWS }}
var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient({{ .EndpointURLWS }}, {
reconnect: true
});
}
var graphQLWSFetcher = subscriptionsClient.request.bind(subscriptionsClient);
{{ end }}

{{ if .UsingHTTP }}
var fetchURL = locationQuery(otherParams, {{ .EndpointURL }});

// Defines a GraphQL fetcher using the fetch API.
function graphQLHttpFetcher(graphQLParams) {
return fetch(fetchURL, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(graphQLParams),
credentials: 'include',
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}
});
}
{{ end }}

{{ if and .UsingWS .UsingHTTP }}
var fetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLHttpFetcher);
{{ else }}
{{ if .UsingWS }}
var fetcher = 'graphQLWSFetcher';
{{ end }}
{{ if .UsingHTTP }}
var fetcher = 'graphQLHttpFetcher';
{{ end }}
{{ end }}

// When the query and variables string is edited, update the URL bar so
// that it can be easily shared.
Expand All @@ -181,7 +240,7 @@ add "&raw" to the end of the URL within a browser.
// Render <GraphiQL /> into the body.
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: graphQLFetcher,
fetcher: fetcher,
onEditQuery: onEditQuery,
onEditVariables: onEditVariables,
onEditOperationName: onEditOperationName,
Expand Down
34 changes: 21 additions & 13 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const (
)

type Handler struct {
Schema *graphql.Schema
pretty bool
graphiql bool
Schema *graphql.Schema
pretty bool
graphiql bool
EndpointURL string
SubscriptionsEndpoint string
}
type RequestOptions struct {
Query string `json:"query" url:"query" schema:"query"`
Expand Down Expand Up @@ -133,7 +135,7 @@ func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *
acceptHeader := r.Header.Get("Accept")
_, raw := r.URL.Query()["raw"]
if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
renderGraphiQL(w, params)
renderGraphiQL(w, params, *h)
return
}
}
Expand All @@ -160,16 +162,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

type Config struct {
Schema *graphql.Schema
Pretty bool
GraphiQL bool
Schema *graphql.Schema
Pretty bool
GraphiQL bool
EndpointURL string
SubscriptionsEndpoint string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making EndpointURL and SubscriptionsEndpoint top-level configuration fields, it would probably be cleaner to group them under a GraphiQLConfig field that can optionally be passed in together with GraphiQL: true. Example:

handler.New(&handler.Config{
    Schema: ...,
    GraphiQL: true,
    GraphiQLConfig: &handler.GraphiQLConfig{
        Endpoint: ...,
        SubscriptionsEndpoint: ...,
    }
})

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the Apollo server code for GraphiQL mentioned in #31 has a few more configuration variables that might make sense to add here (like additional HTTP headers to be passed to the endpoint for e.g. authentication).

}

func NewConfig() *Config {
return &Config{
Schema: nil,
Pretty: true,
GraphiQL: true,
Schema: nil,
Pretty: true,
GraphiQL: true,
EndpointURL: "",
SubscriptionsEndpoint: "",
}
}

Expand All @@ -182,8 +188,10 @@ func New(p *Config) *Handler {
}

return &Handler{
Schema: p.Schema,
pretty: p.Pretty,
graphiql: p.GraphiQL,
Schema: p.Schema,
pretty: p.Pretty,
graphiql: p.GraphiQL,
EndpointURL: p.EndpointURL,
SubscriptionsEndpoint: p.SubscriptionsEndpoint,
}
}