parent
ce086cba19
commit
1a31ccfe41
|
@ -6,7 +6,9 @@ import {
|
||||||
GraphQLNonNull,
|
GraphQLNonNull,
|
||||||
} from 'graphql'
|
} from 'graphql'
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
|
import type { Context, Next } from 'hono'
|
||||||
import { errorMessages, graphqlServer } from '.'
|
import { errorMessages, graphqlServer } from '.'
|
||||||
|
import type { RootResolver } from './index'
|
||||||
|
|
||||||
// Do not show `console.error` messages
|
// Do not show `console.error` messages
|
||||||
jest.spyOn(console, 'error').mockImplementation()
|
jest.spyOn(console, 'error').mockImplementation()
|
||||||
|
@ -26,34 +28,32 @@ describe('errorMessages', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GraphQL Middleware - Simple way', () => {
|
describe('GraphQL Middleware - Simple way', () => {
|
||||||
// Construct a schema, using GraphQL schema language
|
|
||||||
const schema = buildSchema(`
|
|
||||||
type Query {
|
|
||||||
hello: String
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
// The root provides a resolver function for each API endpoint
|
|
||||||
const rootValue = {
|
|
||||||
hello: () => 'Hello world!',
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new Hono()
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
'/graphql',
|
|
||||||
graphqlServer({
|
|
||||||
schema,
|
|
||||||
rootValue,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
app.all('*', (c) => {
|
|
||||||
c.header('foo', 'bar')
|
|
||||||
return c.text('fallback')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should return GraphQL response', async () => {
|
it('Should return GraphQL response', async () => {
|
||||||
|
const schema = buildSchema(`
|
||||||
|
type Query {
|
||||||
|
hello: String
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
const rootValue = {
|
||||||
|
hello: () => 'Hello world!',
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/graphql',
|
||||||
|
graphqlServer({
|
||||||
|
schema,
|
||||||
|
rootResolver: () => rootValue,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
app.all('*', (c) => {
|
||||||
|
c.header('foo', 'bar')
|
||||||
|
return c.text('fallback')
|
||||||
|
})
|
||||||
|
|
||||||
const query = 'query { hello }'
|
const query = 'query { hello }'
|
||||||
const body = {
|
const body = {
|
||||||
query: query,
|
query: query,
|
||||||
|
@ -71,6 +71,59 @@ describe('GraphQL Middleware - Simple way', () => {
|
||||||
expect(await res.text()).toBe('{"data":{"hello":"Hello world!"}}')
|
expect(await res.text()).toBe('{"data":{"hello":"Hello world!"}}')
|
||||||
expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler
|
expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should access the ctx', async () => {
|
||||||
|
const schema = buildSchema(`
|
||||||
|
type Query {
|
||||||
|
hi: String
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
const rootResolver: RootResolver = (ctx?: Context) => {
|
||||||
|
const name = ctx?.get('name')
|
||||||
|
return {
|
||||||
|
hi: `hi ${name}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
app.use('*', async (ctx: Context, next: Next) => {
|
||||||
|
ctx.set('name', 'Jason')
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/graphql',
|
||||||
|
graphqlServer({
|
||||||
|
schema,
|
||||||
|
rootResolver,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
app.all('*', (c) => {
|
||||||
|
c.header('foo', 'bar')
|
||||||
|
return c.text('fallback')
|
||||||
|
})
|
||||||
|
|
||||||
|
const query = 'query { hi }'
|
||||||
|
const body = {
|
||||||
|
query: query,
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await app.request('http://localhost/graphql', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
expect(res).not.toBeNull()
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.text()).toBe('{"data":{"hi":"hi Jason"}}')
|
||||||
|
expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const QueryRootType = new GraphQLObjectType({
|
const QueryRootType = new GraphQLObjectType({
|
||||||
|
|
16
src/index.ts
16
src/index.ts
|
@ -1,6 +1,3 @@
|
||||||
// Based on the code in the `express-graphql` package.
|
|
||||||
// https://github.com/graphql/express-graphql/blob/main/src/index.ts
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Source,
|
Source,
|
||||||
parse,
|
parse,
|
||||||
|
@ -21,12 +18,13 @@ import type {
|
||||||
} from 'graphql'
|
} from 'graphql'
|
||||||
|
|
||||||
import type { Context } from 'hono'
|
import type { Context } from 'hono'
|
||||||
import type { Next } from 'hono'
|
|
||||||
import { parseBody } from './parse-body'
|
import { parseBody } from './parse-body'
|
||||||
|
|
||||||
|
export type RootResolver = (ctx?: Context) => Promise<unknown> | unknown
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
schema: GraphQLSchema
|
schema: GraphQLSchema
|
||||||
rootValue?: unknown
|
rootResolver?: RootResolver,
|
||||||
pretty?: boolean
|
pretty?: boolean
|
||||||
validationRules?: ReadonlyArray<ValidationRule>
|
validationRules?: ReadonlyArray<ValidationRule>
|
||||||
// graphiql?: boolean
|
// graphiql?: boolean
|
||||||
|
@ -34,12 +32,11 @@ type Options = {
|
||||||
|
|
||||||
export const graphqlServer = (options: Options) => {
|
export const graphqlServer = (options: Options) => {
|
||||||
const schema = options.schema
|
const schema = options.schema
|
||||||
const rootValue = options.rootValue
|
|
||||||
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, next: Next) => {
|
return async (c: Context) => {
|
||||||
// GraphQL HTTP only supports GET and POST methods.
|
// GraphQL HTTP only supports GET and POST methods.
|
||||||
if (c.req.method !== 'GET' && c.req.method !== 'POST') {
|
if (c.req.method !== 'GET' && c.req.method !== 'POST') {
|
||||||
return c.json(errorMessages(['GraphQL only supports GET and POST requests.']), 405, {
|
return c.json(errorMessages(['GraphQL only supports GET and POST requests.']), 405, {
|
||||||
|
@ -119,12 +116,13 @@ export const graphqlServer = (options: Options) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: FormattedExecutionResult
|
let result: FormattedExecutionResult
|
||||||
|
const { rootResolver } = options
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await execute({
|
result = await execute({
|
||||||
schema,
|
schema,
|
||||||
document: documentAST,
|
document: documentAST,
|
||||||
rootValue,
|
rootValue: rootResolver ? await rootResolver(c) : null,
|
||||||
variableValues: variables,
|
variableValues: variables,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
})
|
})
|
||||||
|
@ -161,8 +159,6 @@ export const graphqlServer = (options: Options) => {
|
||||||
} else {
|
} else {
|
||||||
return c.json(result)
|
return c.json(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
await next() // XXX
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue