Rust的GraphQL编译器库apollo-compiler的使用:支持高效解析、验证和操作GraphQL Schema与查询
Rust的GraphQL编译器库apollo-compiler的使用:支持高效解析、验证和操作GraphQL Schema与查询
apollo-compiler
是一个用于GraphQL查询语言的基于查询的编译器库。
特性
- 提供相对低级的GraphQL语法AST,以及高级的
Schema
和ExecutableDocument
表示 - 所有三种表示都可以解析(内部使用
apollo-parser
)、程序化创建或修改,并序列化 - 根据GraphQL规范验证Schema和可执行文档
- 执行查询的模式自省部分
快速开始
添加依赖开始使用apollo-compiler
:
cargo add apollo-compiler
或者手动添加到Cargo.toml
:
[dependencies]
apollo-compiler = "1.28.0"
使用示例
基础使用示例:
use apollo_compiler::Schema;
use apollo_compiler::ExecutableDocument;
let sdl = r#"
type Query {
field: Int
}
"#;
let query = "{ field }";
/// 如果验证失败,panic消息会很好地格式化指向源文件的相关部分
let schema = Schema::parse_and_validate(sdl, "sdl.graphql").unwrap();
let doc = ExecutableDocument::parse_and_validate(&schema, query, "query.graphql").unwrap();
完整示例
访问片段定义字段类型
use apollo_compiler::{Schema, ExecutableDocument, Node, executable};
let schema_input = r#"
type User {
id: ID
name: String
profilePic(size: Int): URL
}
schema { query: User }
scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
"#;
let query_input = r#"
query getUser {
... vipCustomer
}
#fragment definition where we want to know the field types.
fragment vipCustomer on User {
id
name
profilePic(size: 50)
}
"#;
let schema = Schema::parse_and_validate(schema_input, "schema.graphql").unwrap();
let document = ExecutableDocument::parse_and_validate(&schema, query_input, "query.graphql")
.unwrap();
let op = document.operations.get(Some("getUser")).expect("getUser query does not exist");
let fragment_in_op = op.selection_set.selections.iter().filter_map(|sel| match sel {
executable::Selection::FragmentSpread(spread) => {
Some(document.fragments.get(&spread.fragment_name)?.as_ref())
}
_ => None
}).collect::<Vec<&executable::Fragment>>();
let fragment_fields = fragment_in_op.iter().flat_map(|frag| {
frag.selection_set.fields()
}).collect::<Vec<&Node<executable::Field>>>();
let field_ty = fragment_fields
.iter()
.map(|f| f.ty().inner_named_type().as_str())
.collect::<Vec<&str>>();
assert_eq!(field_ty, ["ID", "String", "URL"]);
获取查询操作定义中使用的字段上定义的指令
use apollo_compiler::{Schema, ExecutableDocument, Node, executable};
let schema_input = r#"
type Query {
topProducts: Product
}
type Product {
inStock: Boolean @join__field(graph: INVENTORY)
name: String @join__field(graph: PRODUCTS)
}
enum join__Graph {
INVENTORY,
PRODUCTS,
}
directive @join__field(graph: join__Graph) on FIELD_DEFINITION
"#;
let query_input = r#"
query getProduct {
topProducts {
inStock
}
}
"#;
let schema = Schema::parse_and_validate(schema_input, "schema.graphql").unwrap();
let document = ExecutableDocument::parse_and_validate(&schema, query_input, "query.graphql")
.unwrap();
let get_product_op = document
.operations
.get(Some("getProduct"))
.expect("getProduct query does not exist");
let in_stock_field = &get_product_op
.selection_set
.fields()
.find(|f| f.name == "topProducts")
.expect("topProducts field does not exist")
.selection_set
.fields()
.find(|f| f.name == "inStock")
.expect("inStock field does not exist")
.definition;
let in_stock_directive: Vec<_> = in_stock_field
.directives
.iter()
.map(|dir| &dir.name)
.collect();
assert_eq!(in_stock_directive, ["join__field"]);
打印有错误的GraphQL文档的诊断信息
use apollo_compiler::parser::Parser;
let input = "{ ... }";
if let Err(diagnostics) = Parser::new().parse_mixed_validate(input, "document.graphql") {
println!("{diagnostics}")
}
许可证
根据以下任一许可证授权:
- Apache License, Version 2.0
- MIT license
1 回复
Rust的GraphQL编译器库apollo-compiler使用指南
apollo-compiler
是Apollo推出的一个Rust库,用于高效解析、验证和操作GraphQL schema与查询。它提供了强大的工具来处理GraphQL文档,包括类型系统定义和查询验证。
主要特性
- 解析GraphQL schema和查询文档
- 验证GraphQL文档的正确性
- 提供schema构建API
- 支持schema和查询的转换操作
- 高效的错误报告
安装
在Cargo.toml中添加依赖:
[dependencies]
apollo-compiler = "0.11"
基本使用方法
1. 解析GraphQL Schema
use apollo_compiler::Schema;
let input = r#"
type Query {
hello: String
user(id: ID!): User
}
type User {
id: ID!
name: String
}
"#;
let schema = Schema::parse(input, "schema.graphql").unwrap();
2. 解析GraphQL查询
use apollo_compiler::ExecutableDocument;
let query = r#"
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
"#;
let doc = ExecutableDocument::parse(query, "query.graphql").unwrap();
3. 验证查询与Schema的兼容性
use apollo_compiler::validation;
let schema = Schema::parse(/* schema内容 */, "schema.graphql").unwrap();
let doc = ExecutableDocument::parse(/* 查询内容 */, "query.graphql").unwrap();
let diagnostics = validation::validate(&schema, &doc);
if diagnostics.is_empty() {
println!("查询有效!");
} else {
for diagnostic in diagnostics {
println!("{}", diagnostic);
}
}
4. 以编程方式构建Schema
use apollo_compiler::{Schema, ast};
use apollo_compiler::Node;
let mut schema = Schema::new();
schema.schema_definition.make_schema_definition();
// 添加Query类型
let query_type = schema.types.entry("Query".into()).or_default();
query_type.make_object_type_definition();
// 添加hello字段
let hello_field = ast::FieldDefinition {
name: Node::new("hello".into()),
ty: Node::new(ast::Type::Named("String".into())),
arguments: Vec::new(),
directives: Vec::new(),
description: None,
};
query_type
.as_object_mut()
.unwrap()
.fields
.insert("hello".into(), Node::new(hello_field));
5. 转换和操作Schema
use apollo_compiler::Schema;
let schema = Schema::parse(/* schema内容 */, "schema.graphql").unwrap();
// 获取所有类型名
let type_names: Vec<_> = schema.types.keys().collect();
println!("Types: {:?}", type_names);
// 获取特定类型的字段
if let Some(user_type) = schema.types.get("User") {
if let Some(fields) = user_type.as_object().map(|obj| &obj.fields) {
for (field_name, _) in fields {
println!("User field: {}", field_name);
}
}
}
高级用法
自定义验证规则
use apollo_compiler::{Schema, ExecutableDocument};
use apollo_compiler::validation::{ValidatorContext, visit, Visitor};
struct MyCustomRule;
impl Visitor for MyCustomRule {
fn enter_field(&mut self, ctx: &mut ValidatorContext<'_>, field: &ast::Field) {
if field.name.as_str() == "secretField" {
ctx.report_error("不允许查询secretField", field.span());
}
}
}
let schema = Schema::parse(/* schema内容 */, "schema.graphql").unwrap();
let doc = ExecutableDocument::parse(/* 查询内容 */, "query.graphql").unwrap();
let mut ctx = ValidatorContext::new(&schema, &doc);
let mut visitor = MyCustomRule;
visit(&mut visitor, &mut ctx);
生成GraphQL文档字符串
use apollo_compiler::Schema;
let schema = Schema::parse(/* schema内容 */, "schema.graphql").unwrap();
// 将schema转换回GraphQL字符串
let schema_str = schema.to_string();
println!("{}", schema_str);
错误处理
apollo-compiler
提供了详细的错误信息:
let result = Schema::parse("type Query { hello: String", "broken.graphql");
match result {
Ok(schema) => println!("Schema解析成功"),
Err(errors) => {
for error in errors {
println!(
"错误在 {}:{}:{} - {}",
error.location().file_name(),
error.location().line_number(),
error.location().column_number(),
error.message()
);
}
}
}
性能提示
- 对于大型schema,考虑重用
Schema
实例 - 使用
Arc
共享schema以减少克隆 - 批量处理验证以减少重复工作
apollo-compiler
是处理GraphQL的强大工具,特别适合需要高性能和精确控制的Rust应用程序。
完整示例代码
use apollo_compiler::{Schema, ExecutableDocument, validation};
use apollo_compiler::ast::{self, FieldDefinition};
use apollo_compiler::Node;
use std::sync::Arc;
fn main() {
// 示例1: 解析Schema
let schema_input = r#"
type Query {
books: [Book!]!
authors: [Author!]!
}
type Book {
id: ID!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
books: [Book!]!
}
"#;
let schema = Schema::parse(schema_input, "library.graphql").unwrap();
// 示例2: 解析查询
let query_input = r#"
query GetBooks {
books {
title
author {
name
}
}
}
"#;
let document = ExecutableDocument::parse(query_input, "query.graphql").unwrap();
// 示例3: 验证查询
let diagnostics = validation::validate(&schema, &document);
if !diagnostics.is_empty() {
for diag in diagnostics {
println!("验证错误: {}", diag);
}
return;
}
// 示例4: 编程方式构建Schema
let mut new_schema = Schema::new();
new_schema.schema_definition.make_schema_definition();
// 添加Product类型
let product_type = new_schema.types.entry("Product".into()).or_default();
product_type.make_object_type_definition();
// 添加Product字段
let id_field = FieldDefinition {
name: Node::new("id".into()),
ty: Node::new(ast::Type::Named("ID!".into())),
arguments: Vec::new(),
directives: Vec::new(),
description: None,
};
let name_field = FieldDefinition {
name: Node::new("name".into()),
ty: Node::new(ast::Type::Named("String!".into())),
arguments: Vec::new(),
directives: Vec::new(),
description: None,
};
product_type
.as_object_mut()
.unwrap()
.fields
.insert("id".into(), Node::new(id_field));
product_type
.as_object_mut()
.unwrap()
.fields
.insert("name".into(), Node::new(name_field));
// 打印生成的Schema
println!("生成的Schema:\n{}", new_schema.to_string());
// 示例5: 使用Arc共享Schema
let shared_schema = Arc::new(schema);
// 在多线程环境中使用共享Schema
let schema_clone = shared_schema.clone();
std::thread::spawn(move || {
println!("在子线程中访问类型数量: {}", schema_clone.types.len());
}).join().unwrap();
}