26 个不稳定版本 (5 个重大变更)
0.5.2 | 2020 年 2 月 19 日 |
---|---|
0.5.1 | 2019 年 11 月 14 日 |
0.5.0 | 2019 年 10 月 23 日 |
0.3.0 | 2019 年 6 月 18 日 |
0.1.1 | 2018 年 12 月 21 日 |
#14 in #juniper
每月 166 次下载
在 2 crates 中使用
63KB
373 行
juniper-from-schema
该库包含一个过程宏,可以读取 GraphQL 模式文件,并生成相应的 Juniper 宏调用。这意味着您可以拥有一个实际的模式文件,并确保它与您的 Rust 实现匹配。它还消除了使用 Juniper 所涉及的大部分样板代码。
请参阅 crates 文档 以获取使用示例和更多信息。
lib.rs
:
该库包含一个过程宏,可以读取 GraphQL 模式文件,并生成相应的 Juniper 宏调用。这意味着您可以拥有一个实际的模式文件,并确保它与您的 Rust 实现匹配。它还消除了使用 Juniper 所涉及的大部分样板代码。
目录
示例
模式
schema {
query: Query
mutation: Mutation
}
type Query {
// The directive makes the return value `FieldResult<String>`
// rather than the default `FieldResult<&String>`
helloWorld(name: String!): String! @juniper(ownership: "owned")
}
type Mutation {
noop: Boolean!
}
如何实现该模式
#[macro_use]
extern crate juniper;
use juniper_from_schema::graphql_schema_from_file;
// This is the important line
graphql_schema_from_file!("tests/schemas/doc_schema.graphql");
pub struct Context;
impl juniper::Context for Context {}
pub struct Query;
impl QueryFields for Query {
fn field_hello_world(
&self,
executor: &juniper::Executor<'_, Context>,
name: String,
) -> juniper::FieldResult<String> {
Ok(format!("Hello, {}!", name))
}
}
pub struct Mutation;
impl MutationFields for Mutation {
fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool> {
Ok(&true)
}
}
fn main() {
let ctx = Context;
let query = "query { helloWorld(name: \"Ferris\") }";
let (result, errors) = juniper::execute(
query,
None,
&Schema::new(Query, Mutation),
&juniper::Variables::new(),
&ctx,
)
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
result
.as_object_value()
.unwrap()
.get_field_value("helloWorld")
.unwrap()
.as_scalar_value::<String>()
.unwrap(),
"Hello, Ferris!",
);
}
使用 graphql_schema_from_file!
展开后,您的代码将类似于以下内容
#[macro_use]
extern crate juniper;
pub struct Context;
impl juniper::Context for Context {}
pub struct Query;
juniper::graphql_object!(Query: Context |&self| {
field hello_world(&executor, name: String) -> juniper::FieldResult<String> {
<Self as QueryFields>::field_hello_world(&self, &executor, name)
}
});
trait QueryFields {
fn field_hello_world(
&self,
executor: &juniper::Executor<'_, Context>,
name: String,
) -> juniper::FieldResult<String>;
}
impl QueryFields for Query {
fn field_hello_world(
&self,
executor: &juniper::Executor<'_, Context>,
name: String,
) -> juniper::FieldResult<String> {
Ok(format!("Hello, {}!", name))
}
}
pub struct Mutation;
juniper::graphql_object!(Mutation: Context |&self| {
field noop(&executor) -> juniper::FieldResult<&bool> {
<Self as MutationFields>::field_noop(&self, &executor)
}
});
trait MutationFields {
fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool>;
}
impl MutationFields for Mutation {
fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool> {
Ok(&true)
}
}
type Schema = juniper::RootNode<'static, Query, Mutation>;
fn main() {
let ctx = Context;
let query = "query { helloWorld(name: \"Ferris\") }";
let (result, errors) = juniper::execute(
query,
None,
&Schema::new(Query, Mutation),
&juniper::Variables::new(),
&ctx,
)
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
result
.as_object_value()
.unwrap()
.get_field_value("helloWorld")
.unwrap()
.as_scalar_value::<String>()
.unwrap(),
"Hello, Ferris!",
);
}
示例网页应用
您可以在 https://github.com/davidpdrsn/graphql-app-example 中找到如何将此库与 Rocket 和 Diesel 一起用于构建 GraphQL 网页应用的示例,或者可以在 https://github.com/husseinraoouf/graphql-actix-example 中找到如何将此库与 Actix 和 Diesel 一起使用的示例。
GraphQL 功能
此库的目标是支持尽可能多的 GraphQL 功能,与 Juniper 相同。
以下是完整的功能列表
支持
- 对象类型,包括将列表和非空转换为 Rust 类型
- 包括
ID
类型的自定义标量类型 - 接口
- 联合
- 输入对象
- 枚举类型
尚不支持
- 订阅(一旦 Juniper 支持订阅,将支持订阅)
- 类型扩展
ID
类型
GraphQL 类型 ID
将生成到 juniper::ID
。
自定义标量类型
自定义标量类型将生成到围绕 String
的新类型包装器中。例如
scalar Cursor
将导致
pub struct Cursor(pub String);
特殊情况的标量
一些标量名称具有特殊含义。它们是
Url
变为url::Url
。Uuid
变为uuid::Uuid
。Date
变为chrono::naive::NaiveDate
。DateTimeUtc
默认变为chrono::DateTime<chrono::offset::Utc>
,但如果使用scalar DateTimeUtc @juniper(with_time_zone: false)
定义,它将变为chrono::naive::NaiveDateTime
。
Juniper 不支持 chrono::Date
,因此此库也无法支持。您可以在此处了解 Juniper 的支持集成这里。
接口
Juniper 有几种方式在 Rust 中表示 GraphQL 接口。它们列在这里,包括它们的优缺点。
对于生成的代码,我们使用 enum
模式,因为我们发现它是最灵活的。
简略示例(完整示例请见此处)
#
graphql_schema! {
schema {
query: Query
}
type Query {
search(query: String!): [SearchResult!]! @juniper(ownership: "owned")
}
interface SearchResult {
id: ID!
text: String!
}
type Article implements SearchResult {
id: ID!
text: String!
}
type Tweet implements SearchResult {
id: ID!
text: String!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_search(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
query: String,
) -> FieldResult<Vec<SearchResult>> {
let article: Article = Article { id: ID::new("1"), text: "Business".to_string() };
let tweet: Tweet = Tweet { id: ID::new("2"), text: "1 weird tip".to_string() };
let posts = vec![
SearchResult::from(article),
SearchResult::from(tweet),
];
Ok(posts)
}
}
生成的枚举将为实现接口并实现每个类型的 From<T>
的每种类型提供变体。
联合类型
联合类型基本上就是接口,所以它们的工作方式非常相似。
简略示例(完整示例请见此处)
#
graphql_schema! {
schema {
query: Query
}
type Query {
search(query: String!): [SearchResult!]! @juniper(ownership: "owned")
}
union SearchResult = Article | Tweet
type Article {
id: ID!
text: String!
}
type Tweet {
id: ID!
text: String!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_search(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
query: String,
) -> FieldResult<Vec<SearchResult>> {
let article: Article = Article { id: ID::new("1"), text: "Business".to_string() };
let tweet: Tweet = Tweet { id: ID::new("2"), text: "1 weird tip".to_string() };
let posts = vec![
SearchResult::from(article),
SearchResult::from(tweet),
];
Ok(posts)
}
}
输入对象
输入对象将被转换为具有公共字段的 Rust 结构体。
简略示例(完整示例请见此处)
graphql_schema! {
schema {
query: Query
mutation: Mutation
}
type Mutation {
createPost(input: CreatePost!): Post @juniper(ownership: "owned")
}
input CreatePost {
title: String!
}
type Post {
id: ID!
title: String!
}
type Query { noop: Boolean! }
}
pub struct Mutation;
impl MutationFields for Mutation {
fn field_create_post(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
input: CreatePost,
) -> FieldResult<Option<Post>> {
let title: String = input.title;
unimplemented!()
}
}
从该示例中,CreatePost
将定义为
pub struct CreatePost {
pub title: String,
}
枚举类型
GraphQL 枚举类型将被转换为正常的 Rust 枚举。每个变体的名称将是驼峰式。
简略示例(完整示例请见此处)
#
graphql_schema! {
schema {
query: Query
}
enum Status {
PUBLISHED
UNPUBLISHED
}
type Query {
allPosts(status: Status!): [Post!]! @juniper(ownership: "owned")
}
type Post {
id: ID!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_all_posts(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
status: Status,
) -> FieldResult<Vec<Post>> {
match status {
Status::Published => unimplemented!("find published posts"),
Status::Unpublished => unimplemented!("find unpublished posts"),
}
}
}
默认参数值
在 GraphQL 中,如果该参数是可空的,则可以提供字段参数的默认值。
以下类型的参数支持默认值
Float
Int
String
布尔值
- 枚举
- 输入对象(作为字段参数,见下文)
- 包含其他支持类型的列表
简化的示例(完整示例请见此处)
#
graphql_schema! {
schema {
query: Query
}
enum Status {
PUBLISHED
UNPUBLISHED
}
input Pagination {
pageSize: Int!
cursor: ID
}
type Query {
allPosts(
status: Status = PUBLISHED,
pagination: Pagination = { pageSize: 20 }
): [Post!]! @juniper(ownership: "owned")
}
type Post {
id: ID!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_all_posts(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
status: Status,
pagination: Pagination,
) -> FieldResult<Vec<Post>> {
// `status` will be `Status::Published` if not given in the query
match status {
Status::Published => unimplemented!("find published posts"),
Status::Unpublished => unimplemented!("find unpublished posts"),
}
}
}
输入对象的问题点
输入对象的默认值仅支持作为字段参数。以下不支持
input SomeType {
field: Int = 1
}
这不被支持,因为规范对处理多个嵌套默认值的方式不够明确。
此外,默认值只有在没有传递参数时才会使用。所以给定以下模式
input Input {
a: String
b: String
}
type Query {
field(arg: Input = { a: "a" }): Int!
}
和查询
query MyQuery {
field(arg: { b: "my b" })
}
解析器内 arg
的值将是 Input { a: None, b: Some(("my b")) }
。请注意,尽管字段中 a
有默认值,但在这里没有使用,因为我们已经在查询中设置了 arg
。
支持的模式指令
支持一些模式指令,允许您自定义生成的代码
@juniper(ownership: "owned|borrowed|as_ref")
。用于自定义返回数据的所有权。更多信息请见此处。@juniper(infallible: true|false)
。自定义字段是否应该返回Result<T, _>
或只是T
。更多信息请见此处。@deprecated
。用于在您的模式中弃用类型。还支持使用@deprecated(reason: "...")
提供原因
@juniper
的定义
一些在您的 GraphQL 模式中运行的工具要求您包含所有使用的指令的定义。因此,如果您需要,@juniper
的定义如下
directive @juniper(
ownership: String = "borrowed",
infallible: Boolean = false,
with_time_zone: Boolean = true
) on FIELD_DEFINITION
此指令定义允许在您的模式中使用,以及任何其他指令定义。但不同于此定义的 @juniper
定义是不允许的。
该定义可能会在未来版本中更改。请参阅变更日志。
juniper-from-schema 不需要将此放在您的模式中,因此您只需要在需要时包含它。
自定义所有权
默认情况下,所有字段都返回借用值。具体来说,类型是 juniper::FieldResult<&'a T>
,其中 'a
是 self
的生命周期。这对于返回由 self
拥有的数据非常有效,并且避免了如果字段返回拥有值时必须调用的不必要的 .clone()
调用。
但是,如果您需要更改所有权,您必须将指令 @juniper(ownership:)
添加到模式中的字段。
它接受以下参数
@juniper(ownership: "borrowed")
:返回的数据将是从self
借用的(FieldResult<&T>
)。@juniper(ownership: "owned")
:返回类型将是拥有的(FieldResult<T>
)。@juniper(ownership: "as_ref")
:仅适用于Option
和Vec
返回类型。将内部类型更改为借用(FieldResult<Option<&T>>
或FieldResult<Vec<&T>>
)。
示例
graphql_schema! {
schema {
query: Query
}
type Query {
borrowed: String!
owned: String! @juniper(ownership: "owned")
asRef: String @juniper(ownership: "as_ref")
}
}
pub struct Query;
impl QueryFields for Query {
fn field_borrowed(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
// ...
# unimplemented!()
}
fn field_owned(&self, _: &Executor<'_, Context>) -> FieldResult<String> {
// ...
# unimplemented!()
}
fn field_as_ref(&self, _: &Executor<'_, Context>) -> FieldResult<Option<&String>> {
// ...
# unimplemented!()
}
}
所有字段参数都将拥有所有权。
不可失败字段
默认情况下,生成的解析器是可失败的,这意味着它们返回一个 Result<T, _>
而不是裸的 T
。您可以使用 @juniper(infallible: true)
来自定义它。
示例
graphql_schema! {
schema {
query: Query
}
type Query {
canError: String!
cannotError: String! @juniper(infallible: true)
cannotErrorAndOwned: String! @juniper(infallible: true, ownership: "owned")
}
}
pub struct Query;
impl QueryFields for Query {
fn field_can_error(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
// ...
# unimplemented!()
}
fn field_cannot_error(&self, _: &Executor<'_, Context>) -> &String {
// ...
# unimplemented!()
}
fn field_cannot_error_and_owned(&self, _: &Executor<'_, Context>) -> String {
// ...
# unimplemented!()
}
}
GraphQL 到 Rust 类型的映射
这是标准 GraphQL 类型将如何映射到 Rust
Int
->i32
Float
->f64
String
->String
Boolean
->bool
ID
->juniper::ID
查询轨迹
如果您在深度嵌套查询中对预加载关联项不够小心,则可能会遇到大量的N+1查询错误。Juniper提供了一个前瞻API,允许您检查查询中更远处的项目。但是,该API基于字符串,因此您可能会犯拼写错误,并检查不存在的字段。
QueryTrail
是Juniper前瞻的一个轻量级包装,为您的所有类型上的每个字段生成方法。这意味着编译器会拒绝您的代码,如果您检查了无效的字段。
返回对象类型(非标量值)的解析方法(field_*
)将除了执行器之外,还会得到一个QueryTrail
参数。
由于QueryTrail
类型本身是在这个crate中定义的(而不是插入到您的代码中),因此我们无法直接为您的GraphQL字段添加方法。这些方法必须通过“扩展特性”添加。所以如果您看到像这样的错误
| trail.foo();
| ^^^ method not found in `&juniper_from_schema::QueryTrail<'a, User, juniper_from_schema::Walked>`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
2 | use crate::graphql_schema::query_trails::QueryTrailUserExtensions;
|
那么将use crate::graphql_schema::query_trails::*
添加到您的模块中应该可以修复它。这是必要的,因为所有扩展特性都是在名为query_trails
的模块内生成的。这样做是为了您可以在不全局导入您的GraphQL模式中的所有内容的情况下,全局导入QueryTrail
扩展特性。
如果您只想从模式中获取所有内容,use crate::graphql_schema::*
也会导入扩展特性。
简略示例
完整示例请见这里
#
graphql_schema! {
schema {
query: Query
}
type Query {
allPosts: [Post!]! @juniper(ownership: "owned")
}
type Post {
id: Int!
author: User!
}
type User {
id: Int!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_all_posts(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
) -> FieldResult<Vec<Post>> {
// Check if the query includes the author
if let Some(_) = trail.author().walk() {
// Somehow preload the users to avoid N+1 query bugs
// Exactly how to do this depends on your setup
}
// Normally this would come from the database
let post = Post {
id: 1,
author: User { id: 1 },
};
Ok(vec![post])
}
}
pub struct Post {
id: i32,
author: User,
}
impl PostFields for Post {
fn field_id(&self, executor: &Executor<'_, Context>) -> FieldResult<&i32> {
Ok(&self.id)
}
fn field_author(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, User, juniper_from_schema::Walked>,
) -> FieldResult<&User> {
Ok(&self.author)
}
}
pub struct User {
id: i32,
}
impl UserFields for User {
fn field_id(
&self,
executor: &Executor<'_, Context>,
) -> FieldResult<&i32> {
Ok(&self.id)
}
}
类型
查询轨迹有两个泛型参数:QueryTrail<'a, T, K>
。 T
是当前字段返回的类型,而K
是Walked
或NotWalked
。
生命周期'a
来自Juniper,是传入查询的生命周期。
T
T
允许我们为不同的类型实现不同的方法。例如,在上面的示例中,我们为QueryTrail<'_, Post, K>
实现了id
和author
,但只为QueryTrail<'_, User, K>
实现了id
。
如果您的字段返回一个 Vec<T>
或 Option<T>
,则给定的查询路径将变为 QueryTrail<'_, T, _>
。因此,Vec
或 Option
将被移除,您将只得到最内层的类型。这是因为 GraphQL 查询语法中,您查询的是 User
还是 [User]
并不重要。您可以访问的字段是相同的。
K
Walked
和 NotWalked
类型用于检查给定的路径是否实际被检查为查询的一部分。对 QueryTrail<'_, T, K>
的任何方法调用将返回 QueryTrail<'_, T, NotWalked>
,要检查路径是否确实是查询的一部分,您必须调用 .walk()
,它返回 Option<QueryTrail<'_, T, Walked>>
。如果是 Some(_)
,则表示路径是查询的一部分,您可以执行必要的预加载。
示例
if let Some(walked_trail) = trail
.some_field()
.some_other_field()
.third_field()
.walk()
{
// preload stuff
}
您始终可以运行 cargo doc
并检查 QueryTrail
上的所有方法及其调用上下文。
接口和联合 QueryTrail
的向下转换
本节主要与您使用 juniper-eager-loading 有关,但它并不仅限于该库。
如果您有一个 QueryTrail<'a, T, Walked>
,其中 T
是一个接口或联合类型,您可以使用 .downcast()
将该 QueryTrail
转换为接口或联合类型的实现之一。
示例
#
graphql_schema! {
schema {
query: Query
}
type Query {
search(query: String!): [SearchResult!]!
}
interface SearchResult {
id: ID!
}
type Article implements SearchResult {
id: ID!
}
type Tweet implements SearchResult {
id: ID!
}
}
pub struct Query;
impl QueryFields for Query {
fn field_search(
&self,
executor: &Executor<'_, Context>,
trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
query: String,
) -> FieldResult<&Vec<SearchResult>> {
let article_trail: QueryTrail<'_, Article, Walked> = trail.downcast();
let tweet_trail: QueryTrail<'_, Tweet, Walked> = trail.downcast();
// ...
# unimplemented!()
}
}
这有什么用呢?
如果您要执行某种数据预加载,您可能有一个函数可以检查 QueryTrail
并从数据库中加载数据。这样的函数可能看起来像这样
fn preload_users(
mut users: Vec<User>,
query_trail: &QueryTrail<'_, User, Walked>,
db: &Database,
) -> Vec<User> {
// ...
}
此函数在存在返回 [User!]!
的字段时运行良好。该字段将获得 QueryTrail<'a, User, Walked>
,这正是 preload_users
所需要的。
然而,现在想象一下您有一个这样的模式
type Query {
search(query: String!): [SearchResult!]!
}
union SearchResult = User | City | Country
type User {
id: ID!
city: City!
}
type City {
id: ID!
country: Country!
}
type Country {
id: ID!
}
方法 QueryFields::field_search
将接收一个 QueryTrail<'a, SearchResult, Walked>
。这种类型与 preload_users
不兼容。因此,我们必须将我们的 QueryTrail<'a, SearchResult, Walked>
转换为 QueryTrail<'a, User, Walked>
。
可以通过调用 .downcast()
来实现,这对于接口和联合查询路径是自动实现的。参见上面的示例。
带有参数的字段的 QueryTrail
有时你可能有 GraphQL 字段,它们接受影响你的解析器应该返回哪些内容的参数。因此,QueryTrail
允许你检查字段的参数。
简略示例
use chrono::prelude::*;
graphql_schema! {
schema {
query: Query
}
type Query {
countries: [Country!]! @juniper(ownership: "owned")
}
type Country {
users(activeSince: DateTimeUtc!): [User!]! @juniper(ownership: "owned")
}
type User {
id: ID!
}
scalar DateTimeUtc
}
pub struct Query;
impl QueryFields for Query {
fn field_countries<'a>(
&self,
executor: &'a juniper::Executor<'a, Context>,
trail: &'a QueryTrail<'a, Country, Walked>
) -> juniper::FieldResult<Vec<Country>> {
// Get struct that has all arguments passed to `Country.users`
let args: CountryUsersArgs<'a> = trail.users_args();
// The struct has methods for each argument, e.g. `active_since`.
//
// Notice that it automatically converts the incoming value to
// a `DateTime<Utc>`.
let _: DateTime<Utc> = args.active_since();
# unimplemented!()
// ...
}
}
你也可以省略 'a
生命周期。
#
impl QueryFields for Query {
fn field_countries(
&self,
executor: &juniper::Executor<'_, Context>,
trail: &QueryTrail<'_, Country, Walked>
) -> juniper::FieldResult<Vec<Country>> {
let args: CountryUsersArgs = trail.users_args();
# unimplemented!()
// ...
}
}
参数结构的名称总是 {name of type}{name of field}Args
(例如 CountryUsersArgs
)。方法名称总是以 snake case 形式表示参数的名称。
*_args
方法仅在 Walked
查询路径上定义,所以如果你遇到错误
---- src/lib.rs - (line 10) stdout ----
error[E0599]: no method named `users_args` found for type `&QueryTrail<'_, Country, Walked>` in the current
scope
--> src/lib.rs:10:1
|
10 | trail.users_args();
| ^^^^^^^^^^^^ method not found in `&QueryTrail<'_, Country, Walked>`
这很可能是你忘记在 trail
上调用 .walk()
。
请记住,您始终可以运行 cargo doc
来获取生成代码的高级概述。
自定义错误类型
默认情况下,生成的字段方法的返回类型将是 juniper::FieldResult<T>
。这只是一个类型别名,对应于 std::result::Result<T, juniper::FieldError>
。如果您想使用不同于 juniper::FieldError
的错误类型,可以在 graphql_schema_from_file!
中传递 , error_type: YourType
。
请注意,您的自定义错误类型必须实现 juniper::IntoFieldError
以进行类型检查。
示例
graphql_schema_from_file!("tests/schemas/doc_schema.graphql", error_type: MyError);
pub struct MyError(String);
impl juniper::IntoFieldError for MyError {
fn into_field_error(self) -> juniper::FieldError {
// Perform custom error handling
juniper::FieldError::from(self.0)
}
}
pub struct Query;
impl QueryFields for Query {
fn field_hello_world(
&self,
executor: &Executor<'_, Context>,
name: String,
) -> Result<String, MyError> {
Ok(format!("Hello, {}!", name))
}
}
graphql_schema!
不支持更改错误类型。
自定义上下文类型
默认情况下,生成的代码将假设您的上下文类型名为 Context
。如果不是这种情况,您可以通过调用 graphql_schema_from_file!
并传递 context_type: NewName
来自定义它。
示例
graphql_schema_from_file!("tests/schemas/doc_schema.graphql", context_type: MyContext);
pub struct MyContext;
impl juniper::Context for MyContext {}
pub struct Query;
impl QueryFields for Query {
fn field_hello_world(
&self,
executor: &Executor<'_, MyContext>,
name: String,
) -> juniper::FieldResult<String> {
Ok(format!("Hello, {}!", name))
}
}
graphql_schema!
不支持更改上下文类型。
检查生成的代码
如果您想查看生成的代码,可以在编译时设置环境变量 JUNIPER_FROM_SCHEMA_DEBUG
为 1
。例如:
JUNIPER_FROM_SCHEMA_DEBUG=1 cargo build
生成的代码将不会进行格式化,可能难以阅读。最简单的方法是将打印的代码复制到文件中,然后通过 rustfmt 运行。
或者,您可以包含名为 "format-debug-output"
的 功能。这将运行 rustfmt 在打印输出之前。这样您就不必手动进行。例如 Cargo.toml
[dependencies]
juniper-from-schema = { version = "x.y.z", features = ["format-debug-output"] }
不幸的是,这需要您使用 nightly,因为 rustfmt 需要 nightly。它也可能因为 rustfmt 无法编译的原因而破坏您的构建 ¯\\_(ツ)_/¯。如果您遇到这种情况,请删除 "format-debug-output"
功能并手动格式化输出。
依赖项
~7–16MB
~238K SLoC