mirror of
https://github.com/actix/examples
synced 2024-11-27 16:02:57 +01:00
GraphQL Example with Juniper (#181)
* actix-graphql-demo added to examples * Add actix-graphql-demo to the cargo.toml as a member
This commit is contained in:
parent
7be109ee61
commit
1f434406f3
@ -1,6 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"./",
|
"./",
|
||||||
|
"actix-graphql-demo",
|
||||||
"actix_redis",
|
"actix_redis",
|
||||||
"actix_todo",
|
"actix_todo",
|
||||||
"async_db",
|
"async_db",
|
||||||
|
1
actix-graphql-demo/.env.example
Normal file
1
actix-graphql-demo/.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
DATABASE_URL=mysql://user:password@127.0.0.1/dbname
|
26
actix-graphql-demo/Cargo.toml
Normal file
26
actix-graphql-demo/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "actix-graphql-demo"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Dwi Sulfahnur <hello@dwisulfahnur.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
futures = "0.1"
|
||||||
|
|
||||||
|
juniper = "0.13"
|
||||||
|
|
||||||
|
mysql = "16.1.0"
|
||||||
|
r2d2 = "0.8"
|
||||||
|
r2d2_mysql = "16.0"
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.6.2"
|
||||||
|
dotenv = "0.9"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
|
||||||
|
uuid = {"version" = "0.7", "features" = ["serde", "v4"]}
|
28
actix-graphql-demo/README.md
Normal file
28
actix-graphql-demo/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# actix-graphql-demo
|
||||||
|
|
||||||
|
GraphQL Implementation in Rust using Actix, Juniper, and Mysql as Database
|
||||||
|
|
||||||
|
# Prerequites
|
||||||
|
- Rust Installed
|
||||||
|
- MySql as Database
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
|
||||||
|
Create a new database for this project, and import the existing database schema has been provided named ```mysql-schema.sql```.
|
||||||
|
|
||||||
|
Create ```.env``` file on the root directory of this project and set environment variable named ```DATABASE_URL```, the example file has been provided named ```.env.example```, you can see the format on there.
|
||||||
|
|
||||||
|
# Run
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# go to the root dir
|
||||||
|
cd actix-graphql-demo
|
||||||
|
|
||||||
|
# Run
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
### GraphQL Playground
|
||||||
|
|
||||||
|
http://127.0.0.1:8080/graphiql
|
77
actix-graphql-demo/mysql-schema.sql
Normal file
77
actix-graphql-demo/mysql-schema.sql
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
-- MySQL dump 10.13 Distrib 8.0.16, for osx10.14 (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: 127.0.0.1 Database: rust_graphql
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 8.0.15
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
SET NAMES utf8mb4 ;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `product`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `product`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
SET character_set_client = utf8mb4 ;
|
||||||
|
CREATE TABLE `product` (
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`user_id` varchar(255) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`price` decimal(10,0) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `product_fk0` (`user_id`),
|
||||||
|
CONSTRAINT `product_fk0` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `product`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `product` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `product` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `product` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `user`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `user`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
SET character_set_client = utf8mb4 ;
|
||||||
|
CREATE TABLE `user` (
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `email` (`email`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `user`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `user` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `user` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `user` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
13
actix-graphql-demo/src/db.rs
Normal file
13
actix-graphql-demo/src/db.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use r2d2;
|
||||||
|
use r2d2_mysql::mysql::{Opts, OptsBuilder};
|
||||||
|
use r2d2_mysql::MysqlConnectionManager;
|
||||||
|
|
||||||
|
pub type Pool = r2d2::Pool<MysqlConnectionManager>;
|
||||||
|
|
||||||
|
pub fn get_db_pool() -> Pool {
|
||||||
|
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
let opts = Opts::from_url(&db_url).unwrap();
|
||||||
|
let builder = OptsBuilder::from_opts(opts);
|
||||||
|
let manager = MysqlConnectionManager::new(builder);
|
||||||
|
r2d2::Pool::new(manager).expect("Failed to create DB Pool")
|
||||||
|
}
|
44
actix-graphql-demo/src/handlers.rs
Normal file
44
actix-graphql-demo/src/handlers.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use actix_web::{Error, HttpResponse, web};
|
||||||
|
use futures::Future;
|
||||||
|
use juniper::http::graphiql::graphiql_source;
|
||||||
|
use juniper::http::GraphQLRequest;
|
||||||
|
|
||||||
|
use crate::db::Pool;
|
||||||
|
use crate::schemas::root::{Context, create_schema, Schema};
|
||||||
|
|
||||||
|
pub fn graphql(
|
||||||
|
pool: web::Data<Pool>,
|
||||||
|
schema: web::Data<Arc<Schema>>,
|
||||||
|
data: web::Json<GraphQLRequest>,
|
||||||
|
) -> impl Future<Item=HttpResponse, Error=Error> {
|
||||||
|
let ctx = Context {
|
||||||
|
dbpool: pool.get_ref().to_owned(),
|
||||||
|
};
|
||||||
|
web::block(move || {
|
||||||
|
let res = data.execute(&schema, &ctx);
|
||||||
|
Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
|
||||||
|
})
|
||||||
|
.map_err(Error::from)
|
||||||
|
.and_then(|res| {
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.content_type("application/json")
|
||||||
|
.body(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn graphql_playground() -> HttpResponse {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type("text/html; charset=utf-8")
|
||||||
|
.body(graphiql_source("/graphql"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn register(config: &mut web::ServiceConfig) {
|
||||||
|
let schema = std::sync::Arc::new(create_schema());
|
||||||
|
config
|
||||||
|
.data(schema)
|
||||||
|
.route("/graphql", web::post().to_async(graphql))
|
||||||
|
.route("/graphiql", web::get().to(graphql_playground));
|
||||||
|
}
|
31
actix-graphql-demo/src/main.rs
Normal file
31
actix-graphql-demo/src/main.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate juniper;
|
||||||
|
extern crate r2d2;
|
||||||
|
extern crate r2d2_mysql;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use actix_web::{App, HttpServer, middleware, web};
|
||||||
|
|
||||||
|
use crate::db::get_db_pool;
|
||||||
|
use crate::handlers::{register};
|
||||||
|
|
||||||
|
mod handlers;
|
||||||
|
mod schemas;
|
||||||
|
mod db;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
dotenv::dotenv().ok();
|
||||||
|
std::env::set_var("RUST_LOG", "actix_web=info,info");
|
||||||
|
env_logger::init();
|
||||||
|
let pool = get_db_pool();
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.data(pool.clone())
|
||||||
|
.wrap(middleware::Logger::default())
|
||||||
|
.configure(register)
|
||||||
|
.default_service(web::to(|| "404"))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8080")?
|
||||||
|
.run()
|
||||||
|
}
|
3
actix-graphql-demo/src/schemas/mod.rs
Normal file
3
actix-graphql-demo/src/schemas/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod root;
|
||||||
|
pub mod user;
|
||||||
|
pub mod product;
|
52
actix-graphql-demo/src/schemas/product.rs
Normal file
52
actix-graphql-demo/src/schemas/product.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use juniper;
|
||||||
|
use mysql::{Error as DBError, from_row, params, Row};
|
||||||
|
|
||||||
|
use crate::schemas::root::Context;
|
||||||
|
use crate::schemas::user::User;
|
||||||
|
|
||||||
|
/// Product
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Product {
|
||||||
|
pub id: String,
|
||||||
|
pub user_id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub price: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[juniper::object(Context = Context)]
|
||||||
|
impl Product {
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
fn user_id(&self) -> &str {
|
||||||
|
&self.user_id
|
||||||
|
}
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
fn price(&self) -> f64 {
|
||||||
|
self.price
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user(&self, context: &Context) -> Option<User> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let user: Result<Option<Row>, DBError> = conn.first_exec(
|
||||||
|
"SELECT * FROM user WHERE id=:id",
|
||||||
|
params! {"id" => &self.user_id},
|
||||||
|
);
|
||||||
|
if let Err(err) = user {
|
||||||
|
None
|
||||||
|
}else{
|
||||||
|
let (id, name, email) = from_row(user.unwrap().unwrap());
|
||||||
|
Some(User { id, name, email })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
#[graphql(description = "Product Input")]
|
||||||
|
pub struct ProductInput {
|
||||||
|
pub user_id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub price: f64,
|
||||||
|
}
|
165
actix-graphql-demo/src/schemas/root.rs
Normal file
165
actix-graphql-demo/src/schemas/root.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
use juniper::{FieldError, FieldResult, RootNode};
|
||||||
|
use juniper;
|
||||||
|
use mysql::{Error as DBError, from_row, params, Row};
|
||||||
|
|
||||||
|
use crate::db::Pool;
|
||||||
|
|
||||||
|
use super::product::{Product, ProductInput};
|
||||||
|
use super::user::{User, UserInput};
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
pub dbpool: Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl juniper::Context for Context {}
|
||||||
|
|
||||||
|
pub struct QueryRoot;
|
||||||
|
|
||||||
|
#[juniper::object(Context = Context)]
|
||||||
|
impl QueryRoot {
|
||||||
|
#[graphql(description = "List of all users")]
|
||||||
|
fn users(context: &Context) -> FieldResult<Vec<User>> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let users = conn.prep_exec("select * from user", ())
|
||||||
|
.map(|result| {
|
||||||
|
result.map(|x| x.unwrap()).map(|mut row| {
|
||||||
|
let (id, name, email) = from_row(row);
|
||||||
|
User { id, name, email }
|
||||||
|
}).collect()
|
||||||
|
}).unwrap();
|
||||||
|
Ok(users)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(description = "Get Single user reference by user ID")]
|
||||||
|
fn user(context: &Context, id: String) -> FieldResult<User> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
|
||||||
|
let user: Result<Option<Row>, DBError> = conn.first_exec(
|
||||||
|
"SELECT * FROM user WHERE id=:id",
|
||||||
|
params! {"id" => id},
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(err) = user {
|
||||||
|
return Err(FieldError::new(
|
||||||
|
"User Not Found",
|
||||||
|
graphql_value!({ "not_found": "user not found" }),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, name, email) = from_row(user.unwrap().unwrap());
|
||||||
|
Ok(User { id, name, email })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(description = "List of all users")]
|
||||||
|
fn products(context: &Context) -> FieldResult<Vec<Product>> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let products = conn.prep_exec("select * from product", ())
|
||||||
|
.map(|result| {
|
||||||
|
result.map(|x| x.unwrap()).map(|mut row| {
|
||||||
|
let (id, user_id, name, price) = from_row(row);
|
||||||
|
Product { id, user_id, name, price }
|
||||||
|
}).collect()
|
||||||
|
}).unwrap();
|
||||||
|
Ok(products)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(description = "Get Single user reference by user ID")]
|
||||||
|
fn product(context: &Context, id: String) -> FieldResult<Product> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let product: Result<Option<Row>, DBError> = conn.first_exec(
|
||||||
|
"SELECT * FROM user WHERE id=:id",
|
||||||
|
params! {"id" => id},
|
||||||
|
);
|
||||||
|
if let Err(err) = product {
|
||||||
|
return Err(FieldError::new(
|
||||||
|
"Product Not Found",
|
||||||
|
graphql_value!({ "not_found": "product not found" }),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, user_id, name, price) = from_row(product.unwrap().unwrap());
|
||||||
|
Ok(Product { id, user_id, name, price })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MutationRoot;
|
||||||
|
|
||||||
|
#[juniper::object(Context = Context)]
|
||||||
|
impl MutationRoot {
|
||||||
|
fn create_user(context: &Context, user: UserInput) -> FieldResult<User> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let new_id = uuid::Uuid::new_v4().to_simple().to_string();
|
||||||
|
|
||||||
|
let insert: Result<Option<Row>, DBError> = conn.first_exec(
|
||||||
|
"INSERT INTO user(id, name, email) VALUES(:id, :name, :email)",
|
||||||
|
params! {
|
||||||
|
"id" => &new_id.to_owned(),
|
||||||
|
"name" => &user.name.to_owned(),
|
||||||
|
"email" => &user.email.to_owned(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match insert {
|
||||||
|
Ok(opt_row) => {
|
||||||
|
Ok(User {
|
||||||
|
id: new_id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let msg = match err {
|
||||||
|
DBError::MySqlError(err) => err.message,
|
||||||
|
_ => "internal error".to_owned()
|
||||||
|
};
|
||||||
|
Err(FieldError::new(
|
||||||
|
"Failed to create new user",
|
||||||
|
graphql_value!({ "internal_error": msg }),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_product(context: &Context, product: ProductInput) -> FieldResult<Product> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let new_id = uuid::Uuid::new_v4().to_simple().to_string();
|
||||||
|
|
||||||
|
let insert: Result<Option<Row>, DBError> = conn.first_exec(
|
||||||
|
"INSERT INTO product(id, user_id, name, price) VALUES(:id, :user_id, :name, :price)",
|
||||||
|
params! {
|
||||||
|
"id" => &new_id.to_owned(),
|
||||||
|
"user_id" => &product.user_id.to_owned(),
|
||||||
|
"name" => &product.name.to_owned(),
|
||||||
|
"price" => &product.price.to_owned(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match insert {
|
||||||
|
Ok(opt_row) => {
|
||||||
|
Ok(Product {
|
||||||
|
id: new_id,
|
||||||
|
user_id: product.user_id,
|
||||||
|
name: product.name,
|
||||||
|
price: product.price,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let msg = match err {
|
||||||
|
DBError::MySqlError(err) => err.message,
|
||||||
|
_ => "internal error".to_owned()
|
||||||
|
};
|
||||||
|
Err(FieldError::new(
|
||||||
|
"Failed to create new product",
|
||||||
|
graphql_value!({ "internal_error": msg }),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Schema = RootNode<'static, QueryRoot, MutationRoot>;
|
||||||
|
|
||||||
|
pub fn create_schema() -> Schema {
|
||||||
|
Schema::new(QueryRoot, MutationRoot)
|
||||||
|
}
|
45
actix-graphql-demo/src/schemas/user.rs
Normal file
45
actix-graphql-demo/src/schemas/user.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use juniper;
|
||||||
|
use mysql::{from_row, params};
|
||||||
|
|
||||||
|
use crate::schemas::product::Product;
|
||||||
|
use crate::schemas::root::Context;
|
||||||
|
|
||||||
|
/// User
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct User {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
#[graphql(description = "User Input")]
|
||||||
|
pub struct UserInput {
|
||||||
|
pub name: String,
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[juniper::object(Context = Context)]
|
||||||
|
impl User {
|
||||||
|
fn id(&self) -> &str { &self.id }
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
fn email(&self) -> &str { &self.email }
|
||||||
|
|
||||||
|
fn products(&self, context: &Context) -> Vec<Product> {
|
||||||
|
let mut conn = context.dbpool.get().unwrap();
|
||||||
|
let products = conn.prep_exec(
|
||||||
|
"select * from product where user_id=:user_id", params! {
|
||||||
|
"user_id" => &self.id
|
||||||
|
})
|
||||||
|
.map(|result| {
|
||||||
|
result.map(|x| x.unwrap()).map(|mut row| {
|
||||||
|
let (id, user_id, name, price) = from_row(row);
|
||||||
|
Product { id, user_id, name, price }
|
||||||
|
}).collect()
|
||||||
|
}).unwrap();
|
||||||
|
products
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user