Rust异步GraphQL服务器库async-graphql-poem的使用:基于Poem框架的高性能GraphQL接口开发

Rust异步GraphQL服务器库async-graphql-poem的使用:基于Poem框架的高性能GraphQL接口开发

安装

在项目目录中运行以下Cargo命令:

cargo add async-graphql-poem

或者在Cargo.toml中添加以下行:

async-graphql-poem = "7.0.17"

完整示例代码

以下是基于Poem框架开发高性能GraphQL接口的完整示例:

use async_graphql::{
    Object, EmptySubscription, Schema, 
    http::{GraphQLPlaygroundConfig, playground_source},
};
use async_graphql_poem::{GraphQL, GraphQLSubscription};
use poem::{
    get, handler, listener::TcpListener, 
    web::Html, IntoResponse, Route, Server,
};

// 定义查询结构体
struct Query;

// 为Query实现Object trait
#[Object]
impl Query {
    // 定义一个简单的hello字段
    async fn hello(&self) -> String {
        "Hello async-graphql-poem!".to_string()
    }
}

// 定义Mutation结构体
struct Mutation;

#[Object]
impl Mutation {
    // 定义一个简单的echo字段
    async fn echo(&self, message: String) -> String {
        message
    }
}

// 主函数
#[tokio::main]
async fn main() {
    // 创建Schema
    let schema = Schema::build(Query, Mutation, EmptySubscription).finish();
    
    // 创建路由
    let app = Route::new()
        // GraphQL端点
        .at("/graphql", post(GraphQL::new(schema.clone())))
        // GraphQL订阅端点
        .at("/ws", get(GraphQLSubscription::new(schema)))
        // GraphQL Playground界面
        .at("/", get(handler || async {
            Html(playground_source(
                GraphQLPlaygroundConfig::new("/graphql")
                    .subscription_endpoint("/ws"),
            ))
        }));

    // 启动服务器
    println!("Playground: http://localhost:8000");
    Server::new(TcpListener::bind("0.0.0.0:8000"))
        .run(app)
        .await
        .unwrap();
}

代码说明

  1. Schema构建:

    let schema = Schema::build(Query, Mutation, EmptySubscription).finish();
    

    创建了一个包含Query、Mutation和EmptySubscription的GraphQL Schema。

  2. 路由配置:

    • /graphql: 处理GraphQL查询和变更请求
    • /ws: 处理GraphQL订阅(WebSocket)
    • /: 提供GraphQL Playground界面
  3. Playground配置:

    GraphQLPlaygroundConfig::new("/graphql")
        .subscription_endpoint("/ws")
    

    配置了GraphQL Playground的查询和订阅端点。

  4. 服务器启动:

    Server::new(TcpListener::bind("0.0.0.0:8000"))
        .run(app)
        .await
        .unwrap();
    

    在8000端口启动Poem服务器。

功能特性

  1. 高性能异步GraphQL服务器
  2. 支持查询(Query)、变更(Mutation)和订阅(Subscription)
  3. 内置GraphQL Playground界面
  4. 基于Poem框架的轻量级实现
  5. 支持WebSocket协议用于实时订阅

这个示例展示了如何快速搭建一个功能完整的GraphQL服务器,包含了基本查询、变更功能,并支持订阅和Playground界面。


1 回复

Rust异步GraphQL服务器库async-graphql-poem的使用

简介

async-graphql-poem是结合了async-graphqlPoem框架的Rust库,用于构建高性能的GraphQL服务器。它提供了:

  1. 完全异步的GraphQL实现
  2. 基于Poem框架的高性能HTTP服务
  3. 类型安全的Schema定义
  4. 支持GraphQL订阅(WebSocket)
  5. 内置数据加载器(DataLoader)支持

完整示例代码

下面是一个完整的GraphQL服务器示例,包含查询、变更、订阅和DataLoader:

use async_graphql::{
    dataloader::DataLoader, Context, EmptyMutation, EmptySubscription, 
    Object, Schema, SimpleObject, Subscription, 
    http::{GraphQLPlaygroundConfig, playground_source},
    Loader, Result,
};
use async_graphql_poem::GraphQL;
use poem::{
    handler, listener::TcpListener, middleware::Cors, 
    web::Html, Endpoint, EndpointExt, Route, Server,
};
use std::{
    collections::HashMap,
    sync::Arc,
    time::Duration,
};
use tokio::time::interval;
use futures_util::stream::{Stream, StreamExt};

