Seaography 2.0:强大且可扩展的 GraphQL 框架 🧭

发布: (2025年12月5日 GMT+8 18:46)
4 min read
原文: Dev.to

Source: Dev.to

什么是 Seaography

Seaography 是一个基于 SeaORM 构建的 GraphQL 框架,能够自动生成带有 DataLoader 集成的解析器,以解决 N+1 问题。它提供了丰富的自定义选项、轻松添加自定义端点以及细粒度的授权(RBAC、hooks/guards)。

一个简短的演示展示了如何:

  • 从已有数据库生成 SeaORM 实体(例如 Sakila SQLite 示例)
  • 启动一个 GraphQL 服务器(兼容 Axum、Actix、Poem)
  • 通过 GraphQL Playground 运行查询

您可以在 quick‑start guide 中遵循相同的步骤。


支持哪些类型的查询?

过滤、排序和分页

{
  film(
    filters: {
      title: "{ contains: \"sea\" } # ⬅ like '%sea%'"
      and: [{ releaseYear: { gt: "2000" } }, { length: { gt: 120 } }]
    }
    orderBy: { filmId: ASC }
    pagination: { page: { page: 0, limit: 10 } }
    # cursor‑based pagination is also supported:
    # pagination: { cursor: { limit: 10, cursor: "Int[3]:100" } }
  ) {
    nodes {
      filmId
      title
      description
    }
    paginationInfo {
      pages
      current
    }
  }
}

嵌套关联查询

{
  film(
    # filter by related entity
    having: {
      actor: { firstName: { eq: "David" } }
      category: { name: { eq: "Documentary" } }
    }
  ) {
    nodes {
      filmId
      title
      actor {
        nodes {
          firstName
          lastName
        }
      }
      inventory {
        nodes {
          store {
            address {
              address
              city {
                city
              }
            }
          }
        }
      }
    }
  }
}

使用的连接路径

film -> film_actor -> actor
     -> inventory -> store -> address -> city

DataLoader 高效地解析这些关联,消除 N+1 问题。

变更:创建、更新、删除

完整的 CRUD 已支持(CreateOneCreateBatchUpdateDelete)。

mutation {
  filmTextCreateBatch(
    data: [
      { filmId: 1, title: "\"Foo bar\"", description: "\"Lorem ipsum dolor sit amet\"" }
      { filmId: 2, title: "\"Fizz buzz\"", description: "\"Consectetur adipiscing elit\"" }
    ]
  ) {
    filmId
    title
    description
  }
}

自定义查询

Seaography 2.0 引入了宏,使您能够编写自定义查询端点,同时复用框架的分页和连接工具。

带分页的自定义查询

假设有一个从 Sakila 模式生成的 Customer 实体:

//! Entity from the sakila schema, generated by sea-orm-cli
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "customer")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub customer_id: i32,
    pub store_id: i32,
    pub first_name: String,
    pub last_name: String,
    // …
}

创建一个自定义端点,返回当前请求所属店铺的客户:

use seaography::{apply_pagination, Connection, CustomFields, PaginationInput};

pub struct Operations;

#[CustomFields]
impl Operations {
    async fn customer_of_current_store(
        ctx: &Context,
        pagination: PaginationInput,
    ) -> async_graphql::Result> {
        let db = ctx.data::()?;
        let session = ctx.data::()?;

        let query = customer::Entity::find()
            .filter(customer::Column::StoreId.eq(session.store_id));

        // Seaography handles pagination and execution
        let connection = apply_pagination(&CONTEXT, db, query, pagination).await?;
        Ok(connection)
    }
}

该端点暴露为:

customer_of_current_store(
  pagination: PaginationInput
): CustomerConnection!

示例查询

{
  customer_of_current_store(pagination: { page: { page: 0, limit: 10 } }) {
    nodes {
      storeId
      customerId
      firstName
      lastName
      email
    }
    paginationInfo {
      pages
      current
    }
  }
}

只需几行代码,您即可添加一个完整功能、支持分页的 API 端点;繁重的工作由 Seaography + SeaORM 完成。


它是如何工作的?

  • 桥接 – Seaography 将 SeaORM 类型映射到 Async‑GraphQL,使任何 SeaORM 实体都可以作为 GraphQL 输出。
  • 模式生成 – 在应用启动时,Seaography 将 SeaORM 实体的元信息即时转换为 GraphQL 模式。
  • 请求生命周期
    1. Async‑GraphQL 解析传入的 HTTP 请求并构建请求上下文。
    2. 您的 HTTP 处理器拦截请求,将上下文(例如数据库连接、会话)提供给 Seaography。
    3. 解析器(生成的或自定义的)通过 SeaORM 执行查询,可选地使用 DataLoader 高效加载关联。
    4. 在返回最终响应之前,自动应用分页、过滤、排序和授权钩子。
Back to Blog

相关文章

阅读更多 »

我在 JavaScript 的第一步:简要解析

JavaScript 中的变量 **let** 用于可以在以后更改的值。 ```javascript let age = 20; age = 21; ``` **const** 用于不应被更改的值。 ```javascript const PI = 3.14159; ```