Skip to content
On this page

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)

要保持清醒 永远不抱有意外的幻想 凭空的期待最要命