// 定义用户数据结构
#[derive(SimpleObject, Clone)]
struct User {
    id: i32,
    name: String,
    email: String,
}

// 定义消息数据结构
#[derive(SimpleObject)]
struct Message {
    content: String,
    sender: String,
}

// 查询结构体
struct Query;

#[Object]
impl Query {
    // 获取所有用户
    async fn users(&self) -> Vec<User> {
        vec![
            User {
                id: 1,
                name: "Alice".to_string(),
                email: "alice@example.com".to_string(),
            },
            User {
                id: 2,
                name: "Bob".to_string(),
                email: "bob@example.com".to_string(),
            },
        ]
    }

    // 根据ID获取用户
    async fn user(&self, id: i32) -> Option<User> {
        if id == 1 {
            Some(User {
                id: 1,
                name: "Alice".to_string(),
                email: "alice@example.com".to_string(),
            })
        } else {
            None
        }
    }
}

// 变更结构体
struct Mutation;

#[Object]
impl Mutation {
    // 创建用户
    async fn create_user(&self, name: String, email: String) -> User {
        User {
            id: 100,
            name,
            email,
        }
    }
}

// 用户数据加载器
struct UserLoader;

#[async_trait::async_trait]
impl Loader<i32> for UserLoader {
    type Value = User;
    type Error = Arc<String>;

    async fn load(&self, keys: &[i32]) -> Result<HashMap<i32, Self::Value>, Self::Error> {
        let mut users = HashMap::new();
        for &id in keys {
            users.insert(id, User {
                id,
                name: format!("User {}", id),
                email: format!("user{}@example.com", id),
            });
        }
        Ok(users)
    }
}

// 订阅结构体
struct Subscription;

#[Subscription]
impl Subscription {
    // 消息订阅
    async fn messages(&self) -> impl Stream<Item = Message> {
        let messages = vec![
            Message {
                content: "Hello".to_string(),
                sender: "Alice".to_string(),
            },
            Message {
                content: "Hi there".to_string(),
                sender: "Bob".to_string(),
            },
        ];
        
        let mut interval = interval(Duration::from_secs(1));
        let mut index = 0;
        
        futures_util::stream::unfold((interval, messages, index), move |(mut interval, messages, mut index)| async move {
            interval.tick().await;
            if index >= messages.len() {
                index = 0;
            }
            let message = messages[index].clone();
            index += 1;
            Some((message, (interval, messages, index)))
        })
    }
}

// GraphQL Playground页面
#[handler]
async fn graphql_playground() -> Html<String> {
    Html(playground_source(GraphQLPlaygroundConfig::new("/graphql")))
}

#[tokio::main]
async fn main() {
    // 创建Schema
    let schema = Schema::build(Query, Mutation, Subscription)
        .data(DataLoader::new(UserLoader, tokio::spawn))
        .finish();

    // 创建路由
    let app = Route::new()
        .at("/graphql", GraphQL::new(schema))
        .at("/", graphql_playground)
        .with(Cors::new());

    // 启动服务器
    println!("GraphQL Playground: http://localhost:8000");
    Server::new(TcpListener::bind("0.0.0.0:8000"))
        .run(app)
        .await
        .unwrap();
}

项目结构说明

  1. 依赖配置

    • 需要添加async-graphql, async-graphql-poem, poem和tokio依赖
    • 使用tokio作为异步运行时
  2. 核心组件

    • Query: 定义GraphQL查询操作
    • Mutation: 定义GraphQL变更操作
    • Subscription: 定义GraphQL订阅操作
    • UserLoader: 实现DataLoader用于批量加载用户数据
  3. 功能特点

    • 支持查询、变更和订阅三种GraphQL操作类型
    • 使用DataLoader解决N+1查询问题
    • 内置GraphQL Playground用于测试
    • 启用CORS支持跨域请求
  4. 启动方式

    • 服务启动后可以通过http://localhost:8000访问Playground界面
    • GraphQL端点位于/graphql路径
回到顶部