Skip to content

Commit 69f77bc

Browse files
committed
Handle websocket subscriptions in GraphiQL
1 parent 593f69e commit 69f77bc

File tree

2 files changed

+112
-51
lines changed

2 files changed

+112
-51
lines changed

graphiql.go

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ import (
44
"encoding/json"
55
"html/template"
66
"net/http"
7+
"strings"
78

89
"github.com/graphql-go/graphql"
910
)
1011

1112
// page is the page data structure of the rendered GraphiQL page
1213
type graphiqlPage struct {
13-
GraphiqlVersion string
14-
QueryString string
15-
ResultString string
16-
VariablesString string
17-
OperationName string
18-
EndpointURL template.URL
14+
GraphiqlVersion string
15+
SubscriptionTransportVersion string
16+
QueryString string
17+
ResultString string
18+
VariablesString string
19+
OperationName string
20+
EndpointURL template.URL
21+
EndpointURLWS template.URL
22+
SubscriptionsEndpoint template.URL
23+
UsingHTTP bool
24+
UsingWS bool
1925
}
2026

2127
// renderGraphiQL renders the GraphiQL GUI
@@ -51,13 +57,30 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params, handler Handle
5157
resString = string(result)
5258
}
5359

60+
endpointWS := strings.HasPrefix(handler.EndpointURL, "ws://")
61+
UsingHTTP := !endpointWS
62+
UsingWS := endpointWS || handler.SubscriptionsEndpoint != ""
63+
EndpointURLWS := ""
64+
if UsingWS {
65+
if endpointWS {
66+
EndpointURLWS = handler.EndpointURL
67+
} else {
68+
EndpointURLWS = handler.SubscriptionsEndpoint
69+
}
70+
}
71+
5472
p := graphiqlPage{
55-
GraphiqlVersion: graphiqlVersion,
56-
QueryString: params.RequestString,
57-
ResultString: resString,
58-
VariablesString: varsString,
59-
OperationName: params.OperationName,
60-
EndpointURL: template.URL(handler.EndpointURL),
73+
GraphiqlVersion: graphiqlVersion,
74+
SubscriptionTransportVersion: subscriptionTransportVersion,
75+
QueryString: params.RequestString,
76+
ResultString: resString,
77+
VariablesString: varsString,
78+
OperationName: params.OperationName,
79+
EndpointURL: template.URL(handler.EndpointURL),
80+
EndpointURLWS: template.URL(EndpointURLWS),
81+
SubscriptionsEndpoint: template.URL(handler.SubscriptionsEndpoint),
82+
UsingHTTP: UsingHTTP,
83+
UsingWS: UsingWS,
6184
}
6285

6386
err = t.ExecuteTemplate(w, "index", p)
@@ -70,6 +93,9 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params, handler Handle
7093
// graphiqlVersion is the current version of GraphiQL
7194
const graphiqlVersion = "0.11.10"
7295

