async_graphql_poem/extractor.rs
1use async_graphql::http::MultipartOptions;
2use poem::{
3 FromRequest, Request, RequestBody, Result,
4 error::BadRequest,
5 http::{Method, header},
6};
7use tokio_util::compat::TokioAsyncReadCompatExt;
8
9/// An extractor for GraphQL request.
10///
11/// You can just use the extractor as in the example below, but I would
12/// recommend using the [`GraphQL`](crate::GraphQL) endpoint because it is
13/// easier to integrate.
14///
15/// # Example
16///
17/// ```
18/// use async_graphql::{EmptyMutation, EmptySubscription, Object, Schema};
19/// use async_graphql_poem::GraphQLRequest;
20/// use poem::{
21/// EndpointExt, Route, handler,
22/// middleware::AddData,
23/// post,
24/// web::{Data, Json},
25/// };
26///
27/// struct Query;
28///
29/// #[Object]
30/// impl Query {
31/// async fn value(&self) -> i32 {
32/// 100
33/// }
34/// }
35///
36/// type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;
37///
38/// #[handler]
39/// async fn index(req: GraphQLRequest, schema: Data<&MySchema>) -> Json<async_graphql::Response> {
40/// Json(schema.execute(req.0).await)
41/// }
42///
43/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
44/// let app = Route::new().at("/", post(index.with(AddData::new(schema))));
45/// ```
46pub struct GraphQLRequest(pub async_graphql::Request);
47
48impl<'a> FromRequest<'a> for GraphQLRequest {
49 async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result<Self> {
50 Ok(GraphQLRequest(
51 GraphQLBatchRequest::from_request(req, body)
52 .await?
53 .0
54 .into_single()
55 .map_err(BadRequest)?,
56 ))
57 }
58}
59
60/// An extractor for GraphQL batch request.
61pub struct GraphQLBatchRequest(pub async_graphql::BatchRequest);
62
63impl<'a> FromRequest<'a> for GraphQLBatchRequest {
64 async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result<Self> {
65 if req.method() == Method::GET {
66 let req =
67 async_graphql::http::parse_query_string(req.uri().query().unwrap_or_default())
68 .map_err(BadRequest)?;
69 Ok(Self(async_graphql::BatchRequest::Single(req)))
70 } else {
71 let content_type = req
72 .headers()
73 .get(header::CONTENT_TYPE)
74 .and_then(|value| value.to_str().ok())
75 .map(ToString::to_string);
76 Ok(Self(
77 async_graphql::http::receive_batch_body(
78 content_type,
79 body.take()?.into_async_read().compat(),
80 MultipartOptions::default(),
81 )
82 .await
83 .map_err(BadRequest)?,
84 ))
85 }
86 }
87}