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({
|
||||
schema,
|
||||
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>
|
||||
pretty?: boolean
|
||||
validationRules?: ReadonlyArray<ValidationRule>
|
||||
// graphiql?: boolean
|
||||
graphiql?: boolean
|
||||
}
|
||||
|
||||
// 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 pretty = options.pretty ?? false
|
||||
const validationRules = options.validationRules ?? []
|
||||
// const showGraphiQL = options.graphiql ?? false
|
||||
const showGraphiQL = options.graphiql ?? false
|
||||
|
||||
return async (c: Context<E, P, I>) => {
|
||||
// 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
|
||||
|
||||
if (query == null) {
|
||||
if (showGraphiQL && c.req.method === 'GET') {
|
||||
return respondWithGraphiQL(c)
|
||||
}
|
||||
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.
|
||||
const operationAST = getOperationAST(documentAST, operationName)
|
||||
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.
|
||||
return c.json(
|
||||
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) {
|
||||
const payload = JSON.stringify(result, null, pretty ? 2 : 0)
|
||||
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,
|
||||
})
|
||||
)
|
||||
app.use(
|
||||
'/graphql-with-graphiql',
|
||||
graphqlServer({
|
||||
schema: TestSchema,
|
||||
graphiql: true,
|
||||
})
|
||||
)
|
||||
|
||||
it('Allows GET with variable values', async () => {
|
||||
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', () => {
|
||||
|
|
Loading…
Reference in New Issue