Skip to content

Commit f498f59

Browse files
authored
Merge pull request #39 from tirr-c/example-graphql
Add simple GraphQL example
2 parents 6c2b9ef + 6688557 commit f498f59

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ features = ["compat"]
2626
version = "0.3.0-alpha.9"
2727

2828
[dev-dependencies]
29+
juniper = "0.10.0"

examples/graphql.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This example uses Juniper to process GraphQL requests. If you're not familiar with Juniper, take
2+
// a look at [the Juniper book].
3+
//
4+
// [the Juniper book]: https://graphql-rust.github.io/
5+
6+
#![feature(async_await, futures_api)]
7+
8+
use http::status::StatusCode;
9+
use juniper::graphql_object;
10+
use std::sync::{atomic, Arc};
11+
use tide::{body, App, AppData, Response};
12+
13+
// First, we define `Context` that holds accumulator state. This is accessible as App data in
14+
// Tide, and as executor context in Juniper.
15+
#[derive(Clone, Default)]
16+
struct Context(Arc<atomic::AtomicIsize>);
17+
18+
impl juniper::Context for Context {}
19+
20+
// We define `Query` unit struct here. GraphQL queries will refer to this struct. The struct itself
21+
// doesn't have any associated data (and there's no need to do so), but instead it exposes the
22+
// accumulator state from the context.
23+
struct Query;
24+
25+
graphql_object!(Query: Context |&self| {
26+
// GraphQL integers are signed and 32 bits long.
27+
field accumulator(&executor) -> i32 as "Current value of the accumulator" {
28+
executor.context().0.load(atomic::Ordering::Relaxed) as i32
29+
}
30+
});
31+
32+
// Here is `Mutation` unit struct. GraphQL mutations will refer to this struct. This is similar to
33+
// `Query`, but it provides the way to "mutate" the accumulator state.
34+
struct Mutation;
35+
36+
graphql_object!(Mutation: Context |&self| {
37+
field add(&executor, by: i32) -> i32 as "Add given value to the accumulator." {
38+
executor.context().0.fetch_add(by as isize, atomic::Ordering::Relaxed) as i32 + by
39+
}
40+
});
41+
42+
// Adding `Query` and `Mutation` together we get `Schema`, which describes, well, the whole GraphQL
43+
// schema.
44+
type Schema = juniper::RootNode<'static, Query, Mutation>;
45+
46+
// Finally, we'll bridge between Tide and Juniper. `GraphQLRequest` from Juniper implements
47+
// `Deserialize`, so we use `Json` extractor to deserialize the request body.
48+
async fn handle_graphql(
49+
ctx: AppData<Context>,
50+
query: body::Json<juniper::http::GraphQLRequest>,
51+
) -> Result<Response, StatusCode> {
52+
let request = query.0;
53+
let response = request.execute(&Schema::new(Query, Mutation), &ctx);
54+
55+
// `response` has the lifetime of `request`, so we can't use `IntoResponse` directly.
56+
let body_vec = serde_json::to_vec(&response)
57+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
58+
59+
http::Response::builder()
60+
.status(if response.is_ok() { StatusCode::OK } else { StatusCode::BAD_REQUEST })
61+
.header("Content-Type", "application/json")
62+
.body(body::Body::from(body_vec))
63+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
64+
}
65+
66+
fn main() {
67+
let mut app = App::new(Context::default());
68+
69+
app.at("/graphql").post(handle_graphql);
70+
71+
app.serve("127.0.0.1:7878");
72+
}

0 commit comments

Comments
 (0)