Seaography 2.0: A Powerful and Extensible GraphQL Framework 🧭

Published: (December 5, 2025 at 05:46 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

What is Seaography

Seaography is a GraphQL framework built on top of SeaORM that automatically generates resolvers with DataLoader integration to solve the N+1 problem. It offers extensive customization options, easy addition of custom endpoints, and fine‑grained authorization (RBAC, hooks/guards).

A short demo shows how to:

  • Generate SeaORM entities from an existing database (e.g., the Sakila SQLite demo)
  • Spin up a GraphQL server (compatible with Axum, Actix, Poem)
  • Run queries via GraphQL Playground

You can follow the same steps in the quick‑start guide.


What kinds of queries are supported?

Filter, Ordering and Pagination

{
  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
    }
  }
}

Nested Relational Query

{
  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
              }
            }
          }
        }
      }
    }
  }
}

Join paths used

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

A DataLoader resolves these relations efficiently, eliminating the N+1 problem.

Mutations: create, update, delete

Full CRUD is supported (CreateOne, CreateBatch, Update, Delete).

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
  }
}

Custom Query

Seaography 2.0 introduces macros that let you write custom query endpoints while reusing the framework’s pagination and connection utilities.

Custom Query with pagination

Assume a Customer entity generated from the Sakila schema:

//! 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,
    // …
}

Create a custom endpoint that returns customers belonging to the store of the current request:

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)
    }
}

The endpoint is exposed as:

customer_of_current_store(
  pagination: PaginationInput
): CustomerConnection!

Example query

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

With just a few lines of code you add a fully functional, paginated API endpoint; the heavy lifting is done by Seaography + SeaORM.


How does it work?

  • Bridge – Seaography maps SeaORM types to Async‑GraphQL, allowing any SeaORM entity to be used as GraphQL output.
  • Schema generation – On application startup, Seaography transforms the meta‑information of SeaORM entities into a GraphQL schema on‑the‑fly.
  • Request lifecycle
    1. Async‑GraphQL parses the incoming HTTP request and builds a request context.
    2. Your HTTP handler intercepts the request, providing the context (e.g., database connection, session) to Seaography.
    3. Resolvers (generated or custom) execute queries via SeaORM, optionally using DataLoaders for efficient relation loading.
    4. Pagination, filtering, ordering, and authorization hooks are applied automatically before the final response is returned.
Back to Blog

Related posts

Read more »