Appearance
hono-graphql
简介
hono-graphql 是 Hono 框架的 GraphQL 服务器中间件,它允许开发者轻松地在 Hono 应用中集成 GraphQL API。该中间件基于标准的 GraphQL.js 库,提供了完整的 GraphQL 服务器功能,包括查询执行、类型验证和内省。
安装
bash
npm install hono @hono/graphql graphql基本使用
typescript
import { Hono } from 'hono'
import { graphqlServer } from '@hono/graphql-server'
import { GraphQLObjectType, GraphQLSchema, GraphQLString, GraphQLInt, GraphQLList, GraphQLNonNull } from 'graphql'
const app = new Hono()
// 定义 GraphQL 类型
const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLNonNull(GraphQLInt) },
name: { type: GraphQLNonNull(GraphQLString) },
email: { type: GraphQLString },
},
})
// 模拟数据
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
{ id: 3, name: '王五', email: 'wangwu@example.com' },
]
// 定义 GraphQL Schema
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
// 获取单个用户
user: {
type: UserType,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
},
resolve: (_, args) => {
return users.find((user) => user.id === args.id)
},
},
// 获取所有用户
users: {
type: GraphQLList(UserType),
resolve: () => {
return users
},
},
},
}),
})
// 使用 GraphQL 中间件
app.use(
'/graphql',
graphqlServer({
schema,
}),
)
app.listen(3000)高级配置
typescript
import { Hono } from 'hono'
import { graphqlServer } from '@hono/graphql-server'
import {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQLBoolean,
} from 'graphql'
const app = new Hono()
// 定义 GraphQL 类型
const PostType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
title: { type: GraphQLNonNull(GraphQLString) },
content: { type: GraphQLString },
published: { type: GraphQLBoolean },
author: {
type: UserType,
resolve: (post) => {
return users.find((user) => user.id === post.authorId)
},
},
}),
})
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
name: { type: GraphQLNonNull(GraphQLString) },
email: { type: GraphQLString },
posts: {
type: GraphQLList(PostType),
resolve: (user) => {
return posts.filter((post) => post.authorId === user.id)
},
},
}),
})
// 模拟数据
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
]
const posts = [
{ id: 1, title: '第一篇文章', content: '这是内容...', published: true, authorId: 1 },
{ id: 2, title: '第二篇文章', content: '这是内容...', published: false, authorId: 1 },
{ id: 3, title: '第三篇文章', content: '这是内容...', published: true, authorId: 2 },
]
// 定义 GraphQL Schema
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
},
resolve: (_, args) => {
return users.find((user) => user.id === args.id)
},
},
users: {
type: GraphQLList(UserType),
resolve: () => {
return users
},
},
post: {
type: PostType,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
},
resolve: (_, args) => {
return posts.find((post) => post.id === args.id)
},
},
posts: {
type: GraphQLList(PostType),
args: {
published: { type: GraphQLBoolean },
},
resolve: (_, args) => {
if (args.published !== undefined) {
return posts.filter((post) => post.published === args.published)
}
return posts
},
},
},
}),
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: {
createPost: {
type: PostType,
args: {
title: { type: GraphQLNonNull(GraphQLString) },
content: { type: GraphQLString },
authorId: { type: GraphQLNonNull(GraphQLInt) },
published: { type: GraphQLBoolean },
},
resolve: (_, args) => {
const newPost = {
id: posts.length + 1,
title: args.title,
content: args.content || '',
authorId: args.authorId,
published: args.published !== undefined ? args.published : false,
}
posts.push(newPost)
return newPost
},
},
updatePost: {
type: PostType,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
title: { type: GraphQLString },
content: { type: GraphQLString },
published: { type: GraphQLBoolean },
},
resolve: (_, args) => {
const postIndex = posts.findIndex((post) => post.id === args.id)
if (postIndex === -1) return null
const updatedPost = {
...posts[postIndex],
...(args.title !== undefined && { title: args.title }),
...(args.content !== undefined && { content: args.content }),
...(args.published !== undefined && { published: args.published }),
}
posts[postIndex] = updatedPost
return updatedPost
},
},
deletePost: {
type: GraphQLBoolean,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
},
resolve: (_, args) => {
const postIndex = posts.findIndex((post) => post.id === args.id)
if (postIndex === -1) return false
posts.splice(postIndex, 1)
return true
},
},
},
}),
})
// 使用 GraphQL 中间件,配置高级选项
app.use(
'/graphql',
graphqlServer({
schema,
// 启用 GraphiQL 界面(开发环境)
graphiql: process.env.NODE_ENV !== 'production',
// 自定义上下文
context: (c) => {
// 可以添加认证信息、数据库连接等
return {
authHeader: c.req.header('Authorization'),
// db: getDbConnection(),
}
},
// 自定义错误处理
formatError: (error) => {
console.error('GraphQL Error:', error)
// 在生产环境中隐藏详细错误信息
if (process.env.NODE_ENV === 'production') {
return { message: '发生错误' }
}
return error
},
}),
)
app.listen(3000)与 TypeScript 集成
typescript
import { Hono } from 'hono'
import { graphqlServer } from '@hono/graphql-server'
import {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQLBoolean,
} from 'graphql'
// 定义类型
interface User {
id: number
name: string
email?: string
}
interface Post {
id: number
title: string
content?: string
published: boolean
authorId: number
}
interface Context {
authHeader?: string
userId?: number
}
const app = new Hono()
// 定义 GraphQL 类型
const PostType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
title: { type: GraphQLNonNull(GraphQLString) },
content: { type: GraphQLString },
published: { type: GraphQLBoolean },
author: {
type: UserType,
resolve: (post: Post) => {
return users.find((user) => user.id === post.authorId)
},
},
}),
})
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
name: { type: GraphQLNonNull(GraphQLString) },
email: { type: GraphQLString },
posts: {
type: GraphQLList(PostType),
resolve: (user: User) => {
return posts.filter((post) => post.authorId === user.id)
},
},
}),
})
// 模拟数据
const users: User[] = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
]
const posts: Post[] = [
{ id: 1, title: '第一篇文章', content: '这是内容...', published: true, authorId: 1 },
{ id: 2, title: '第二篇文章', content: '这是内容...', published: false, authorId: 1 },
{ id: 3, title: '第三篇文章', content: '这是内容...', published: true, authorId: 2 },
]
// 定义 GraphQL Schema
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQLNonNull(GraphQLInt) },
},
resolve: (_, args, context: Context) => {
// 可以使用上下文进行认证检查
if (!context.userId) {
throw new Error('未授权')
}
return users.find((user) => user.id === args.id)
},
},
// 其他查询字段...
},
}),
// 变更和订阅...
})
// 使用 GraphQL 中间件
app.use(
'/graphql',
graphqlServer<Context>({
schema,
graphiql: true,
context: (c) => {
// 解析认证令牌
const authHeader = c.req.header('Authorization')
let userId: number | undefined
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7)
// 在实际应用中,这里会验证令牌并提取用户ID
userId = token === 'valid-token' ? 1 : undefined
}
return { authHeader, userId }
},
}),
)
app.listen(3000)