correct doc examples so they compile

- examples to fix still
  - error_extensions.md ResultExt example does not compile!
     - trait ErrorExtensions is not implemented for ParseIntError
  - dataloader
     - requires sqlx to work. So we either "stub" it OR we rewrite them simpler to use a  simple "faux" db library
This commit is contained in:
Edward Rudd 2022-06-02 17:32:12 -04:00
parent 1a0ea01fdb
commit 3b7ed74d11
64 changed files with 943 additions and 131 deletions

View File

@ -15,23 +15,27 @@
## Entity lookup function
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: ID }
struct Query;
#[Object]
impl Query {
#[entity]
#[graphql(entity)]
async fn find_user_by_id(&self, id: ID) -> User {
User { ... }
User { id }
}
#[entity]
#[graphql(entity)]
async fn find_user_by_id_with_username(&self, #[graphql(key)] id: ID, username: String) -> User {
User { ... }
User { id }
}
#[entity]
#[graphql(entity)]
async fn find_user_by_id_and_username(&self, id: ID, username: String) -> User {
User { ... }
User { id }
}
}
```
@ -59,6 +63,10 @@ A single primary key can consist of multiple fields, and even nested fields, you
In the following example, the primary key of the `User` object is `key { a b }`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: i32 }
#[derive(InputObject)]
struct NestedKey {
a: i32,
@ -69,9 +77,9 @@ struct Query;
#[Object]
impl Query {
#[entity]
#[graphql(entity)]
async fn find_user_by_key(&self, key: NestedKey) -> User {
User { ... }
User { id: key.a }
}
}
```

View File

