Below you can find the source code that allows to run following read and write graphql queries:
read data
query {users{id, name}}
write data
mutation {addUserIdName(id: "id1", name:"name1") {
id
}}
mutation {
addUser(userInput: {id: "id2", name: "name2"}) {
id
}
}
the server will generate following schema:
type Mutation {
addUserIdName(id: String!, name: String!): User!
addUser(userInput: UserInput!): User!
}
input UserInput {
id: String!
name: String!
}
type Query {
users: [User!]!
}
type User {
id: String!
name: String!
}
schema {
query: Query
mutation: Mutation
}
Complete source code, the data is stored in hash map:
use std::{collections::HashMap, convert::Infallible, sync::Arc};
use tokio::sync::RwLock;
#[macro_use]
extern crate juniper;
#[macro_use]
extern crate log;
use juniper::{EmptySubscription, RootNode};
struct Query;
struct Mutation;
#[derive(GraphQLInputObject)]
struct UserInput {
id: String,
name: String,
}
impl From<UserInput> for User {
fn from(user_input: UserInput) -> Self {
Self {
id: user_input.id,
name: user_input.name,
}
}
}
#[derive(Clone, GraphQLObject)]
struct User {
id: String,
name: String,
}
use hyper::{
server::Server,
service::{make_service_fn, service_fn},
Method, Response, StatusCode,
};
struct Context {
users: RwLock<HashMap<String, User>>,
}
impl juniper::Context for Context {}
#[graphql_object(context = Context)]
impl Query {
async fn users(context: &Context) -> Vec<User> {
let map = context.users.read().await;
map.values().cloned().collect()
}
}
#[graphql_object(context = Context)]
impl Mutation {
#[graphql(name = "addUserIdName")]
async fn add_user(context: &Context, id: String, name: String) -> User {
info!("create user by id and name");
let mut map = context.users.write().await;
let user: User = User { id: id, name: name };
map.insert(user.id.clone(), user.clone());
user
}
async fn add_user(context: &Context, user_input: UserInput) -> User {
info!("create user");
let mut map = context.users.write().await;
let user: User = user_input.into();
map.insert(user.id.clone(), user.clone());
user
}
}
use hyper::Body;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 3000).into();
let root_node = Arc::new(RootNode::new(
Query,
Mutation,
EmptySubscription::<Context>::new(),
));
println!("{}", root_node.as_schema_language());
let new_service = make_service_fn(move |_| {
let root_node = root_node.clone();
let context = Arc::new(Context {
users: RwLock::new(HashMap::new()),
});
let ctx = context.clone();
async {
Ok::<_, hyper::Error>(service_fn(move |req| {
let root_node = root_node.clone();
let ctx = ctx.clone();
async {
Ok::<_, Infallible>(match (req.method(), req.uri().path()) {
(&Method::GET, "/") => juniper_hyper::graphiql("/graphql", None).await,
(&Method::GET, "/graphql") | (&Method::POST, "/graphql") => {
juniper_hyper::graphql(root_node, ctx, req).await
}
_ => {
let mut response = Response::new(Body::empty());
*response.status_mut() = StatusCode::NOT_FOUND;
response
}
})
}
}))
}
});
let server = Server::bind(&addr).serve(new_service);
println!("Listening on http://{addr}");
if let Err(e) = server.await {
eprintln!("server error: {e}")
}
}
Run the sever:
RUST_LOG=info cargo r
Dependencies
[dependencies]
juniper = "0.15"
juniper_hyper = "0.8.0"
hyper = { version = "0.14", features = ["server", "runtime"] }
log = "0.4"
pretty_env_logger = "0.4"