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}