GraphQL API 与 Rust:实践逐步教程
GraphQL 是一种强大的 API 查询语言,允许客户端只请求他们需要的数据。结合 Rust 的性能和安全性,您将获得高负载 Web 服务的理想解决方案。在本教程中,我们将使用 async-graphql crate 从头开始创建一个完整的 GraphQL API。
为什么选择 Rust?它提供零成本抽象、内存安全性和惊人的速度。Rust 上的 GraphQL 比 JavaScript 或 Python 上的同类产品运行得更快,这对于生产系统至关重要。
1. 项目设置和依赖项
让我们从创建一个新的 Rust 项目开始。打开终端并执行:
cargo new graphql_rust_apicd graphql_rust_api现在将必要的依赖项添加到 Cargo.toml 文件中:
[dependencies]async-graphql = "7.0"async-graphql-actix-web = "7.0"actix-web = "4"actix-cors = "0.7"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }这些 crate 提供了所需的一切:async-graphql — GraphQL 核心,actix-web — Web 服务器,serde — 数据序列化。
2. 创建数据模型和模式
让我们为我们的 API 定义一个简单的模型 — 书籍。创建结构和 GraphQL 模式:
use async_graphql::*;use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Serialize, Deserialize, SimpleObject)]struct Book { id: ID, title: String, author: String, year: i32,}
#[derive(Clone, Debug, Serialize, Deserialize, InputObject)]struct BookInput { title: String, author: String, year: i32,}注意属性 #[derive(SimpleObject)] 和 #[derive(InputObject)]。第一个自动生成用于输出的 GraphQL 类型,第二个用于数据输入。
3. 实现解析器(Query 和 Mutation)
解析器是处理请求的函数。让我们创建一个存储结构和解析器:
use std::sync::Mutex;
struct QueryRoot;
#[Object]impl QueryRoot { async fn books(&self, ctx: &Context<'_>) -> Vec<Book> { let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>(); storage.lock().unwrap().clone() }
async fn book(&self, ctx: &Context<'_>, id: ID) -> Option<Book> { let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>(); storage.lock().unwrap().iter().find(|b| b.id == id).cloned() }}
struct MutationRoot;
#[Object]impl MutationRoot { async fn add_book(&self, ctx: &Context<'_>, input: BookInput) -> Book { let mut storage = ctx.data_unchecked::<Mutex<Vec<Book>>>() .lock().unwrap(); let book = Book { id: ID::from(storage.len() + 1), title: input.title, author: input.author, year: input.year, }; storage.push(book.clone()); book }}我们使用 Mutex 来实现线程安全的数据访问。在实际项目中,您会连接数据库,但对于本教程来说,这是一个理想的选择。
4. 构建和启动服务器
最后一步 — 将所有内容整合到一个可运行的服务器中。让我们创建入口点:
use actix_web::{guard, web, App, HttpServer, HttpRequest, HttpResponse};use actix_cors::Cors;use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
async fn index( schema: web::Data<Schema<QueryRoot, MutationRoot, EmptySubscription>>, req: GraphQLRequest,) -> GraphQLResponse { schema.execute(req.into_inner()).await.into()}
#[actix_web::main]async fn main() -> std::