Repository: graphql-rust/juniper (language: rust)

Issue:
Return `Response<String>` instead of `Response<Body>` in hyper integration
**Is your feature request related to a problem? Please describe.**
Sometimes it's useful to inspect the response returned by `juniper_hyper::graphql`. For example, in case of an error I would like to debug-log the whole JSON response. This is currently very tricky. 

The function returns `Response<Body>` and the only way to read that `Body` is via the `Stream` trait (or the helper functions in `hyper::body` which also use that trait). But all those methods require `Body` by value or `&mut Body`. And in general that makes sense: `Body` can be many things and is not necessarily a byte buffer. 

The best I could come up with to log the body is:

```rust
let (parts, body) = response.into_parts();
let body = hyper::body::to_bytes(body).await
    .expect("could not read API response body (this should never happen)");
debug!("Response body of failed API request: {}", String::from_utf8_lossy(&body));
response = hyper::Response::from_parts(parts, body.into());
```

I need to completely unpack the response to then assemble it again. And the `await` and `expect` are both not nice here.

**Describe the solution you'd like**
From a quick glance at the `juniper_hyper` code, it seems like the `Body` is always created via `From<String>`. But `String` implements the trait `hyper::HttpBody` directly! So my suggestion is that all functions return `Response<String>`. With that, my logging would be as simple as:

```
debug!("Response body of failed API request: {}", response.body());
```



Discussion / hints:
@LukasKalbertodt makes sense! Would love to see a PR for this.

Fix patch:
diff --git a/juniper_hyper/CHANGELOG.md b/juniper_hyper/CHANGELOG.md
index e0e722ea6..435c134e2 100644
--- a/juniper_hyper/CHANGELOG.md
+++ b/juniper_hyper/CHANGELOG.md
@@ -11,6 +11,10 @@ All user visible changes to `juniper_hyper` crate will be documented in this fil
 ### BC Breaks
 
 - Switched to 0.16 version of [`juniper` crate].
