feat(graphql-server): add GraphiQL support (#670)
* feat(graphql-server): add GraphiQL support * test(graphql-server): add GraphiQL tests * docs(graphql-server): add GraphiQL usage * chore: add changesetpull/671/head
parent
59894e77c0
commit
e3eafbc117
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@hono/graphql-server': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: add GraphiQL support
|
|
@ -42,6 +42,7 @@ app.use(
|
||||||
graphqlServer({
|
graphqlServer({
|
||||||
schema,
|
schema,
|
||||||
rootResolver,
|
rootResolver,
|
||||||
|
graphiql: true, // if `true`, presents GraphiQL when the GraphQL endpoint is loaded in a browser.
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ type Options<E extends Env = any, P extends string = any, I extends Input = {}>
|
||||||
rootResolver?: RootResolver<E, P, I>
|
rootResolver?: RootResolver<E, P, I>
|
||||||
pretty?: boolean
|
pretty?: boolean
|
||||||
validationRules?: ReadonlyArray<ValidationRule>
|
validationRules?: ReadonlyArray<ValidationRule>
|
||||||
// graphiql?: boolean
|
graphiql?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -44,7 +44,7 @@ export const graphqlServer = <E extends Env = any, P extends string = any, I ext
|
||||||
const schema = options.schema
|
const schema = options.schema
|
||||||
const pretty = options.pretty ?? false
|
const pretty = options.pretty ?? false
|
||||||
const validationRules = options.validationRules ?? []
|
const validationRules = options.validationRules ?? []
|
||||||
// const showGraphiQL = options.graphiql ?? false
|
const showGraphiQL = options.graphiql ?? false
|
||||||
|
|
||||||
return async (c: Context<E, P, I>) => {
|
return async (c: Context<E, P, I>) => {
|
||||||
// GraphQL HTTP only supports GET and POST methods.
|
// GraphQL HTTP only supports GET and POST methods.
|
||||||
|
@ -68,6 +68,9 @@ export const graphqlServer = <E extends Env = any, P extends string = any, I ext
|
||||||
const { query, variables, operationName } = params
|
const { query, variables, operationName } = params
|
||||||
|
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
|
if (showGraphiQL && c.req.method === 'GET') {
|
||||||
|
return respondWithGraphiQL(c)
|
||||||
|
}
|
||||||
return c.json(errorMessages(['Must provide query string.']), 400)
|
return c.json(errorMessages(['Must provide query string.']), 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,13 +110,6 @@ export const graphqlServer = <E extends Env = any, P extends string = any, I ext
|
||||||
// Determine if this GET request will perform a non-query.
|
// Determine if this GET request will perform a non-query.
|
||||||
const operationAST = getOperationAST(documentAST, operationName)
|
const operationAST = getOperationAST(documentAST, operationName)
|
||||||
if (operationAST && operationAST.operation !== 'query') {
|
if (operationAST && operationAST.operation !== 'query') {
|
||||||
/*
|
|
||||||
Now , does not support GraphiQL
|
|
||||||
if (showGraphiQL) {
|
|
||||||
//return respondWithGraphiQL(response, graphiqlOptions, params)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Otherwise, report a 405: Method Not Allowed error.
|
// Otherwise, report a 405: Method Not Allowed error.
|
||||||
return c.json(
|
return c.json(
|
||||||
errorMessages([
|
errorMessages([
|
||||||
|
@ -156,12 +152,6 @@ export const graphqlServer = <E extends Env = any, P extends string = any, I ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Now, does not support GraphiQL
|
|
||||||
if (showGraphiQL) {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (pretty) {
|
if (pretty) {
|
||||||
const payload = JSON.stringify(result, null, pretty ? 2 : 0)
|
const payload = JSON.stringify(result, null, pretty ? 2 : 0)
|
||||||
return c.text(payload, 200, {
|
return c.text(payload, 200, {
|
||||||
|
@ -242,4 +232,87 @@ export const errorMessages = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const graphiQLResponse = () => {}
|
export const respondWithGraphiQL = (c: Context) => {
|
||||||
|
// https://github.com/graphql/graphiql/blob/85edb9e0505db8ff963c9ad4674bc8fa2e02a35a/examples/graphiql-cdn/index.html
|
||||||
|
return c.html(`<!--
|
||||||
|
* Copyright (c) 2021 GraphQL Contributors
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
-->
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>GraphiQL</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphiql {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!--
|
||||||
|
This GraphiQL example depends on Promise and fetch, which are available in
|
||||||
|
modern browsers, but can be "polyfilled" for older browsers.
|
||||||
|
GraphiQL itself depends on React DOM.
|
||||||
|
If you do not want to rely on a CDN, you can host these files locally or
|
||||||
|
include them directly in your favored resource bundler.
|
||||||
|
-->
|
||||||
|
<script
|
||||||
|
crossorigin
|
||||||
|
src="https://unpkg.com/react@18/umd/react.development.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
crossorigin
|
||||||
|
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
|
||||||
|
></script>
|
||||||
|
<!--
|
||||||
|
These two files can be found in the npm module, however you may wish to
|
||||||
|
copy them directly into your environment, or perhaps include them in your
|
||||||
|
favored resource bundler.
|
||||||
|
-->
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/graphiql/graphiql.min.js"
|
||||||
|
type="application/javascript"
|
||||||
|
></script>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
|
||||||
|
<!--
|
||||||
|
These are imports for the GraphIQL Explorer plugin.
|
||||||
|
-->
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/@graphiql/plugin-explorer/dist/index.umd.js"
|
||||||
|
crossorigin
|
||||||
|
></script>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/@graphiql/plugin-explorer/dist/style.css"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="graphiql">Loading...</div>
|
||||||
|
<script>
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('graphiql'));
|
||||||
|
const fetcher = GraphiQL.createFetcher({
|
||||||
|
url: '${c.req.path}',
|
||||||
|
});
|
||||||
|
const explorerPlugin = GraphiQLPluginExplorer.explorerPlugin();
|
||||||
|
root.render(
|
||||||
|
React.createElement(GraphiQL, {
|
||||||
|
fetcher,
|
||||||
|
defaultEditorToolsVisibility: true,
|
||||||
|
plugins: [explorerPlugin],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
|
@ -174,6 +174,13 @@ describe('GraphQL Middleware - GET functionality', () => {
|
||||||
schema: TestSchema,
|
schema: TestSchema,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
app.use(
|
||||||
|
'/graphql-with-graphiql',
|
||||||
|
graphqlServer({
|
||||||
|
schema: TestSchema,
|
||||||
|
graphiql: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
it('Allows GET with variable values', async () => {
|
it('Allows GET with variable values', async () => {
|
||||||
const query = {
|
const query = {
|
||||||
|
@ -276,6 +283,21 @@ describe('GraphQL Middleware - GET functionality', () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Errors when query is not provided and GraphiQL is disabled', async () => {
|
||||||
|
const res = await app.request('http://localhost/graphql', {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
expect(res.status).toBe(400)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Renders GraphiQL when query is not provided and GraphiQL is enabled', async () => {
|
||||||
|
const res = await app.request('http://localhost/graphql-with-graphiql', {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.text()).toContain('<div id="graphiql">')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GraphQL Middleware - POST functionality', () => {
|
describe('GraphQL Middleware - POST functionality', () => {
|
||||||
|
|
Loading…
Reference in New Issue