@ -5,9 +5,14 @@ Apollo Tracing provides performance analysis results for each step of query. Thi
To enable the Apollo Tracing extension, add the extension when the `Schema` is created.
```rust
# extern crate async_graphql;
use async_graphql::*;
use async_graphql::extensions::ApolloTracing;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.extension(ApolloTracing) // Enable ApolloTracing extension
.finish();

View File

@ -15,17 +15,23 @@ when querying multiple resolvers, the results of all cache control parameters wi
We can use `QueryResponse` to get a merged cache control result from a query result, and call `CacheControl::value` to get the corresponding HTTP header.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
#[Object(cache_control(max_age = 60))]
impl Query {
#[graphql(cache_control(max_age = 30))]
async fn value1(&self) -> i32 {
1
}
#[graphql(cache_control(private))]
async fn value2(&self) -> i32 {
2
}
async fn value3(&self) -> i32 {
3
}
}
```
@ -46,4 +52,3 @@ The following are different queries corresponding to different cache control res
# max_age=60
{ value3 }
```

View File

@ -15,6 +15,7 @@ You can request the data inside a query by just calling `ctx.data::<TypeOfYourDa
The following example shows how to borrow data in `Context`.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -32,12 +33,22 @@ impl Query {
### Schema data
You can put data inside the context at the creation of the schema, it's usefull for data that do not change, like a connection pool.
You can put data inside the context at the creation of the schema, it's useful for data that do not change, like a connection pool.
An instance of how it would be written inside an application:
```rust
let schema = Schema::build(Query::default(), Mutation::default(), EmptySubscription)
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { version: i32}
# struct EnvStruct;
# let env_struct = EnvStruct;
# struct S3Object;
# let s3_storage = S3Object;
# struct DBConnection;
# let db_core = DBConnection;
let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription)
.data(env_struct)
.data(s3_storage)
.data(db_core)
@ -51,22 +62,31 @@ You can put data inside the context at the execution of the request, it's useful
A little example with a `warp` route:
```rust
# extern crate async_graphql;
# extern crate async_graphql_warp;
# extern crate warp;
# use async_graphql::*;
# use warp::{Filter, Reply};
# use std::convert::Infallible;
# #[derive(Default, SimpleObject)]
# struct Query { name: String }
# struct AuthInfo { pub token: Option<String> }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
# let schema_filter = async_graphql_warp::graphql(schema);
let graphql_post = warp::post()
.and(warp::path("graphql"))
.and(warp::header::optional("Authorization"))
.and(schema_filter)
.and(a_warp_filter)
...
.and_then( |schema: (Schema<Query, Mutation, Subscriptions>, async_graphql::Request), arg2: ArgType2 ...| async move {
let (schema, request) = schema;
let your_auth_data = auth_function_from_headers(headers).await?;
.and_then( |auth: Option<String>, (schema, mut request): (Schema<Query, EmptyMutation, EmptySubscription>, async_graphql::Request)| async move {
// Do something to get auth data from the header
let your_auth_data = AuthInfo { token: auth };
let response = schema
.execute(
request
.data(your_auth_data)
.data(something_else)
).await;
Ok(async_graphql_warp::Response::from(response))
Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(response))
});
```
@ -75,6 +95,11 @@ let graphql_post = warp::post()
With the Context you can also insert and appends headers.
```rust
# extern crate async_graphql;
# extern crate http;
# use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
# use async_graphql::*;
# struct Query;
#[Object]
impl Query {
async fn greet(&self, ctx: &Context<'_>) -> String {
@ -101,6 +126,7 @@ Sometimes you want to know what fields are requested in the subquery to optimize
If you want to perform a search accross the query or the subqueries, you do not have to do this by hand with the `SelectionField`, you can use the `ctx.look_ahead()` to perform a selection
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]

View File

@ -5,6 +5,7 @@ Relay's cursor connection specification is designed to provide a consistent meth
Defining a cursor connection in `async-graphql` is very simple, you just call the `connection::query` function and query data in the closure.
```rust
# extern crate async_graphql;
use async_graphql::*;
use async_graphql::types::connection::*;
@ -34,10 +35,10 @@ impl Query {
let mut connection = Connection::new(start > 0, end < 10000);
connection.edges.extend(
(start..end).into_iter().map(|n|
Ok(Edge::with_additional_fields(n, n as i32, EmptyFields)),
))?;
Ok(connection)
})
Edge::with_additional_fields(n, n as i32, EmptyFields)
));
Ok::<_, async_graphql::Error>(connection)
}).await
}
}

View File

@ -8,6 +8,8 @@ generate a factory function that receives the parameters of the directive and re
Currently `Async-graphql` only supports directive located at `FIELD`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct ConcatDirective {
value: String,
}
@ -33,6 +35,18 @@ fn concat(value: String) -> impl CustomDirective {
Register the directive when building the schema:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn verison(&self) -> &str { "1.0" } }
# struct ConcatDirective { value: String, }
# #[async_trait::async_trait]
# impl CustomDirective for ConcatDirective {
# async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> { todo!() }
# }
# #[Directive(location = "field")]
# fn concat(value: String) -> impl CustomDirective { ConcatDirective { value } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.directive(concat)
.finish();

View File

@ -7,6 +7,7 @@ Using `async-graphql::Scalar`, you can add support for a scalar when you impleme
The following example defines a 64-bit integer scalar where its input and output are strings.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct StringNumber(i64);
@ -34,6 +35,11 @@ impl ScalarType for StringNumber {
If your type implemented `serde::Serialize` and `serde::Deserialize`, then you can use this macro to define a scalar more simply.
```rust
# extern crate async_graphql;
# extern crate serde;
# use async_graphql::*;
# use serde::{Serialize, Deserialize};
# use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
struct MyValue {
a: i32,

View File

@ -12,7 +12,7 @@ query { todos { users { name } } }
and `User` resolver is like this:
```rust
```rust,ignore
struct User {
id: u64,
}
@ -65,7 +65,7 @@ We need to group queries and exclude duplicate queries. `Dataloader` can do this
The following is an example of using `DataLoader` to optimize queries::
```rust
```rust,ignore
use async_graphql::*;
use async_graphql::dataloader::*;
use itertools::Itertools;
@ -115,7 +115,9 @@ SELECT name FROM user WHERE id IN (1, 2, 3, 4)
You can implement multiple data types for the same `Loader`, like this:
```rust
```rust,ignore
# extern crate async_graphql;
# use async_graphql::*;
struct PostgresLoader {
pool: sqlx::Pool<Postgres>,
}

View File

@ -6,6 +6,7 @@ Below are some examples.
## Object field
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -17,26 +18,35 @@ fn my_default() -> i32 {
#[Object]
impl Query {
// The default value of the value parameter is 0, it will call i32::default()
fn test1(&self, #[graphql(default)] value: i32) {}
async fn test1(&self, #[graphql(default)] value: i32) -> i32 { todo!() }
// The default value of the value parameter is 10
fn test2(&self, #[graphql(default = 10)] value: i32) {}
async fn test2(&self, #[graphql(default = 10)] value: i32) -> i32 { todo!() }
// The default value of the value parameter uses the return result of the my_default function, the value is 30.
fn test3(&self, #[graphql(default_with = "my_default()")] value: i32) {}
async fn test3(&self, #[graphql(default_with = "my_default()")] value: i32) -> i32 { todo!() }
}
```
## Interface field
```rust
# extern crate async_graphql;
# fn my_default() -> i32 { 5 }
# struct MyObj;
# #[Object]
# impl MyObj {
# async fn test1(&self, value: i32) -> i32 { todo!() }
# async fn test2(&self, value: i32) -> i32 { todo!() }
# async fn test3(&self, value: i32) -> i32 { todo!() }
# }
use async_graphql::*;
#[derive(Interface)]
#[graphql(
field(name = "test1", arg(name = "value", default)),
field(name = "test2", arg(name = "value", default = 10)),
field(name = "test3", arg(name = "value", default_with = "my_default()")),
field(name = "test1", type = "i32", arg(name = "value", type = "i32", default)),
field(name = "test2", type = "i32", arg(name = "value", type = "i32", default = 10)),
field(name = "test3", type = "i32", arg(name = "value", type = "i32", default_with = "my_default()")),
)]
enum MyInterface {
MyObj(MyObj),
@ -46,6 +56,8 @@ enum MyInterface {
## Input object field
```rust
# extern crate async_graphql;
# fn my_default() -> i32 { 5 }
use async_graphql::*;
#[derive(InputObject)]

View File

@ -11,6 +11,16 @@ When creating your `Schema`, you can use `SchemaBuilder::data` to configure the
The following `value_from_db` function shows how to retrieve a database connection from `Context`.
```rust
# extern crate async_graphql;
# struct Data { pub name: String }
# struct DbConn {}
# impl DbConn {
# fn query_something(&self, id: i64) -> std::result::Result<Data, String> { Ok(Data {name:"".into()})}
# }
# struct DbPool {}
# impl DbPool {
# fn take(&self) -> DbConn { DbConn {} }
# }
use async_graphql::*;
struct MyObject {

View File

@ -5,6 +5,7 @@ It's easy to define an `Enum`, here we have an example:
**Async-graphql will automatically change the name of each item to GraphQL's CONSTANT_CASE convention. You can use `name` to rename.**
```rust
# extern crate async_graphql;
use async_graphql::*;
/// One of the films in the Star Wars Trilogy
@ -30,6 +31,8 @@ expose remote enumeration types to GraphQL. In order to provide an `Enum` type,
enum that has parity with the existing, remote enum type.
```rust
# extern crate async_graphql;
# mod remote_crate { pub enum RemoteEnum { A, B, C } }
use async_graphql::*;
/// Provides parity with a remote enum type
@ -55,6 +58,9 @@ impl From<remote_crate::RemoteEnum> for LocalEnum {
The process is tedious and requires multiple steps to keep the local and remote enums in sync. `Async_graphql` provides a handy feature to generate the `From<remote_crate::RemoteEnum> for LocalEnum` as well as an opposite direction of `From<LocalEnum> for remote_crate::RemoteEnum` via an additional attribute after deriving `Enum`:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# mod remote_crate { pub enum RemoteEnum { A, B, C } }
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
#[graphql(remote = "remote_crate::RemoteEnum")]
enum LocalEnum {

View File

@ -8,6 +8,9 @@ The definition of `InputObject` is similar to [SimpleObject](define_simple_objec
You can add optional `#[graphql]` attributes to add descriptions or rename the field.
```rust
# extern crate async_graphql;
# #[derive(SimpleObject)]
# struct User { a: i32 }
use async_graphql::*;
#[derive(InputObject)]
@ -23,6 +26,7 @@ impl Mutation {
async fn users_at_location(&self, coordinate: Coordinate, radius: f64) -> Vec<User> {
// Writes coordination to database.
// ...
# todo!()
}
}
```
@ -35,6 +39,12 @@ and specify how its concrete types should be implemented.
In the following example, two `InputObject` types are created:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(InputObject)]
# struct SomeType { a: i32 }
# #[derive(InputObject)]
# struct SomeOtherType { a: i32 }
#[derive(InputObject)]
#[graphql(concrete(name = "SomeName", params(SomeType)))]
#[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
@ -63,6 +73,19 @@ input SomeOtherName {
In your resolver method or field of another input object, use as a normal generic type:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(InputObject)]
# struct SomeType { a: i32 }
# #[derive(InputObject)]
# struct SomeOtherType { a: i32 }
# #[derive(InputObject)]
# #[graphql(concrete(name = "SomeName", params(SomeType)))]
# #[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
# pub struct SomeGenericInput<T: InputType> {
# field1: Option<T>,
# field2: String
# }
#[derive(InputObject)]
pub struct YetAnotherInput {
a: SomeGenericInput<SomeType>,

View File

@ -14,6 +14,7 @@ If you need e.g. a snake_cased GraphQL field name, you can use both the `name` a
- When only `name` exists, `name.to_camel_case()` is the GraphQL field name and the `name` is the resolver function name.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Circle {
@ -75,6 +76,8 @@ If an interface is not referenced, it will not exist in the registry, as in the
because `MyInterface` is not referenced in `Schema`, the `MyInterface` type will not exist in the registry.
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Interface)]
#[graphql(
field(name = "name", type = "String"),
@ -103,7 +106,18 @@ type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;
You need to manually register the `MyInterface` type when constructing the `Schema`:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Interface)]
# #[graphql(field(name = "name", type = "String"))]
# enum MyInterface { MyObject(MyObject) }
# #[derive(SimpleObject)]
# struct MyObject { name: String, }
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
Schema::build(Query, EmptyMutation, EmptySubscription)
.register_type::<MyInterface>()
.register_output_type::<MyInterface>()
.finish();
```

View File

@ -6,6 +6,9 @@ It is especially useful when you want a user to be able to choose between severa
This feature is still an [RFC](https://github.com/graphql/graphql-spec/pull/825) and therefore not yet officially part of the GraphQL spec, but `Async-graphql` already supports it!
```rust
# extern crate async_graphql;
# #[derive(SimpleObject)]
# struct User { a: i32 }
use async_graphql::*;
#[derive(OneofObject)]
@ -29,6 +32,7 @@ struct Query {}
impl Query {
async fn search_users(&self, by: Vec<UserBy>) -> Vec<User> {
// ... Searches and returns a list of users ...
# todo!()
}
}
```

View File

@ -6,6 +6,7 @@ If you don't require automatic mapping of fields, see [Object](define_complex_ob
The example below defines an object `MyObject` which includes the fields `a` and `b`. `c` will be not mapped to GraphQL as it is labelled as `#[graphql(skip)]`
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]
@ -32,6 +33,8 @@ the non-calculated fields, where as the `ComplexObject` macro let's you write us
Resolvers added to `ComplexObject` adhere to the same rules as resolvers of [Object](define_complex_object.html).
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(SimpleObject)]
#[graphql(complex)] // NOTE: If you want the `ComplexObject` macro to take effect, this `complex` attribute is required.
struct MyObj {
@ -55,6 +58,12 @@ and specify how its concrete types should be implemented.
In the following example, two `SimpleObject` types are created:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct SomeType { a: i32 }
# #[derive(SimpleObject)]
# struct SomeOtherType { a: i32 }
#[derive(SimpleObject)]
#[graphql(concrete(name = "SomeName", params(SomeType)))]
#[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
@ -83,6 +92,19 @@ type SomeOtherName {
In your resolver method or field of another object, use as a normal generic type:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct SomeType { a: i32 }
# #[derive(SimpleObject)]
# struct SomeOtherType { a: i32 }
# #[derive(SimpleObject)]
# #[graphql(concrete(name = "SomeName", params(SomeType)))]
# #[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
# pub struct SomeGenericObject<T: OutputType> {
# field1: Option<T>,
# field2: String,
# }
#[derive(SimpleObject)]
pub struct YetAnotherObject {
a: SomeGenericObject<SomeType>,
@ -95,6 +117,8 @@ You can pass multiple generic types to `params()`, separated by a comma.
## Used for both input and output
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(SimpleObject, InputObject)]
#[graphql(input_name = "MyObjInput")] // Note: You must use the input_name attribute to define a new name for the input type, otherwise a runtime error will occur.
struct MyObj {

View File

@ -6,6 +6,7 @@ The implementation is quite similar for `Async-graphql`; from `Async-graphql`'s
The following example modified the definition of `Interface` a little bit and removed fields.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Circle {
@ -54,6 +55,7 @@ into the parent union. This is done by applying `#[graphql(flatten)]` on each
member we want to flatten.
```rust
# extern crate async_graphql;
#[derive(async_graphql::Union)]
pub enum TopLevelUnion {
A(A),
@ -65,6 +67,7 @@ pub enum TopLevelUnion {
#[derive(async_graphql::SimpleObject)]
pub struct A {
a: i32,
// ...
}
@ -76,11 +79,13 @@ pub enum B {
#[derive(async_graphql::SimpleObject)]
pub struct C {
c: i32,
// ...
}
#[derive(async_graphql::SimpleObject)]
pub struct D {
d: i32,
// ...
}
```
@ -88,6 +93,13 @@ pub struct D {
The above example transforms the top-level union into this equivalent:
```rust
# extern crate async_graphql;
# #[derive(async_graphql::SimpleObject)]
# struct A { a: i32 }
# #[derive(async_graphql::SimpleObject)]
# struct C { c: i32 }
# #[derive(async_graphql::SimpleObject)]
# struct D { d: i32 }
#[derive(async_graphql::Union)]
pub enum TopLevelUnion {
A(A),

View File

@ -58,6 +58,11 @@ You can limit the depth when creating `Schema`. If the query exceeds this limit,
message `Query is nested too deep` will be returned.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.limit_depth(5) // Limit the maximum depth to 5
.finish();
@ -82,6 +87,11 @@ You can limit the complexity when creating the `Schema`. If the query exceeds th
and `Query is too complex` will be returned.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.limit_complexity(5) // Limit the maximum complexity to 5
.finish();
@ -96,6 +106,8 @@ In the following code, the complexity of the `value` field is `5`. The complexit
used to calculate the complexity of the `values` field, and the type of the return value must be `usize`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct Query;
#[Object]
@ -114,4 +126,3 @@ impl Query {
**Note: The complexity calculation is done in the validation phase and not the execution phase,
so you don't have to worry about partial execution of over-limit queries.**

View File

@ -5,12 +5,16 @@ Sometimes two fields have the same query logic, but the output type is different
In the following example, you already have a `duration_rfc2822` field outputting the time format in `RFC2822` format, and then reuse it to derive a new `date_rfc3339` field.
```rust
struct DateRFC3339(chrono::DateTime);
struct DateRFC2822(chrono::DateTime);
# extern crate chrono;
# use chrono::Utc;
# extern crate async_graphql;
# use async_graphql::*;
struct DateRFC3339(chrono::DateTime<Utc>);
struct DateRFC2822(chrono::DateTime<Utc>);
#[Scalar]
impl ScalarType for DateRFC3339 {
fn parse(value: Value) -> InputValueResult { ... }
fn parse(value: Value) -> InputValueResult<Self> { todo!() }
fn to_value(&self) -> Value {
Value::String(self.0.to_rfc3339())
@ -19,7 +23,7 @@ impl ScalarType for DateRFC3339 {
#[Scalar]
impl ScalarType for DateRFC2822 {
fn parse(value: Value) -> InputValueResult { ... }
fn parse(value: Value) -> InputValueResult<Self> { todo!() }
fn to_value(&self) -> Value {
Value::String(self.0.to_rfc2822())
@ -57,7 +61,7 @@ type Query {
A derived field won't be able to manage everythings easily: Rust's [orphan rule](https://doc.rust-lang.org/book/traits.html#rules-for-implementing-traits) requires that either the
trait or the type for which you are implementing the trait must be defined in the same crate as the impl, so the following code cannot be compiled:
```
```rust,ignore
impl From<Vec<U>> for Vec<T> {
...
}
@ -70,6 +74,10 @@ We included a `with` parameter to help you define a function to call instead of
### Example
```rust
# extern crate serde;
# use serde::{Serialize, Deserialize};
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Serialize, Deserialize, Clone)]
struct ValueDerived(String);

View File

@ -15,9 +15,15 @@ the error message exposed by `std::fmt::Display`. However, `Error` actually prov
A resolver looks like this:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32, Error> {
Err(Error::new("MyMessage").extend_with(|_, e| e.set("details", "CAN_NOT_FETCH")))
}
# }
```
may then return a response like this:
@ -47,12 +53,18 @@ The easiest way to provide extensions to any error is by calling `extend_with` o
This will on the fly convert any error into a `Error` with the given extension.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
use std::num::ParseIntError;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.map_err(|err: ParseIntError| err.extend_with(|_err, e| e.set("code", 404)))?)
}
# }
```
### Implementing ErrorExtensions for custom errors.
@ -60,10 +72,10 @@ If you find yourself attaching extensions to your errors all over the place you
implementing the trait on your custom error type directly.
```rust
#[macro_use]
extern crate thiserror;
#[derive(Debug, Error)]
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[error("Could not find resource")]
NotFound,
@ -81,7 +93,7 @@ impl ErrorExtensions for MyError {
Error::new(format!("{}", self)).extend_with(|err, e|
match self {
MyError::NotFound => e.set("code", "NOT_FOUND"),
MyError::ServerError(reason) => e.set("reason", reason),
MyError::ServerError(reason) => e.set("reason", reason.clone()),
MyError::ErrorWithoutExtensions => {}
})
}
@ -92,11 +104,29 @@ This way you only need to call `extend` on your error to deliver the error messa
Or further extend your error through `extend_with`.
```rust
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
# #[derive(Debug, thiserror::Error)]
# pub enum MyError {
# #[error("Could not find resource")]
# NotFound,
#
# #[error("ServerError")]
# ServerError(String),
#
# #[error("No Extensions")]
# ErrorWithoutExtensions,
# }
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions_result(&self) -> Result<i32> {
// Err(MyError::NotFound.extend())
// OR
Err(MyError::NotFound.extend_with(|_, e| e.set("on_the_fly", "some_more_info")))
}
# }
```
```json
@ -118,13 +148,19 @@ async fn parse_with_extensions_result(&self) -> Result<i32> {
## ResultExt
This trait enables you to call `extend_err` directly on results. So the above code becomes less verbose.
```rust
```rust,ignore
# // @todo figure out why this example does not compile!
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.extend_err(|_, e| e.set("code", 404))?)
}
# }
```
### Chained extensions
Since `ErrorExtensions` and `ResultExt` are implemented for any type `&E where E: std::fmt::Display`
@ -132,7 +168,11 @@ we can chain the extension together.
```rust
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
match "234a".parse() {
Ok(n) => Ok(n),
@ -143,6 +183,7 @@ async fn parse_with_extensions(&self) -> Result<i32> {
.extend_with(|_, e| e.set("code", 500))),
}
}
# }
```
Expected response:
@ -187,4 +228,3 @@ async fn parse_with_extensions_result(&self) -> Result<i32> {
.map_err(|ref e: ParseIntError| e.extend_with(|_, e| e.set("code", 404)))
}
```

View File

@ -2,7 +2,7 @@
Resolve can return a `Result`, which has the following definition:
```rust
```rust,ignore
type Result<T> = std::result::Result<T, Error>;
```
@ -12,6 +12,8 @@ The following example shows how to parse an input string to an integer. When par
See the [Error Extensions](error_extensions.md) section of this book for more details.
```rust
# extern crate async_graphql;
# use std::num::ParseIntError;
use async_graphql::*;
struct Query;
@ -21,7 +23,7 @@ impl Query {
async fn parse_with_extensions(&self, input: String) -> Result<i32> {
Ok("234a"
.parse()
.map_err(|err: ParseIntError| err.extend_with(|_| json!({"code": 400})))?)
.map_err(|err: ParseIntError| err.extend_with(|_, e| e.set("code", 400)))?)
}
}
```
@ -29,7 +31,7 @@ impl Query {
#### Errors in subscriptions
Errors can be returned from subscription resolvers as well, using a return type of the form:
```rust
```rust,ignore
async fn my_subscription_resolver(&self) -> impl Stream<Item = Result<MyItem, MyError>> { ... }
```

View File

@ -15,6 +15,8 @@ To improve network performance for large queries, you can enable this Persisted
This extension doesn't force you to use some cache strategy, you can choose the caching strategy you want, you'll just have to implement the `CacheStorage` trait:
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[async_trait::async_trait]
pub trait CacheStorage: Send + Sync + Clone + 'static {
/// Load the query by `key`.

View File

@ -10,7 +10,7 @@ Across every step, you'll have the `ExtensionContext` supplied with data about y
For those who don't know, let's dig deeper into what is a middleware:
```rust
```rust,ignore
async fn middleware(&self, ctx: &ExtensionContext<'_>, next: NextMiddleware<'_>) -> MiddlewareResult {
// Logic to your middleware.
@ -38,21 +38,35 @@ First, when we receive a request, if it's not a subscription, the first function
Default implementation for `request`:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
next.run(ctx).await
}
# }
```
Depending on where you put your logic code, it'll be executed at the beginning or at the ending of the query being processed.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
// The code here will be run before the prepare_request is executed.
let result = next.run(ctx).await;
// The code after the completion of this future will be after the processing, just before sending the result to the user.
result
}
# }
```
### prepare_request
@ -60,6 +74,13 @@ async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Re
Just after the `request`, we will have the `prepare_request` lifecycle, which will be hooked.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn prepare_request(
&self,
ctx: &ExtensionContext<'_>,
@ -71,6 +92,7 @@ async fn prepare_request(
// The code here will be run just after the prepare_request
result
}
# }
```
### parse_query
@ -78,6 +100,13 @@ async fn prepare_request(
The `parse_query` will create a GraphQL `ExecutableDocument` on your query, it'll check if the query is valid for the GraphQL Spec. Usually the implemented spec in `async-graphql` tends to be the last stable one (October2021).
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# use async_graphql::parser::types::ExecutableDocument;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at parse query.
async fn parse_query(
&self,
@ -90,6 +119,7 @@ async fn parse_query(
) -> ServerResult<ExecutableDocument> {
next.run(ctx, query, variables).await
}
# }
```
### validation
@ -97,6 +127,12 @@ async fn parse_query(
The `validation` step will check (depending on your `validation_mode`) rules the query should abide to and give the client data about why the query is not valid.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at validation query.
async fn validation(
&self,
@ -105,6 +141,7 @@ async fn validation(
) -> Result<ValidationResult, Vec<ServerError>> {
next.run(ctx).await
}
# }
```
### execute
@ -112,6 +149,12 @@ async fn validation(
The `execution` step is a huge one, it'll start the execution of the query by calling each resolver concurrently for a `Query` and serially for a `Mutation`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at execute query.
async fn execute(
&self,
@ -124,6 +167,7 @@ async fn execute(
// After resolving the whole query
result
}
# }
````
### resolve
@ -131,6 +175,12 @@ async fn execute(
The `resolve` step is launched for each field.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at resolve field.
async fn resolve(
&self,
@ -143,6 +193,7 @@ async fn resolve(
// Logic after resolving the field
result
}
# }
```
### subscribe
@ -150,6 +201,13 @@ async fn resolve(
The `subscribe` lifecycle has the same behavior as the `request` but for a `Subscritpion`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# use futures_util::stream::BoxStream;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at subscribe request.
fn subscribe<'s>(
&self,
@ -159,4 +217,5 @@ fn subscribe<'s>(
) -> BoxStream<'s, Response> {
next.run(ctx, stream)
}
# }
```

View File

@ -3,6 +3,8 @@
You can define a `guard` for the fields of `Object`, `SimpleObject`, `ComplexObject` and `Subscription`, it will be executed before calling the resolver function, and an error will be returned if it fails.
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Eq, PartialEq, Copy, Clone)]
enum Role {
Admin,
@ -34,6 +36,14 @@ impl Guard for RoleGuard {
Use it with the `guard` attribute:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Eq, PartialEq, Copy, Clone)]
# enum Role { Admin, Guest, }
# struct RoleGuard { role: Role, }
# impl RoleGuard { fn new(role: Role) -> Self { Self { role } } }
# #[async_trait::async_trait]
# impl Guard for RoleGuard { async fn check(&self, ctx: &Context<'_>) -> Result<()> { todo!() } }
#[derive(SimpleObject)]
struct Query {
/// Only allow Admin
@ -50,6 +60,8 @@ struct Query {
Sometimes guards need to use field parameters, you need to pass the parameter value when creating the guard like this:
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct EqGuard {
expect: i32,
actual: i32,
@ -81,4 +93,4 @@ impl Query {
value
}
}
```
```

View File

@ -17,6 +17,7 @@
- **regex=RE** is match for the regex.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -24,7 +25,8 @@ struct Query;
#[Object]
impl Query {
/// The length of the name must be greater than or equal to 5 and less than or equal to 10.
async fn input(#[graphql(validator(min_length = 5, max_length = 10))] name: String) {
async fn input(&self, #[graphql(validator(min_length = 5, max_length = 10))] name: String) -> Result<i32> {
# todo!()
}
}
```
@ -34,13 +36,15 @@ impl Query {
You can enable the `list` attribute, and the validator will check all members in list:
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(list, max_length = 10))] names: Vec<String>) {
async fn input(&self, #[graphql(validator(list, max_length = 10))] names: Vec<String>) -> Result<i32> {
# todo!()
}
}
```
@ -48,6 +52,8 @@ impl Query {
## Custom validator
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct MyValidator {
expect: i32,
}

View File

@ -5,9 +5,18 @@
When you define your `actix_web::App` you need to pass in the Schema as data.
```rust
# extern crate async_graphql_actix_web;
# extern crate async_graphql;
# extern crate actix_web;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use actix_web::{web, HttpRequest, HttpResponse};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
async fn index(
// Schema now accessible here
schema: web::Data<Schema>,
schema: web::Data<Schema<Query, EmptyMutation, EmptySubscription>>,
request: GraphQLRequest,
) -> web::Json<GraphQLResponse> {
web::Json(schema.execute(request.into_inner()).await.into())
@ -17,11 +26,20 @@ async fn index(
## Subscription example
```rust
# extern crate async_graphql_actix_web;
# extern crate async_graphql;
# extern crate actix_web;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use actix_web::{web, HttpRequest, HttpResponse};
use async_graphql_actix_web::GraphQLSubscription;
async fn index_ws(
schema: web::Data<Schema>,
schema: web::Data<Schema<Query, EmptyMutation, EmptySubscription>>,
req: HttpRequest,
payload: web::Payload,
) -> Result<HttpResponse> {
) -> actix_web::Result<HttpResponse> {
GraphQLSubscription::new(Schema::clone(&*schema)).start(&req, payload)
}
```

View File

@ -3,6 +3,13 @@
## Request example
```rust
# extern crate async_graphql_poem;
# extern crate async_graphql;
# extern crate poem;
# use async_graphql::*;
# #[derive(Default, SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use poem::Route;
use async_graphql_poem::GraphQL;
@ -13,7 +20,14 @@ let app = Route::new()
## Subscription example
```rust
use poem::Route;
# extern crate async_graphql_poem;
# extern crate async_graphql;
# extern crate poem;
# use async_graphql::*;
# #[derive(Default, SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use poem::{get, Route};
use async_graphql_poem::GraphQLSubscription;
let app = Route::new()

View File

@ -10,6 +10,16 @@ You can combine other filters later, or directly call `Schema::execute` to execu
## Request example
```rust
# extern crate async_graphql_warp;
# extern crate async_graphql;
# extern crate warp;
# use async_graphql::*;
# use std::convert::Infallible;
# use warp::Filter;
# struct QueryRoot;
# #[Object]
# impl QueryRoot { async fn version(&self) -> &str { "1.0" } }
# async fn other() {
type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
@ -18,20 +28,39 @@ let filter = async_graphql_warp::graphql(schema).and_then(|(schema, request): (M
let resp = schema.execute(request).await;
// Return result
Ok::<_, Infallible>(async_graphql_warp::Response::from(resp))
Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(resp))
});
warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
# }
```
## Subscription example
```rust
# extern crate async_graphql_warp;
# extern crate async_graphql;
# extern crate warp;
# use async_graphql::*;
# use futures_util::stream::{Stream, StreamExt};
# use std::convert::Infallible;
# use warp::Filter;
# struct SubscriptionRoot;
# #[Subscription]
# impl SubscriptionRoot {
# async fn tick(&self) -> impl Stream<Item = i32> {
# futures_util::stream::iter(0..10)
# }
# }
# struct QueryRoot;
# #[Object]
# impl QueryRoot { async fn version(&self) -> &str { "1.0" } }
# async fn other() {
let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
let filter = async_graphql_warp::graphql_subscription(schema);
warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
# }
```
## More examples
[https://github.com/async-graphql/examples/tree/master/warp](https://github.com/async-graphql/examples/tree/master/warp)

View File

@ -2,7 +2,7 @@
Usually we can create multiple implementations for the same type in Rust, but due to the limitation of procedural macros, we can not create multiple Object implementations for the same type. For example, the following code will fail to compile.
```rust
```rust,ignore,does_not_compile
#[Object]
impl Query {
async fn users(&self) -> Vec<User> {
@ -25,6 +25,12 @@ Instead, the `#[derive(MergedObject)]` macro allows you to split an object's res
**Note:** This works for queries and mutations. For subscriptions, see "Merging Subscriptions" below.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { a: i32 }
# #[derive(SimpleObject)]
# struct Movie { a: i32 }
#[derive(Default)]
struct UserQuery;
@ -66,13 +72,18 @@ Like merging Objects, each subscription block requires a unique name.
Example:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use futures_util::stream::{Stream};
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
#[derive(Default)]
struct Subscription1;
#[Subscription]
impl Subscription1 {
async fn events1(&self) -> impl Stream<Item = i32> {
futures::stream::iter(0..10)
futures_util::stream::iter(0..10)
}
}
@ -82,7 +93,7 @@ struct Subscription2;
#[Subscription]
impl Subscription2 {
async fn events2(&self) -> impl Stream<Item = i32> {
futures::stream::iter(10..20)
futures_util::stream::iter(10..20)
}
}

View File

@ -5,7 +5,10 @@
The query root object is a GraphQL object with a definition similar to other objects. Resolver functions for all fields of the query object are executed concurrently.
```rust
# extern crate async_graphql;
use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { a: i32 }
struct Query;
@ -13,6 +16,7 @@ struct Query;
impl Query {
async fn user(&self, username: String) -> Result<Option<User>> {
// Look up users from the database
# todo!()
}
}
@ -25,6 +29,7 @@ The mutation root object is also a GraphQL object, but it executes sequentially.
The following mutation root object provides an example of user registration and login:
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Mutation;
@ -33,11 +38,12 @@ struct Mutation;
impl Mutation {
async fn signup(&self, username: String, password: String) -> Result<bool> {
// User signup
}
# todo!()
}
async fn login(&self, username: String, password: String) -> Result<String> {
// User login (generate token)
# todo!()
}
}
```

View File

@ -19,6 +19,7 @@ The Schema of a GraphQL contains a required Query, an optional Mutation, and an
Here is a simple example where we provide just one query that returns the sum of `a` and `b`.
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -37,13 +38,23 @@ impl Query {
In our example, there is only a Query without a Mutation or Subscription, so we create the Schema with `EmptyMutation` and `EmptySubscription`, and then call `Schema::execute` to execute the Query.
```rust
# extern crate async_graphql;
# use async_graphql::*;
#
# struct Query;
# #[Object]
# impl Query {
# async fn version(&self) -> &str { "1.0" }
# }
# async fn other() {
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let res = schema.execute("{ add(a: 10, b: 20) }").await;
# }
```
## Output the query results as JSON
```rust
```rust,ignore
let json = serde_json::to_string(&res);
```

View File

@ -4,6 +4,7 @@ You can export your schema in Schema Definition Language (SDL) by using the `Sch
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -15,11 +16,8 @@ impl Query {
}
}
#[tokio::main]
async fn main() {
let schema = Schema::build(Query, EmptyMutation, EmptySubscription).finish();
let schema = Schema::build(Query, EmptyMutation, EmptySubscription).finish();
// Print the schema in SDL format
println!("{}", &schema.sdl());
}
// Print the schema in SDL format
println!("{}", &schema.sdl());
```

View File

@ -5,6 +5,12 @@ The definition of the subscription root object is slightly different from other
The following example subscribes to an integer stream, which generates one integer per second. The parameter `step` specifies the integer step size with a default of 1.
```rust
# extern crate async_graphql;
# use std::time::Duration;
# use async_graphql::futures_util::stream::Stream;
# use async_graphql::futures_util::StreamExt;
# extern crate tokio_stream;
# extern crate tokio;
use async_graphql::*;
struct Subscription;

View File

@ -3,6 +3,7 @@
By default, all types and fields are visible in introspection. But maybe you want to hide some content according to different users to avoid unnecessary misunderstandings. You can add the `visible` attribute to the type or field to do it.
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]
@ -20,7 +21,7 @@ struct MyObj {
c: i32,
}
#[derive(Enum)]
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
enum MyEnum {
// This item will be visible in introspection.
A,
@ -41,4 +42,4 @@ fn is_admin(ctx: &Context<'_>) -> bool {
ctx.data_unchecked::<IsAdmin>().0
}
```
```

View File

@ -15,23 +15,27 @@
## 实体查找函数
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: ID }
struct Query;
#[Object]
impl Query {
#[entity]
#[graphql(entity)]
async fn find_user_by_id(&self, id: ID) -> User {
User { ... }
User { id }
}
#[entity]
#[graphql(entity)]
async fn find_user_by_id_with_username(&self, #[graphql(key)] id: ID, username: String) -> User {
User { ... }
User { id }
}
#[entity]
#[graphql(entity)]
async fn find_user_by_id_and_username(&self, id: ID, username: String) -> User {
User { ... }
User { id }
}
}
```
@ -59,6 +63,10 @@ impl Query {
下面的例子中`User`对象的主键是`key { a b }`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: i32 }
#[derive(InputObject)]
struct NestedKey {
a: i32,
@ -69,9 +77,9 @@ struct Query;
#[Object]
impl Query {
#[entity]
#[graphql(entity)]
async fn find_user_by_key(&self, key: NestedKey) -> User {
User { ... }
User { id: key.a }
}
}
```

View File

@ -5,9 +5,14 @@
启用`Apollo Tracing`扩展需要在创建`Schema`的时候添加该扩展。
```rust
# extern crate async_graphql;
use async_graphql::*;
use async_graphql::extensions::ApolloTracing;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.extension(ApolloTracing) // 启用ApolloTracing扩展
.finish();

View File

@ -15,17 +15,23 @@
我们可以从查询结果`QueryResponse`中获取缓存控制合并结果,并且调用`CacheControl::value`来获取对应的HTTP头。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
#[Object(cache_control(max_age = 60))]
impl Query {
#[graphql(cache_control(max_age = 30))]
async fn value1(&self) -> i32 {
1
}
#[graphql(cache_control(private))]
async fn value2(&self) -> i32 {
2
}
async fn value3(&self) -> i32 {
3
}
}
```

View File

@ -15,6 +15,7 @@
下面的例子展示了如何从`Context`中借用数据。
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -35,7 +36,17 @@ impl Query {
你可以在创建`Schema`时将数据放入上下文中,这对于不会更改的数据非常有用,例如连接池。
```rust
let schema = Schema::build(Query::default(), Mutation::default(), EmptySubscription)
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { version: i32}
# struct EnvStruct;
# let env_struct = EnvStruct;
# struct S3Object;
# let s3_storage = S3Object;
# struct DBConnection;
# let db_core = DBConnection;
let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription)
.data(env_struct)
.data(s3_storage)
.data(db_core)
@ -49,22 +60,31 @@ impl Query {
一个使用`warp`的小例子:
```rust
# extern crate async_graphql;
# extern crate async_graphql_warp;
# extern crate warp;
# use async_graphql::*;
# use warp::{Filter, Reply};
# use std::convert::Infallible;
# #[derive(Default, SimpleObject)]
# struct Query { name: String }
# struct AuthInfo { pub token: Option<String> }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
# let schema_filter = async_graphql_warp::graphql(schema);
let graphql_post = warp::post()
.and(warp::path("graphql"))
.and(warp::header::optional("Authorization"))
.and(schema_filter)
.and(a_warp_filter)
...
.and_then( |schema: (Schema<Query, Mutation, Subscriptions>, async_graphql::Request), arg2: ArgType2 ...| async move {
let (schema, request) = schema;
let your_auth_data = auth_function_from_headers(headers).await?;
.and_then( |auth: Option<String>, (schema, mut request): (Schema<Query, EmptyMutation, EmptySubscription>, async_graphql::Request)| async move {
// Do something to get auth data from the header
let your_auth_data = AuthInfo { token: auth };
let response = schema
.execute(
request
.data(your_auth_data)
.data(something_else)
).await;
Ok(async_graphql_warp::Response::from(response))
Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(response))
});
```
@ -73,6 +93,11 @@ let graphql_post = warp::post()
使用`Context`你还可以插入或添加HTTP头。
```rust
# extern crate async_graphql;
# extern crate http;
# use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
# use async_graphql::*;
# struct Query;
#[Object]
impl Query {
async fn greet(&self, ctx: &Context<'_>) -> String {
@ -99,6 +124,7 @@ impl Query {
如果要跨查询或子查询执行搜索,则不必使用 `SelectionField` 手动执行此操作,可以使用 `ctx.look_ahead()` 来执行选择。
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]

View File

@ -7,6 +7,7 @@ Relay定义了一套游标连接规范以提供一致性的分页查询方式
下面是一个简单的获取连续整数的数据源:
```rust
# extern crate async_graphql;
use async_graphql::*;
use async_graphql::types::connection::*;
@ -36,10 +37,11 @@ impl Query {
let mut connection = Connection::new(start > 0, end < 10000);
connection.edges.extend(
(start..end).into_iter().map(|n|
Ok(Edge::new_with_additional_fields(n, n as i32, EmptyFields)),
))?;
Ok(connection)
})
Edge::with_additional_fields(n, n as i32, EmptyFields)
));
Ok::<_, async_graphql::Error>(connection)
}).await
}
}
```

View File

@ -7,6 +7,8 @@
目前`Async-graphql`仅支持添加`FIELD`位置的指令。
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct ConcatDirective {
value: String,
}
@ -32,6 +34,18 @@ fn concat(value: String) -> impl CustomDirective {
创建模式时注册指令:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn verison(&self) -> &str { "1.0" } }
# struct ConcatDirective { value: String, }
# #[async_trait::async_trait]
# impl CustomDirective for ConcatDirective {
# async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> { todo!() }
# }
# #[Directive(location = "field")]
# fn concat(value: String) -> impl CustomDirective { ConcatDirective { value } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.directive(concat)
.finish();

View File

@ -7,6 +7,7 @@
下面的例子定义一个64位整数标量但它的输入输出都是字符串。 (`Async-graphql`已经内置了对64位整数的支持正是采用字符串作为输入输出)
```rust
# extern crate async_graphql;
use async_graphql::*;
@ -36,6 +37,11 @@ impl ScalarType for StringNumber {
如果你的类型实现了`serde :: Serialize`和`serde :: Deserialize`,那么可以使用此宏更简单地定义标量。
```rust
# extern crate async_graphql;
# extern crate serde;
# use async_graphql::*;
# use serde::{Serialize, Deserialize};
# use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
struct MyValue {
a: i32,

View File

@ -12,7 +12,7 @@ query { todos { users { name } } }
实现`User`的resolver代码如下
```rust
```rust,ignore
struct User {
id: u64,
}
@ -63,7 +63,7 @@ SELECT name FROM user WHERE id = $1
下面是使用`DataLoader`来优化查询请求的例子:
```rust
```rust,ignore
use async_graphql::*;
use async_graphql::dataloader::*;
use itertools::Itertools;
@ -113,7 +113,7 @@ SELECT name FROM user WHERE id IN (1, 2, 3, 4)
你可以为同一个`Loader`实现多种数据类型,就像下面这样:
```rust
```rust,ignore
struct PostgresLoader {
pool: sqlx::Pool<Postgres>,
}

View File

@ -5,6 +5,7 @@
## 对象字段参数
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -16,26 +17,35 @@ fn my_default() -> i32 {
#[Object]
impl Query {
// value参数的默认值为0它会调用i32::default()
fn test1(&self, #[graphql(default)] value: i32) {}
async fn test1(&self, #[graphql(default)] value: i32) -> i32 { todo!() }
// value参数的默认值为10
fn test2(&self, #[graphql(default = 10)] value: i32) {}
async fn test2(&self, #[graphql(default = 10)] value: i32) -> i32 { todo!() }
// value参数的默认值使用my_default函数的返回结果值为30
fn test3(&self, #[graphql(default_with = "my_default()")] value: i32) {}
async fn test3(&self, #[graphql(default_with = "my_default()")] value: i32) -> i32 { todo!() }
}
```
## 接口字段参数
```rust
# extern crate async_graphql;
# fn my_default() -> i32 { 5 }
# struct MyObj;
# #[Object]
# impl MyObj {
# async fn test1(&self, value: i32) -> i32 { todo!() }
# async fn test2(&self, value: i32) -> i32 { todo!() }
# async fn test3(&self, value: i32) -> i32 { todo!() }
# }
use async_graphql::*;
#[derive(Interface)]
#[graphql(
field(name = "test1", arg(name = "value", default)),
field(name = "test2", arg(name = "value", default = 10)),
field(name = "test3", arg(name = "value", default_with = "my_default()")),
field(name = "test1", type = "i32", arg(name = "value", type = "i32", default)),
field(name = "test2", type = "i32", arg(name = "value", type = "i32", default = 10)),
field(name = "test3", type = "i32", arg(name = "value", type = "i32", default_with = "my_default()")),
)]
enum MyInterface {
MyObj(MyObj),
@ -45,6 +55,8 @@ enum MyInterface {
## 输入对象(InputObject)
```rust
# extern crate async_graphql;
# fn my_default() -> i32 { 5 }
use async_graphql::*;
#[derive(InputObject)]

View File

@ -9,6 +9,16 @@ Resolver函数用于计算字段的值你可以执行一个数据库查询
在查询数据库时你可能需要一个数据库连接池对象这个对象是个全局的你可以在创建Schema的时候用`SchemaBuilder::data`函数设置`Schema`数据, 用`Context::data`函数设置`Context`数据。下面的`value_from_db`字段展示了如何从`Context`中获取一个数据库连接。
```rust
# extern crate async_graphql;
# struct Data { pub name: String }
# struct DbConn {}
# impl DbConn {
# fn query_something(&self, id: i64) -> std::result::Result<Data, String> { Ok(Data {name:"".into()})}
# }
# struct DbPool {}
# impl DbPool {
# fn take(&self) -> DbConn { DbConn {} }
# }
use async_graphql::*;
struct MyObject {

View File

@ -5,6 +5,7 @@
**Async-graphql会自动把枚举项的名称转换为GraphQL标准的大写加下划线形式你也可以用`name`属性自已定义名称。**
```rust
# extern crate async_graphql;
use async_graphql::*;
/// One of the films in the Star Wars Trilogy
@ -27,6 +28,8 @@ pub enum Episode {
Rust的 [孤儿规则](https://doc.rust-lang.org/book/traits.html#rules-for-implementing-traits) 要求特质或您要实现特质的类型必须在相同的板条箱中定义因此你不能向GraphQL公开外部枚举类型。 为了创建`Enum`类型,一种常见的解决方法是创建一个新的与现有远程枚举类型同等的枚举。
```rust
# extern crate async_graphql;
# mod remote_crate { pub enum RemoteEnum { A, B, C } }
use async_graphql::*;
/// Provides parity with a remote enum type
@ -52,6 +55,9 @@ impl From<remote_crate::RemoteEnum> for LocalEnum {
该过程很繁琐,需要多个步骤才能使本地枚举和远程枚举保持同步。`Async_graphql`提供了一个方便的功能,可在派生`Enum`之后通过附加属性生成LocalEnum的`From <remote_crate::RemoteEnum>`以及相反的`From<LocalEnum> for remote_crate::RemoteEnum`:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# mod remote_crate { pub enum RemoteEnum { A, B, C } }
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
#[graphql(remote = "remote_crate::RemoteEnum")]
enum LocalEnum {

View File

@ -5,6 +5,9 @@
你也通过可选的`#[graphql]`属性来给字段添加描述,重命名。
```rust
# extern crate async_graphql;
# #[derive(SimpleObject)]
# struct User { a: i32 }
use async_graphql::*;
#[derive(InputObject)]
@ -20,6 +23,7 @@ impl Mutation {
async fn users_at_location(&self, coordinate: Coordinate, radius: f64) -> Vec<User> {
// 将坐标写入数据库
// ...
# todo!()
}
}
```
@ -31,6 +35,12 @@ impl Mutation {
在下面的示例中,创建了两种`InputObject`类型:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(InputObject)]
# struct SomeType { a: i32 }
# #[derive(InputObject)]
# struct SomeOtherType { a: i32 }
#[derive(InputObject)]
#[graphql(concrete(name = "SomeName", params(SomeType)))]
#[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
@ -59,6 +69,19 @@ input SomeOtherName {
在其它`InputObject`中使用具体的泛型类型:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(InputObject)]
# struct SomeType { a: i32 }
# #[derive(InputObject)]
# struct SomeOtherType { a: i32 }
# #[derive(InputObject)]
# #[graphql(concrete(name = "SomeName", params(SomeType)))]
# #[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
# pub struct SomeGenericInput<T: InputType> {
# field1: Option<T>,
# field2: String
# }
#[derive(InputObject)]
pub struct YetAnotherInput {
a: SomeGenericInput<SomeType>,

View File

@ -12,6 +12,7 @@
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Circle {
@ -72,6 +73,8 @@ enum Shape {
即使`MyObject`实现了`MyInterface`,但由于`Schema`中并没有引用`MyInterface`,类型注册表中将不会存在`MyInterface`类型的信息。
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Interface)]
#[graphql(
field(name = "name", type = "String"),
@ -100,7 +103,18 @@ type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;
你需要在构造Schema时手工注册`MyInterface`类型:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Interface)]
# #[graphql(field(name = "name", type = "String"))]
# enum MyInterface { MyObject(MyObject) }
# #[derive(SimpleObject)]
# struct MyObject { name: String, }
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
Schema::build(Query, EmptyMutation, EmptySubscription)
.register_type::<MyInterface>()
.register_output_type::<MyInterface>()
.finish();
```

View File

@ -5,6 +5,7 @@
下面的例子定义了一个名称为MyObject的对象包含字段`a`和`b``c`由于标记为`#[graphql(skip)]`所以不会映射到GraphQL。
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]
@ -27,6 +28,12 @@ struct MyObject {
在下面的示例中,创建了两种`SimpleObject`类型:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct SomeType { a: i32 }
# #[derive(SimpleObject)]
# struct SomeOtherType { a: i32 }
#[derive(SimpleObject)]
#[graphql(concrete(name = "SomeName", params(SomeType)))]
#[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
@ -55,6 +62,19 @@ type SomeOtherName {
在其它`Object`中使用具体的泛型类型:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct SomeType { a: i32 }
# #[derive(SimpleObject)]
# struct SomeOtherType { a: i32 }
# #[derive(SimpleObject)]
# #[graphql(concrete(name = "SomeName", params(SomeType)))]
# #[graphql(concrete(name = "SomeOtherName", params(SomeOtherType)))]
# pub struct SomeGenericObject<T: OutputType> {
# field1: Option<T>,
# field2: String,
# }
#[derive(SimpleObject)]
pub struct YetAnotherObject {
a: SomeGenericObject<SomeType>,
@ -72,6 +92,8 @@ pub struct YetAnotherObject {
一些简单的字段,并使用`ComplexObject`宏来定义其他一些需要计算的字段。
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(SimpleObject)]
#[graphql(complex)] // 注意: 如果你希望ComplexObject宏生效complex属性是必须的
struct MyObj {
@ -90,6 +112,8 @@ impl MyObj {
## 同时用于输入和输出
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(SimpleObject, InputObject)]
#[graphql(input_name = "MyObjInput")] // 注意: 你必须用input_name属性为输入类型定义一个新的名称否则将产生一个运行时错误。
struct MyObj {

View File

@ -5,6 +5,7 @@
下面把接口定义的例子做一个小小的修改,去掉字段的定义。
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Circle {
@ -49,6 +50,7 @@ enum Shape {
GraphQL的有个限制是`Union`类型内不能包含其它联合类型。 所有成员必须为`Object`。
位置支持嵌套`Union`,我们可以用`#graphql(flatten)`,是它们合并到上级`Union`类型。
```rust
# extern crate async_graphql;
#[derive(async_graphql::Union)]
pub enum TopLevelUnion {
A(A),
@ -60,6 +62,7 @@ pub enum TopLevelUnion {
#[derive(async_graphql::SimpleObject)]
pub struct A {
a: i32,
// ...
}
@ -71,11 +74,13 @@ pub enum B {
#[derive(async_graphql::SimpleObject)]
pub struct C {
c: i32,
// ...
}
#[derive(async_graphql::SimpleObject)]
pub struct D {
d: i32,
// ...
}
```
@ -83,6 +88,13 @@ pub struct D {
上面的示例将顶级`Union`转换为以下等效形式:
```rust
# extern crate async_graphql;
# #[derive(async_graphql::SimpleObject)]
# struct A { a: i32 }
# #[derive(async_graphql::SimpleObject)]
# struct C { c: i32 }
# #[derive(async_graphql::SimpleObject)]
# struct D { d: i32 }
#[derive(async_graphql::Union)]
pub enum TopLevelUnion {
A(A),

View File

@ -53,6 +53,11 @@ type Post {
在创建`Schema`的时候可以限制深度,如果查询语句超过这个限制,则会出错并且返回`Query is nested too deep.`消息。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.limit_depth(5) // 限制最大深度为5
.finish();
@ -75,6 +80,11 @@ let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
在创建`Schema`的时候可以限制复杂度,如果查询语句超过这个限制,则会出错并且返回`Query is too complex.`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query { async fn version(&self) -> &str { "1.0" } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.limit_complexity(5) // 限制最大深度为5
.finish();
@ -87,6 +97,8 @@ let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
`count`是字段的参数,这个表达式用于计算`values`字段的复杂度,并且返回值的类型必须是`usize`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct Query;
#[Object]

View File

@ -5,12 +5,16 @@
在以下例子中,你已经有一个`duration_rfc2822`字段输出`RFC2822`格式的时间格式,然后复用它派生一个新的`date_rfc3339`字段。
```rust
struct DateRFC3339(chrono::DateTime);
struct DateRFC2822(chrono::DateTime);
# extern crate chrono;
# use chrono::Utc;
# extern crate async_graphql;
# use async_graphql::*;
struct DateRFC3339(chrono::DateTime<Utc>);
struct DateRFC2822(chrono::DateTime<Utc>);
#[Scalar]
impl ScalarType for DateRFC3339 {
fn parse(value: Value) -> InputValueResult { ... }
fn parse(value: Value) -> InputValueResult<Self> { todo!() }
fn to_value(&self) -> Value {
Value::String(self.0.to_rfc3339())
@ -19,7 +23,7 @@ impl ScalarType for DateRFC3339 {
#[Scalar]
impl ScalarType for DateRFC2822 {
fn parse(value: Value) -> InputValueResult { ... }
fn parse(value: Value) -> InputValueResult<Self> { todo!() }
fn to_value(&self) -> Value {
Value::String(self.0.to_rfc2822())
@ -56,7 +60,7 @@ type Query {
因为 [孤儿规则](https://doc.rust-lang.org/book/traits.html#rules-for-implementing-traits),以下代码无法通过编译:
```
```rust,ignore
impl From<Vec<U>> for Vec<T> {
...
}
@ -69,6 +73,10 @@ impl From<Vec<U>> for Vec<T> {
### Example
```rust
# extern crate serde;
# use serde::{Serialize, Deserialize};
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Serialize, Deserialize, Clone)]
struct ValueDerived(String);

View File

@ -17,9 +17,15 @@
Resolver函数类似这样
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32, Error> {
Err(Error::new("MyMessage").extend_with(|_, e| e.set("details", "CAN_NOT_FETCH")))
}
# }
```
然后可以返回如下响应:
@ -49,12 +55,18 @@ async fn parse_with_extensions(&self) -> Result<i32, Error> {
这将把任何错误转换为具有给定扩展信息的`Error`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
use std::num::ParseIntError;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.map_err(|err: ParseIntError| err.extend_with(|_err, e| e.set("code", 404)))?)
}
# }
```
### 为自定义错误实现ErrorExtensions
@ -63,10 +75,10 @@ async fn parse_with_extensions(&self) -> Result<i32> {
```rust
#[macro_use]
extern crate thiserror;
#[derive(Debug, Error)]
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[error("Could not find resource")]
NotFound,
@ -84,7 +96,7 @@ impl ErrorExtensions for MyError {
Error::new(format!("{}", self)).extend_with(|err, e|
match self {
MyError::NotFound => e.set("code", "NOT_FOUND"),
MyError::ServerError(reason) => e.set("reason", reason),
MyError::ServerError(reason) => e.set("reason", reason.clone()),
MyError::ErrorWithoutExtensions => {}
})
}
@ -94,11 +106,29 @@ impl ErrorExtensions for MyError {
您只需要对错误调用`extend`即可将错误与其提供的扩展信息一起传递,或者通过`extend_with`进一步扩展错误信息。
```rust
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
# #[derive(Debug, thiserror::Error)]
# pub enum MyError {
# #[error("Could not find resource")]
# NotFound,
#
# #[error("ServerError")]
# ServerError(String),
#
# #[error("No Extensions")]
# ErrorWithoutExtensions,
# }
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions_result(&self) -> Result<i32> {
// Err(MyError::NotFound.extend())
// OR
Err(MyError::NotFound.extend_with(|_, e| e.set("on_the_fly", "some_more_info")))
}
# }
```
```json
@ -120,13 +150,19 @@ async fn parse_with_extensions_result(&self) -> Result<i32> {
## ResultExt
这个特质使您可以直接在结果上调用`extend_err`。因此上面的代码不再那么冗长。
```rust
```rust,ignore
# // @todo figure out why this example does not compile!
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.extend_err(|_, e| e.set("code", 404))?)
}
# }
```
### 链式调用
@ -134,7 +170,11 @@ async fn parse_with_extensions(&self) -> Result<i32> {
由于对所有`&E where E: std::fmt::Display`实现了`ErrorExtensions`和`ResultsExt`,我们可以将扩展链接在一起。
```rust
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
match "234a".parse() {
Ok(n) => Ok(n),
@ -145,6 +185,7 @@ async fn parse_with_extensions(&self) -> Result<i32> {
.extend_with(|_, e| e.set("code", 500))),
}
}
# }
```
响应:
@ -191,4 +232,3 @@ async fn parse_with_extensions_result(&self) -> Result<i32> {
.map_err(|ref e: ParseIntError| e.extend_with(|_, e| e.set("code", 404)))
}
```

View File

@ -2,7 +2,7 @@
Resolver函数可以返回一个Result类型以下是Result的定义
```rust
```rust,ignore
type Result<T> = std::result::Result<T, Error>;
```
@ -11,6 +11,8 @@ type Result<T> = std::result::Result<T, Error>;
下面是一个例子,解析一个输入的字符串到整数,当解析失败时返回错误,并且附加额外的错误信息。
```rust
# extern crate async_graphql;
# use std::num::ParseIntError;
use async_graphql::*;
struct Query;
@ -20,7 +22,7 @@ impl Query {
async fn parse_with_extensions(&self, input: String) -> Result<i32> {
Ok("234a"
.parse()
.map_err(|err| err.extend_with(|_| json!({"code": 400})))?)
.map_err(|err: ParseIntError| err.extend_with(|_, e| e.set("code", 400)))?)
}
}
```

View File

@ -15,6 +15,8 @@
这个扩展不会强迫你使用一些缓存策略,你可以选择你想要的缓存策略,你只需要实现 `CacheStorage` trait
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[async_trait::async_trait]
pub trait CacheStorage: Send + Sync + Clone + 'static {
/// Load the query by `key`.

View File

@ -8,7 +8,7 @@
让我们了解什么是中间件:
```rust
```rust,ignore
async fn middleware(&self, ctx: &ExtensionContext<'_>, next: NextMiddleware<'_>) -> MiddlewareResult {
// 你的中间件代码
@ -34,21 +34,35 @@ async fn middleware(&self, ctx: &ExtensionContext<'_>, next: NextMiddleware<'_>)
Default implementation for `request`:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
next.run(ctx).await
}
# }
```
根据你放置逻辑代码的位置,它将在正在查询执行的开头或结尾执行。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
// 此处的代码将在执行 prepare_request 之前运行。
let result = next.run(ctx).await;
// 此处的代码将在把结果发送给客户端之前执行
result
}
# }
```
### 准备查询
@ -56,6 +70,13 @@ async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Re
`request` 之后,将调用`prepare_request`,你可以在此处对请求做一些转换。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
async fn prepare_request(
&self,
ctx: &ExtensionContext<'_>,
@ -67,6 +88,7 @@ async fn prepare_request(
// 此处的代码在 prepare_request 之后执行
result
}
# }
```
### 解析查询
@ -74,6 +96,13 @@ async fn prepare_request(
`parse_query` 将解析查询语句并生成 GraphQL `ExecutableDocument`,并且检查查询是否遵循 GraphQL 规范。 通常,`async-graphql` 遵循最后一个稳定的规范October2021
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# use async_graphql::parser::types::ExecutableDocument;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at parse query.
async fn parse_query(
&self,
@ -86,6 +115,7 @@ async fn parse_query(
) -> ServerResult<ExecutableDocument> {
next.run(ctx, query, variables).await
}
# }
```
### 校验
@ -93,6 +123,12 @@ async fn parse_query(
`validation` 步骤将执行查询校验(取决于你指定的 `validation_mode`),并向客户端提供有关查询无效的原因。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at validation query.
async fn validation(
&self,
@ -101,6 +137,7 @@ async fn validation(
) -> Result<ValidationResult, Vec<ServerError>> {
next.run(ctx).await
}
# }
```
### 执行
@ -108,6 +145,12 @@ async fn validation(
`execution` 步骤是一个很大的步骤,它将并发执行`Query`,或者顺序执行`Mutation`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at execute query.
async fn execute(
&self,
@ -120,6 +163,7 @@ async fn execute(
// 此处的代码在执行完整查询之后执行
result
}
# }
````
### resolve
@ -127,6 +171,12 @@ async fn execute(
为每个字段执行`resolve`.
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at resolve field.
async fn resolve(
&self,
@ -139,6 +189,7 @@ async fn resolve(
// resolve字段之后
result
}
# }
```
### 订阅
@ -146,6 +197,13 @@ async fn resolve(
`subscribe`的行为和`request`很像,只是专门用于订阅查询。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use async_graphql::extensions::*;
# use futures_util::stream::BoxStream;
# struct MyMiddleware;
# #[async_trait::async_trait]
# impl Extension for MyMiddleware {
/// Called at subscribe request.
fn subscribe<'s>(
&self,
@ -155,4 +213,5 @@ fn subscribe<'s>(
) -> BoxStream<'s, Response> {
next.run(ctx, stream)
}
# }
```

View File

@ -3,6 +3,8 @@
你可以为`Object`, `SimpleObject`, `ComplexObject`和`Subscription`的字段定义`守卫`,它将在调用字段的 Resolver 函数前执行,如果失败则返回一个错误。
```rust
# extern crate async_graphql;
# use async_graphql::*;
#[derive(Eq, PartialEq, Copy, Clone)]
enum Role {
Admin,
@ -34,6 +36,14 @@ impl Guard for RoleGuard {
用`guard`属性使用它:
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(Eq, PartialEq, Copy, Clone)]
# enum Role { Admin, Guest, }
# struct RoleGuard { role: Role, }
# impl RoleGuard { fn new(role: Role) -> Self { Self { role } } }
# #[async_trait::async_trait]
# impl Guard for RoleGuard { async fn check(&self, ctx: &Context<'_>) -> Result<()> { todo!() } }
#[derive(SimpleObject)]
struct Query {
/// 只允许Admin访问
@ -50,6 +60,8 @@ struct Query {
有时候守卫需要从字段参数中获取值,你需要像下面这样在创建守卫时传递该参数值:
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct EqGuard {
expect: i32,
actual: i32,

View File

@ -17,6 +17,7 @@
- **regex=RE** 匹配正则表达式
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -24,7 +25,8 @@ struct Query;
#[Object]
impl Query {
/// name参数的长度必须大于等于5小于等于10
async fn input(#[graphql(validator(min_length = 5, max_length = 10))] name: String) {
async fn input(&self, #[graphql(validator(min_length = 5, max_length = 10))] name: String) -> Result<i32> {
# todo!()
}
}
```
@ -34,13 +36,15 @@ impl Query {
你可以打开`list`属性表示校验器作用于一个列表内的所有成员:
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(list, max_length = 10))] names: Vec<String>) {
async fn input(&self, #[graphql(validator(list, max_length = 10))] names: Vec<String>) -> Result<i32> {
# todo!()
}
}
```
@ -48,6 +52,8 @@ impl Query {
## 自定义校验器
```rust
# extern crate async_graphql;
# use async_graphql::*;
struct MyValidator {
expect: i32,
}

View File

@ -9,8 +9,17 @@
你需要把Schema传入`actix_web::App`作为全局数据。
```rust
# extern crate async_graphql_actix_web;
# extern crate async_graphql;
# extern crate actix_web;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use actix_web::{web, HttpRequest, HttpResponse};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
async fn index(
schema: web::Data<Schema>,
schema: web::Data<Schema<Query, EmptyMutation, EmptySubscription>>,
request: GraphQLRequest,
) -> web::Json<GraphQLResponse> {
web::Json(schema.execute(request.into_inner()).await.into())
@ -20,11 +29,20 @@ async fn index(
## 订阅例子
```rust
# extern crate async_graphql_actix_web;
# extern crate async_graphql;
# extern crate actix_web;
# use async_graphql::*;
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use actix_web::{web, HttpRequest, HttpResponse};
use async_graphql_actix_web::GraphQLSubscription;
async fn index_ws(
schema: web::Data<Schema>,
schema: web::Data<Schema<Query, EmptyMutation, EmptySubscription>>,
req: HttpRequest,
payload: web::Payload,
) -> Result<HttpResponse> {
) -> actix_web::Result<HttpResponse> {
GraphQLSubscription::new(Schema::clone(&*schema)).start(&req, payload)
}
```

View File

@ -3,6 +3,13 @@
## 请求例子
```rust
# extern crate async_graphql_poem;
# extern crate async_graphql;
# extern crate poem;
# use async_graphql::*;
# #[derive(Default, SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use poem::Route;
use async_graphql_poem::GraphQL;
@ -13,7 +20,14 @@ let app = Route::new()
## 订阅例子
```rust
use poem::Route;
# extern crate async_graphql_poem;
# extern crate async_graphql;
# extern crate poem;
# use async_graphql::*;
# #[derive(Default, SimpleObject)]
# struct Query { a: i32 }
# let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
use poem::{get, Route};
use async_graphql_poem::GraphQLSubscription;
let app = Route::new()

View File

@ -9,6 +9,16 @@
## 请求例子
```rust
# extern crate async_graphql_warp;
# extern crate async_graphql;
# extern crate warp;
# use async_graphql::*;
# use std::convert::Infallible;
# use warp::Filter;
# struct QueryRoot;
# #[Object]
# impl QueryRoot { async fn version(&self) -> &str { "1.0" } }
# async fn other() {
type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
@ -17,17 +27,37 @@ let filter = async_graphql_warp::graphql(schema).and_then(|(schema, request): (M
let resp = schema.execute(request).await;
// 返回结果
Ok::<_, Infallible>(async_graphql_warp::Response::from(resp))
Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(resp))
});
warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
# }
```
## 订阅例子
```rust
# extern crate async_graphql_warp;
# extern crate async_graphql;
# extern crate warp;
# use async_graphql::*;
# use futures_util::stream::{Stream, StreamExt};
# use std::convert::Infallible;
# use warp::Filter;
# struct SubscriptionRoot;
# #[Subscription]
# impl SubscriptionRoot {
# async fn tick(&self) -> impl Stream<Item = i32> {
# futures_util::stream::iter(0..10)
# }
# }
# struct QueryRoot;
# #[Object]
# impl QueryRoot { async fn version(&self) -> &str { "1.0" } }
# async fn other() {
let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
let filter = async_graphql_warp::graphql_subscription(schema);
warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
# }
```
## 更多例子

View File

@ -4,7 +4,7 @@
通常我们在Rust中可以为同一类型创建多个实现但由于过程宏的限制无法为同一个类型创建多个Object实现。例如下面的代码将无法通过编译。
```rust
```rust,ignore,does_not_compile
#[Object]
impl MyObject {
async fn field1(&self) -> i32 {
@ -25,6 +25,12 @@ impl MyObject {
**提示:** 每个`#[Object]`需要一个唯一的名称,即使在一个`MergedObject`内,所以确保每个对象有单独的名称。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { a: i32 }
# #[derive(SimpleObject)]
# struct Movie { a: i32 }
#[derive(Default)]
struct UserQuery;
@ -64,13 +70,18 @@ let schema = Schema::new(
像合并对象一样,每个订阅块都需要一个唯一的名称。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# use futures_util::stream::{Stream};
# #[derive(Default,SimpleObject)]
# struct Query { a: i32 }
#[derive(Default)]
struct Subscription1;
#[Subscription]
impl Subscription1 {
async fn events1(&self) -> impl Stream<Item = i32> {
futures::stream::iter(0..10)
futures_util::stream::iter(0..10)
}
}
@ -80,7 +91,7 @@ struct Subscription2;
#[Subscription]
impl Subscription2 {
async fn events2(&self) -> impl Stream<Item = i32> {
futures::stream::iter(10..20)
futures_util::stream::iter(10..20)
}
}

View File

@ -5,7 +5,10 @@
查询根对象是一个GraphQL对象定义类似其它对象。查询对象的所有字段Resolver函数是并发执行的。
```rust
# extern crate async_graphql;
use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { a: i32 }
struct Query;
@ -13,6 +16,7 @@ struct Query;
impl Query {
async fn user(&self, username: String) -> Result<Option<User>> {
// 在数据库中查找用户
# todo!()
}
}
@ -25,6 +29,7 @@ impl Query {
下面的变更根对象提供用户注册和登录操作:
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Mutation;
@ -33,11 +38,12 @@ struct Mutation;
impl Mutation {
async fn signup(&self, username: String, password: String) -> Result<bool> {
// 用户注册
# todo!()
}
async fn login(&self, username: String, password: String) -> Result<String> {
// 用户登录并生成token
# todo!()
}
}
```

View File

@ -19,6 +19,7 @@ Async-graphql实现了常用数据类型到GraphQL类型的映射例如`i32`,
下面是一个简单的例子,我们只提供一个查询,返回`a`和`b`的和。
```rust
# extern crate async_graphql;
use async_graphql::*;
struct Query;
@ -38,13 +39,23 @@ impl Query {
在我们这个例子里面只有Query没有Mutation和Subscription所以我们用`EmptyMutation`和`EmptySubscription`来创建Schema然后调用`Schema::execute`来执行查询。
```rust
# extern crate async_graphql;
# use async_graphql::*;
#
# struct Query;
# #[Object]
# impl Query {
# async fn version(&self) -> &str { "1.0" }
# }
# async fn other() {
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let res = schema.execute("{ add(a: 10, b: 20) }").await;
# }
```
## 把查询结果输出为JSON
```rust
```rust,ignore
let json = serde_json::to_string(&res);
```

View File

@ -5,6 +5,12 @@
下面的例子订阅一个整数流,它每秒产生一个整数,参数`step`指定了整数的步长默认为1。
```rust
# extern crate async_graphql;
# use std::time::Duration;
# use async_graphql::futures_util::stream::Stream;
# use async_graphql::futures_util::StreamExt;
# extern crate tokio_stream;
# extern crate tokio;
use async_graphql::*;
struct Subscription;

View File

@ -3,6 +3,7 @@
默认情况下,所有类型,字段在内省中都是可见的。但可能你希望根据不同的用户来隐藏一些信息,避免引起不必要的误会。你可以在类型或者字段上添加`visible`属性来做到。
```rust
# extern crate async_graphql;
use async_graphql::*;
#[derive(SimpleObject)]
@ -19,7 +20,7 @@ struct MyObj {
c: i32,
}
#[derive(Enum)]
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
enum MyEnum {
// 这个项目将在内省中可见
A,