+- Changed return type of all functions from `Response<Body>` to `Response<String>`. ([#1101], [#1096])
+
+[#1096]: /../../issues/1096
+[#1101]: /../../pull/1101
 
 
 
diff --git a/juniper_hyper/examples/hyper_server.rs b/juniper_hyper/examples/hyper_server.rs
index afa1d4e09..069e0c6fd 100644
--- a/juniper_hyper/examples/hyper_server.rs
+++ b/juniper_hyper/examples/hyper_server.rs
@@ -3,7 +3,7 @@ use std::{convert::Infallible, sync::Arc};
 use hyper::{
     server::Server,
     service::{make_service_fn, service_fn},
-    Body, Method, Response, StatusCode,
+    Method, Response, StatusCode,
 };
 use juniper::{
     tests::fixtures::starwars::schema::{Database, Query},
@@ -38,7 +38,7 @@ async fn main() {
                             juniper_hyper::graphql(root_node, ctx, req).await
                         }
                         _ => {
-                            let mut response = Response::new(Body::empty());
+                            let mut response = Response::new(String::new());
                             *response.status_mut() = StatusCode::NOT_FOUND;
                             response
                         }
diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs
index eb8dafe35..4836a66cb 100644
--- a/juniper_hyper/src/lib.rs
+++ b/juniper_hyper/src/lib.rs
@@ -17,7 +17,7 @@ pub async fn graphql_sync<CtxT, QueryT, MutationT, SubscriptionT, S>(
     root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
     context: Arc<CtxT>,
     req: Request<Body>,
-) -> Response<Body>
+) -> Response<String>
 where
     QueryT: GraphQLType<S, Context = CtxT>,
     QueryT::TypeInfo: Sync,
@@ -38,7 +38,7 @@ pub async fn graphql<CtxT, QueryT, MutationT, SubscriptionT, S>(
     root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
     context: Arc<CtxT>,
     req: Request<Body>,
-) -> Response<Body>
+) -> Response<String>
 where
     QueryT: GraphQLTypeAsync<S, Context = CtxT>,
     QueryT::TypeInfo: Sync,
@@ -57,7 +57,7 @@ where
 
 async fn parse_req<S: ScalarValue>(
     req: Request<Body>,
-) -> Result<GraphQLBatchRequest<S>, Response<Body>> {
+) -> Result<GraphQLBatchRequest<S>, Response<String>> {
     match *req.method() {
         Method::GET => parse_get_req(req),
         Method::POST => {
@@ -121,32 +121,27 @@ async fn parse_post_graphql_req<S: ScalarValue>(
 pub async fn graphiql(
     graphql_endpoint: &str,
     subscriptions_endpoint: Option<&str>,
-) -> Response<Body> {
+) -> Response<String> {
     let mut resp = new_html_response(StatusCode::OK);
     // XXX: is the call to graphiql_source blocking?
-    *resp.body_mut() = Body::from(juniper::http::graphiql::graphiql_source(
-        graphql_endpoint,
-        subscriptions_endpoint,
-    ));
+    *resp.body_mut() =
+        juniper::http::graphiql::graphiql_source(graphql_endpoint, subscriptions_endpoint);
     resp
 }
 
 pub async fn playground(
     graphql_endpoint: &str,
     subscriptions_endpoint: Option<&str>,
-) -> Response<Body> {
+) -> Response<String> {
     let mut resp = new_html_response(StatusCode::OK);
-    *resp.body_mut() = Body::from(juniper::http::playground::playground_source(
-        graphql_endpoint,
-        subscriptions_endpoint,
-    ));
+    *resp.body_mut() =
+        juniper::http::playground::playground_source(graphql_endpoint, subscriptions_endpoint);
     resp
 }
 
-fn render_error(err: GraphQLRequestError) -> Response<Body> {
-    let message = err.to_string();
+fn render_error(err: GraphQLRequestError) -> Response<String> {
     let mut resp = new_response(StatusCode::BAD_REQUEST);
-    *resp.body_mut() = Body::from(message);
+    *resp.body_mut() = err.to_string();
     resp
 }
 
@@ -154,7 +149,7 @@ async fn execute_request_sync<CtxT, QueryT, MutationT, SubscriptionT, S>(
     root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
     context: Arc<CtxT>,
     request: GraphQLBatchRequest<S>,
-) -> Response<Body>
+) -> Response<String>
 where
     QueryT: GraphQLType<S, Context = CtxT>,
     QueryT::TypeInfo: Sync,
@@ -166,7 +161,7 @@ where
     S: ScalarValue + Send + Sync,
 {
     let res = request.execute_sync(&*root_node, &context);
-    let body = Body::from(serde_json::to_string_pretty(&res).unwrap());
+    let body = serde_json::to_string_pretty(&res).unwrap();
     let code = if res.is_ok() {
         StatusCode::OK
     } else {
@@ -185,7 +180,7 @@ async fn execute_request<CtxT, QueryT, MutationT, SubscriptionT, S>(
     root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
     context: Arc<CtxT>,
     request: GraphQLBatchRequest<S>,
-) -> Response<Body>
+) -> Response<String>
 where
     QueryT: GraphQLTypeAsync<S, Context = CtxT>,
     QueryT::TypeInfo: Sync,
@@ -197,7 +192,7 @@ where
     S: ScalarValue + Send + Sync,
 {
     let res = request.execute(&*root_node, &context).await;
-    let body = Body::from(serde_json::to_string_pretty(&res).unwrap());
+    let body = serde_json::to_string_pretty(&res).unwrap();
     let code = if res.is_ok() {
         StatusCode::OK
     } else {
@@ -260,13 +255,13 @@ fn invalid_err(parameter_name: &str) -> GraphQLRequestError {
     ))
 }
 
-fn new_response(code: StatusCode) -> Response<Body> {
-    let mut r = Response::new(Body::empty());
+fn new_response(code: StatusCode) -> Response<String> {
+    let mut r = Response::new(String::new());
     *r.status_mut() = code;
     r
 }
 
-fn new_html_response(code: StatusCode) -> Response<Body> {
+fn new_html_response(code: StatusCode) -> Response<String> {
     let mut resp = new_response(code);
     resp.headers_mut().insert(
         header::CONTENT_TYPE,
@@ -313,7 +308,7 @@ mod tests {
     use hyper::{
         server::Server,
         service::{make_service_fn, service_fn},
-        Body, Method, Response, StatusCode,
+        Method, Response, StatusCode,
     };
     use juniper::{
         http::tests as http_tests,
@@ -409,7 +404,7 @@ mod tests {
                                 super::graphql(root_node, ctx, req).await
                             }
                         } else {
-                            let mut resp = Response::new(Body::empty());
+                            let mut resp = Response::new(String::new());
                             *resp.status_mut() = StatusCode::NOT_FOUND;
                             resp
                         })


Test patch: