pub fn make_graphql_filter<S, Query, Mutation, Subscription, CtxT, CtxErr>(
schema: impl Into<Arc<RootNode<'static, Query, Mutation, Subscription, S>>>,
context_extractor: impl Filter<Extract = (CtxT,), Error = CtxErr> + Send + Sync + 'static,
) -> impl Filter<Extract = (Response,), Error = Rejection> + Clone + Sendwhere
Query: GraphQLTypeAsync<S, Context = CtxT> + Send + 'static,
Query::TypeInfo: Send + Sync,
Mutation: GraphQLTypeAsync<S, Context = CtxT> + Send + 'static,
Mutation::TypeInfo: Send + Sync,
Subscription: GraphQLSubscriptionType<S, Context = CtxT> + Send + 'static,
Subscription::TypeInfo: Send + Sync,
CtxT: Send + Sync + 'static,
CtxErr: Into<Rejection>,
S: ScalarValue + Send + Sync + 'static,
Expand description
Makes a Filter
for handling GraphQL queries/mutations.
The schema
argument is your juniper
schema.
The context_extractor
argument should be a Filter
that provides the GraphQL context,
required by the schema
.
§Example
type UserId = String;
struct AppState(Vec<i64>);
struct ExampleContext(Arc<AppState>, UserId);
struct QueryRoot;
#[graphql_object(context = ExampleContext)]
impl QueryRoot {
fn say_hello(context: &ExampleContext) -> String {
format!(
"good morning {}, the app state is {:?}",
context.1,
context.0,
)
}
}
let schema = RootNode::new(QueryRoot, EmptyMutation::new(), EmptySubscription::new());
let app_state = Arc::new(AppState(vec![3, 4, 5]));
let app_state = warp::any().map(move || app_state.clone());
let context_extractor = warp::any()
.and(warp::header::<String>("authorization"))
.and(app_state)
.map(|auth_header: String, app_state: Arc<AppState>| {
let user_id = auth_header; // we believe them
ExampleContext(app_state, user_id)
});
let graphql_endpoint = warp::path("graphql")
.and(make_graphql_filter(schema, context_extractor));
§Fallible context_extractor
WARNING: In case the
context_extractor
is fallible (e.g. implementsFilter
<Error =
Rejection
>
), it’s error should be handled viaFilter::recover()
to fails fast and avoid switching to otherFilter
s branches, becauseRejection
doesn’t mean to abort the whole request, but rather to say that aFilter
couldn’t fulfill its preconditions.
#[derive(Clone, Copy, Debug)]
struct NotAuthorized;
impl warp::reject::Reject for NotAuthorized {}
impl warp::Reply for NotAuthorized {
fn into_response(self) -> warp::reply::Response {
http::StatusCode::FORBIDDEN.into_response()
}
}
let schema = RootNode::new(QueryRoot, EmptyMutation::new(), EmptySubscription::new());
let app_state = Arc::new(AppState(vec![3, 4, 5]));
let app_state = warp::any().map(move || app_state.clone());
let context_extractor = warp::any()
.and(warp::header::<String>("authorization"))
.and(app_state)
.and_then(|auth_header: String, app_state: Arc<AppState>| async move {
if auth_header == "correct" {
Ok(ExampleContext(app_state, auth_header))
} else {
Err(warp::reject::custom(NotAuthorized))
}
});
let graphql_endpoint = warp::path("graphql")
.and(make_graphql_filter(schema, context_extractor))
.recover(|rejection: warp::reject::Rejection| async move {
rejection
.find::<NotAuthorized>()
.map(|e| e.into_response())
.ok_or(rejection)
});