96+
// subscriptionTransportVersion is the current version of the subscription transport of GraphiQL
97+
const subscriptionTransportVersion = "0.8.2"
98+
7399
// tmpl is the page template to render GraphiQL
74100
const graphiqlTemplate = `
75101
{{ define "index" }}
@@ -96,10 +122,20 @@ add "&raw" to the end of the URL within a browser.
96122
}
97123
</style>
98124
<link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" />
99-
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
100125
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
101126
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>
102127
<script src="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.min.js"></script>
128+
129+
{{ if .UsingHTTP }}
130+
<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>
131+
{{ end }}
132+
{{ if .UsingWS }}
133+
<script src="//unpkg.com/subscriptions-transport-ws@{{ .SubscriptionTransportVersion }}/browser/client.js"></script>
134+
{{ end }}
135+
{{ if and .UsingWS .UsingHTTP }}
136+
<script src="//unpkg.com/[email protected]/browser/client.js"></script>
137+
{{ end }}
138+
103139
</head>
104140
<body>
105141
<script>
@@ -136,28 +172,49 @@ add "&raw" to the end of the URL within a browser.
136172
otherParams[k] = parameters[k];
137173
}
138174
}
139-
var fetchURL = locationQuery(otherParams, {{ .EndpointURL }});
140-
141-
// Defines a GraphQL fetcher using the fetch API.
142-
function graphQLFetcher(graphQLParams) {
143-
return fetch(fetchURL, {
144-
method: 'post',
145-
headers: {
146-
'Accept': 'application/json',
147-
'Content-Type': 'application/json'
148-
},
149-
body: JSON.stringify(graphQLParams),
150-
credentials: 'include',
151-
}).then(function (response) {
152-
return response.text();
153-
}).then(function (responseBody) {
154-
try {
155-
return JSON.parse(responseBody);
156-
} catch (error) {
157-
return responseBody;
158-
}
175+
176+
{{ if .UsingWS }}
177+
var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient({{ .EndpointURLWS }}, {
178+
reconnect: true
159179
});
160-
}
180+
var graphQLWSFetcher = subscriptionsClient.request.bind(subscriptionsClient);
181+
{{ end }}
182+
183+
{{ if .UsingHTTP }}
184+
var fetchURL = locationQuery(otherParams, {{ .EndpointURL }});
185+
186+
// Defines a GraphQL fetcher using the fetch API.
187+
function graphQLHttpFetcher(graphQLParams) {
188+
return fetch(fetchURL, {
189+
method: 'post',
190+
headers: {
191+
'Accept': 'application/json',
192+
'Content-Type': 'application/json'
193+
},
194+
body: JSON.stringify(graphQLParams),
195+
credentials: 'include',
196+
}).then(function (response) {
197+
return response.text();
198+
}).then(function (responseBody) {
199+
try {
200+
return JSON.parse(responseBody);
201+
} catch (error) {
202+
return responseBody;
203+
}
204+
});
205+
}
206+
{{ end }}
207+
208+
{{ if and .UsingWS .UsingHTTP }}
209+
var fetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLHttpFetcher);
210+
{{ else }}
211+
{{ if .UsingWS }}
212+
var fetcher = 'graphQLWSFetcher';
213+
{{ end }}
214+
{{ if .UsingHTTP }}
215+
var fetcher = 'graphQLHttpFetcher';
216+
{{ end }}
217+
{{ end }}
161218
162219
// When the query and variables string is edited, update the URL bar so
163220
// that it can be easily shared.
@@ -183,7 +240,7 @@ add "&raw" to the end of the URL within a browser.
183240
// Render <GraphiQL /> into the body.
184241
ReactDOM.render(
185242
React.createElement(GraphiQL, {
186-
fetcher: graphQLFetcher,
243+
fetcher: fetcher,
187244
onEditQuery: onEditQuery,
188245
onEditVariables: onEditVariables,
189246
onEditOperationName: onEditOperationName,

handler.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ const (
1919
)
2020

2121
type Handler struct {
22-
Schema *graphql.Schema
23-
pretty bool
24-
graphiql bool
25-
EndpointURL string
22+
Schema *graphql.Schema
23+
pretty bool
24+
graphiql bool
25+
EndpointURL string
26+
SubscriptionsEndpoint string
2627
}
2728
type RequestOptions struct {
2829
Query string `json:"query" url:"query" schema:"query"`
@@ -161,18 +162,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
161162
}
162163

163164
type Config struct {
164-
Schema *graphql.Schema
165-
Pretty bool
166-
GraphiQL bool
167-
EndpointURL string
165+
Schema *graphql.Schema
166+
Pretty bool
167+
GraphiQL bool
168+
EndpointURL string
169+
SubscriptionsEndpoint string
168170
}
169171

170172
func NewConfig() *Config {
171173
return &Config{
172-
Schema: nil,
173-
Pretty: true,
174-
GraphiQL: true,
175-
EndpointURL: "/",
174+
Schema: nil,
175+
Pretty: true,
176+
GraphiQL: true,
177+
EndpointURL: "",
178+
SubscriptionsEndpoint: "",
176179
}
177180
}
178181

@@ -185,9 +188,10 @@ func New(p *Config) *Handler {
185188
}
186189

187190
return &Handler{
188-
Schema: p.Schema,
189-
pretty: p.Pretty,
190-
graphiql: p.GraphiQL,
191-
EndpointURL: p.EndpointURL,
191+
Schema: p.Schema,
192+
pretty: p.Pretty,
193+
graphiql: p.GraphiQL,
194+
EndpointURL: p.EndpointURL,
195+
SubscriptionsEndpoint: p.SubscriptionsEndpoint,
192196
}
193197
}

0 commit comments

Comments
 (0)