parent
ce086cba19
commit
1a31ccfe41
|
@ -6,7 +6,9 @@ import {
|
|||
GraphQLNonNull,
|
||||
} from 'graphql'
|
||||
import { Hono } from 'hono'
|
||||
import type { Context, Next } from 'hono'
|
||||
import { errorMessages, graphqlServer } from '.'
|
||||
import type { RootResolver } from './index'
|
||||
|
||||
// Do not show `console.error` messages
|
||||
jest.spyOn(console, 'error').mockImplementation()
|
||||
|
@ -26,34 +28,32 @@ describe('errorMessages', () => {
|
|||
})
|
||||
|
||||
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 () => {
|
||||
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 body = {
|
||||
query: query,
|
||||
|
@ -71,6 +71,59 @@ describe('GraphQL Middleware - Simple way', () => {
|
|||
expect(await res.text()).toBe('{"data":{"hello":"Hello world!"}}')
|
||||
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({
|
||||
|
|
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 {
|
||||
Source,
|
||||
parse,
|
||||
|
@ -21,12 +18,13 @@ import type {
|
|||
} from 'graphql'
|
||||
|
||||
import type { Context } from 'hono'
|
||||
import type { Next } from 'hono'
|
||||
import { parseBody } from './parse-body'
|
||||
|
||||
export type RootResolver = (ctx?: Context) => Promise<unknown> | unknown
|
||||
|
||||
type Options = {
|
||||
schema: GraphQLSchema
|
||||
rootValue?: unknown
|
||||
rootResolver?: RootResolver,
|
||||
pretty?: boolean
|
||||
validationRules?: ReadonlyArray<ValidationRule>
|
||||
// graphiql?: boolean
|
||||
|
@ -34,12 +32,11 @@ type Options = {
|
|||
|
||||
export const graphqlServer = (options: Options) => {
|
||||
const schema = options.schema
|
||||
const rootValue = options.rootValue
|
||||
const pretty = options.pretty ?? false
|
||||
const validationRules = options.validationRules ?? []
|
||||
// const showGraphiQL = options.graphiql ?? false
|
||||
|
||||
return async (c: Context, next: Next) => {
|
||||
return async (c: Context) => {
|
||||
// GraphQL HTTP only supports GET and POST methods.
|
||||
if (c.req.method !== 'GET' && c.req.method !== 'POST') {
|
||||
return c.json(errorMessages(['GraphQL only supports GET and POST requests.']), 405, {
|
||||
|
@ -119,12 +116,13 @@ export const graphqlServer = (options: Options) => {
|
|||
}
|
||||
|
||||
let result: FormattedExecutionResult
|
||||
const { rootResolver } = options
|
||||
|
||||
try {
|
||||
result = await execute({
|
||||
schema,
|
||||
document: documentAST,
|
||||
rootValue,
|
||||
rootValue: rootResolver ? await rootResolver(c) : null,
|
||||
variableValues: variables,
|
||||
operationName: operationName,
|
||||
})
|
||||
|
@ -161,8 +159,6 @@ export const graphqlServer = (options: Options) => {
|
|||
} else {
|
||||
return c.json(result)
|
||||
}
|
||||
|
||||
await next() // XXX
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue