当前位置:网站首页>Build graphql service based on Actix, async graphql, rbatis, pgsql/mysql (4) - change service
Build graphql service based on Actix, async graphql, rbatis, pgsql/mysql (4) - change service
2022-06-30 19:11:00 【ruonou. com】
front 3 In this article , We initialized and built the engineering structure , Selected the required crate, And successfully built GraphQL Query service , And the first refactoring of the code . This article , It's us doing GraphQL The last part of service backend development : Change service . After this article ,GraphQL The first stage of service backend development is over , And then we go on be based on Rust Of Web The front-end development . In this series , Adopt spiral thinking ,Web After the front-end infrastructure development , Go back to GraphQL Back end development improvements .
Small refactoring of custom table names
With reference based on actix-web + async-graphql + rbatis + postgresql / mysql Build asynchrony Rust GraphQL service (2) - A friend who inquires about the service article contacts the author , About... In the article user Table and User The problem of structure with the same name . Table names can be customized , And then in rbatis As specified in . such as , We'll take the user Change the name of the watch to users, that async-graphql The code for the simple object is as follows :
use serde::{Serialize, Deserialize};
#[rbatis::crud_enable(table_name:"users")]
#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct User {
pub id: i32,
pub email: String,
pub username: String,
pub cred: String,
}
#[async_graphql::ComplexObject]
impl User {
pub async fn from(&self) -> String {
let mut from = String::new();
from.push_str(&self.username);
from.push_str("<");
from.push_str(&self.email);
from.push_str(">");
from
}
}among
#[rbatis::crud_enable(table_name:"users")]Table name in , You don't have to wrap it in double quotation marks . Quotation marks for the sample code , Only the author is used to .
Dependency update
since be based on actix-web + async-graphql + rbatis + postgresql / mysql Build asynchrony Rust GraphQL service (3) - restructure after , It has been about half a month . In the past half a month , Active Rust Community ecology , Many updates have been made :Rust Version will be updated to 1.52.0,Rust 2021 Version will be released soon …… In this example project , Dependencies used async-graphql / async-graphql-actix-web( Thank Mr. Sun for his hard work , It is suggested that friends who read this article ,star Miss sun's async-graphql Warehouse )、rbatis etc. crate Have it all. 1-2 Upgrade of versions .
You can use cargo upgrade upgrade , Or just modify it Cargo.toml file , All use the latest version of dependencies crate:
[package]
name = "backend"
version = "0.1.0"
authors = [" Who am I ?"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "3.3.2"
dotenv = "0.15.0"
lazy_static = "1.4.0"
async-graphql = { version = "2.8.4", features = ["chrono"] }
async-graphql-actix-web = "2.8.4"
rbatis = { version = "1.8.84", default-features = false, features = ["mysql", "postgres"] }
serde = { version = "1.0.125", features = ["derive"] }In this series , about crate Dependence , take It is unnecessary to introduce Principles . With final completion , The total dependencies are about 56 individual .
Change service
Next , We develop GraphQL Change services for . Example , We use Model -> service -> Bus In order to develop . This order is not fixed , In actual development , You can adjust it according to your habits .
Definition NewUser Enter the object type
Here it is , We define a to be inserted users A structure in a collection , Include the corresponding fields , It as a async-graphql Medium Enter the object type . It should be noted that ,mysql or postgres in ,id It's an auto primary key , So there is no need to define this field .cred It is planned to use PBKDF2 Encrypt password (salt) And hash (hash) The authentication code after operation , Need to define , However, it is not necessary to fill in . therefore , Here we need to introduce a async-graphql Attribute tags in #[graphql(skip)], It means that this field will not be mapped to GraphQL.
The code is simpler , So we directly post users/models.rs File full code :
use serde::{Serialize, Deserialize};
#[rbatis::crud_enable(table_name:"users")]
#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct User {
pub id: i32,
pub email: String,
pub username: String,
pub cred: String,
}
#[async_graphql::ComplexObject]
impl User {
pub async fn from(&self) -> String {
let mut from = String::new();
from.push_str(&self.username);
from.push_str("<");
from.push_str(&self.email);
from.push_str(">");
from
}
}
#[rbatis::crud_enable(table_name:"users")]
#[derive(async_graphql::InputObject, Serialize, Deserialize, Clone, Debug)]
pub struct NewUser {
#[graphql(skip)]
pub id: i32,
pub email: String,
pub username: String,
#[graphql(skip)]
pub cred: String,
}Write service layer code , take NewUser Structure is inserted into the database
Service layer users/services.rs in , We just need to define a function , Is used to NewUser Structure insertion mysql/postgres database . We from GraphiQL/playground In order to get NewUser When the structure is , Because we used the tag #[graphql(skip)], therefore id、cred Fields do not map to GraphQL. about mysql/postgres Document database features ,id Is a self increasing field ;cred We set it to non empty , So write a fixed value for it . As this tutorial progresses , We will iterate to associate user specific values , Use PBKDF2 Encrypt password (salt) And hash (hash) The authentication code after operation .
meanwhile , Practical application , When inserting a user , We should set a user unique flag attribute , To determine whether this user already exists in the database . In this instance , We use email As the unique attribute of the user . therefore , We need to develop get_user_by_email service .
also , We will NewUser Structure insertion mysql/postgres After the database , The insert result should be returned . therefore , We also need to develop a system based on username perhaps email Query the user's GraphQL service . Because we have set email For the unique attribute of the user , So direct use get_user_by_email The query has been inserted into the user .
Service layer users/services.rs The complete code of the document is as follows :
use async_graphql::{Error, ErrorExtensions};
use rbatis::rbatis::Rbatis;
use rbatis::crud::CRUD;
use crate::util::constant::GqlResult;
use crate::users::models::{NewUser, User};
// Query all users
pub async fn all_users(my_pool: &Rbatis) -> GqlResult<Vec<User>> {
let users = my_pool.fetch_list::<User>("").await.unwrap();
if users.len() > 0 {
Ok(users)
} else {
Err(Error::new("1-all-users")
.extend_with(|_, e| e.set("details", "No records")))
}
}
// adopt email Get users
pub async fn get_user_by_email(
my_pool: &Rbatis,
email: &str,
) -> GqlResult<User> {
let email_wrapper = my_pool.new_wrapper().eq("email", email);
let user = my_pool.fetch_by_wrapper::<User>("", &email_wrapper).await;
if user.is_ok() {
Ok(user.unwrap())
} else {
Err(Error::new("email non-existent ")
.extend_with(|_, e| e.set("details", "1_EMAIL_NOT_EXIStS")))
}
}
// Insert a new user
pub async fn new_user(
my_pool: &Rbatis,
mut new_user: NewUser,
) -> GqlResult<User> {
new_user.email = new_user.email.to_lowercase();
if self::get_user_by_email(my_pool, &new_user.email).await.is_ok() {
Err(Error::new("email Already exists ")
.extend_with(|_, e| e.set("details", "1_EMAIL_EXIStS")))
} else {
new_user.cred =
"P38V7+1Q5sjuKvaZEXnXQqI9SiY6ZMisB8QfUOP91Ao=".to_string();
my_pool.save("", &new_user).await.expect(" Insert user There was an error with the data ");
self::get_user_by_email(my_pool, &new_user.email).await
}
}Add services to the service bus
The service bus corresponding to the query service is gql/queries.rs, Change the service bus corresponding to the service to gql/mutations.rs. up to now , We have never written a change service bus file gql/mutations.rs. Now? , We will new_user Change services and get_user_by_email Query services are added to the change and query service bus respectively .
Plus our inquiry service all_users service , The total number of service buses is 2 File ,3 A service .
Query service bus gql/queries.rs
use async_graphql::Context;
use rbatis::rbatis::Rbatis;
use crate::util::constant::GqlResult;
use crate::users::{self, models::User};
pub struct QueryRoot;
#[async_graphql::Object]
impl QueryRoot {
// Get all users
async fn all_users(&self, ctx: &Context<'_>) -> GqlResult<Vec<User>> {
let my_pool = ctx.data_unchecked::<Rbatis>();
users::services::all_users(my_pool).await
}
// according to email Get users
async fn get_user_by_email(
&self,
ctx: &Context<'_>,
email: String,
) -> GqlResult<User> {
let my_pool = ctx.data_unchecked::<Rbatis>();
users::services::get_user_by_email(my_pool, &email).await
}
}Change the service bus gql/mutations.rs
use async_graphql::Context;
use rbatis::rbatis::Rbatis;
use crate::users::{
self,
models::{NewUser, User},
};
use crate::util::constant::GqlResult;
pub struct MutationRoot;
#[async_graphql::Object]
impl MutationRoot {
// Insert a new user
async fn new_user(
&self,
ctx: &Context<'_>,
new_user: NewUser,
) -> GqlResult<User> {
let my_pool = ctx.data_unchecked::<Rbatis>();
users::services::new_user(my_pool, new_user).await
}
}First validation
Query service 、 All change services are coded , Let's verify the development results . adopt cargo run perhaps cargo watch Start the application , Browser input http://127.0.0.1:8080/v1i, open graphiql/playgound Interface .
If your configuration does not follow the tutorial , Please input the correct link according to your configuration , See your
.envFile configuration items .
however , If you pass graphiql/playgound Interface docs Tab view , Still, you can only see that there is a single one under the query service allUsers: [User!]!. This is because , In our previous tutorials , Just write the query service code , So the server Schema The build uses EmptyMutation. We need to change our own service bus gql/mutations.rs, Add to SchemaBuilder in .
It only involves gql/mod.rs file .
Add change service bus to SchemaBuilder
gql/mod.rs The complete code of the document is as follows :
pub mod mutations;
pub mod queries;
use actix_web::{web, HttpResponse, Result};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql::{EmptySubscription, Schema};
use async_graphql_actix_web::{Request, Response};
use crate::util::constant::CFG;
use crate::dbs::mysql::my_pool;
use crate::gql::{queries::QueryRoot, mutations::MutationRoot};
type ActixSchema = Schema<
queries::QueryRoot,
mutations::MutationRoot,
async_graphql::EmptySubscription,
>;
pub async fn build_schema() -> ActixSchema {
// obtain mysql After data pool , It can be added to :
// 1. As async-graphql The global data of ;
// 2. As actix-web Application data for , The advantage is that you can perform atomic operations ;
// 3. Use lazy-static.rs
let my_pool = my_pool().await;
// The root object for the query and Mutatio, and use EmptySubscription.
// Add global mysql pool in the schema object.
Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(my_pool)
.finish()
}
pub async fn graphql(schema: web::Data<ActixSchema>, req: Request) -> Response {
schema.execute(req.into_inner()).await.into()
}
pub async fn graphiql() -> Result<HttpResponse> {
Ok(HttpResponse::Ok().content_type("text/html; charset=utf-8").body(
playground_source(
GraphQLPlaygroundConfig::new(CFG.get("GQL_VER").unwrap())
.subscription_endpoint(CFG.get("GQL_VER").unwrap()),
),
))
}Okay, Be accomplished , We do the second verification .
Second validation
The opening method and precautions are the same as the first verification .
After normal startup , If you pass graphiql/playgound Interface docs Tab view , You will see that the list of query and change services has changed . As shown in the figure below :
Insert a new user ( Repeat insert )
Inserted newUser The data is ( Be careful ,GraphQL Automatically convert to hump naming ):
newUser: {
email: "[email protected]",
username: " Who am I "
}Insert... For the first time , The result will be inserted correctly :
{
"data": {
"newUser": {
"cred": "P38V7+1Q5sjuKvaZEXnXQqI9SiY6ZMisB8QfUOP91Ao=",
"email": "[email protected]",
"from": " Who am I <[email protected]>",
"id": 5,
"username": " Who am I "
}
}
} Repeat the insertion for the second time , because email Already exists , The error message defined in our development is returned :
{
"data": null,
"errors": [
{
"message": "email Already exists ",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"newUser"
],
"extensions": {
"details": "1_EMAIL_EXIStS"
}
}
]
}Please check your database by yourself , The target data has been inserted normally .
thus , Change service development is completed .
The source code warehouse of this instance is github, Welcome to improve together .
The next plan
After the change service development is completed , At the back end, we will come to a stage . The next chapter begins with , We develop the front end , Still use Rust Technology stack :actix-web、rhai、handlebars-rust、surf, as well as graphql_client.
This practice , We call it Rust The whole stack Development ;-)
Thank you for reading , Welcome to exchange . If you find Wrongly written characters , Please also send me a message
边栏推荐
- 传统微服务框架如何无缝过渡到服务网格 ASM
- Swin-transformer --relative positional Bias
- 基于UDP协议设计的大文件传输软件
- Is it safe to open a mobile stock account? Is it reliable?
- Entry node of link in linked list - linked list topic
- Electronic components bidding and purchasing Mall: optimize traditional purchasing business and speed up enterprise digital upgrading
- 云上“视界” 创新无限 | 2022阿里云直播峰会正式上线
- Go Redis连接池
- [community star selection] the 23rd issue of the July revision plan | bit by bit creation, converging into a tower! Huawei freebuses 4E and other cool gifts
- Courage to be hated: Adler's philosophy class: the father of self inspiration
猜你喜欢

TCP粘包问题
![[Collection - industry solutions] how to build a high-performance data acceleration and data editing platform](/img/56/9f3370eac60df182971607aa642dc2.jpg)
[Collection - industry solutions] how to build a high-performance data acceleration and data editing platform

Deep learning compiler understanding

20220528【聊聊假芯片】贪便宜往往吃大亏,盘点下那些假的内存卡和固态硬盘

Lenovo Yoga 27 2022, full upgrade of super configuration

What if the apple watch fails to power on? Apple watch can not boot solution!

TCP packet sticking problem

Small program container technology to promote the operation efficiency of the park

PHP uses queues to solve maze problems

PyTorch学习(三)
随机推荐
Evolution of screen display technology
NEON优化2:ARM优化高频指令总结
Coding officially entered Tencent conference application market!
《被讨厌的勇气:“自我启发之父”阿德勒的哲学课》
Redis入门到精通01
PyTorch学习(三)
华兴证券:混合云原生架构下的 Kitex 实践
PC wechat multi open
Classic problem of leetcode dynamic programming (I)
详解单例模式
链表中环的入口结点-链表专题
【合集- 行业解决方案】如何搭建高性能的数据加速与数据编排平台
MRO工业品采购管理系统:赋能MRO企业采购各节点,构建数字化采购新体系
Is it safe to open an account for goucai? Is it reliable?
mysql函数获取全路径
删除排序链表中的重复元素 II[链表节点统一操作--dummyHead]
com.alibaba.fastjson.JSONObject # toJSONString 消除循环引用
【TiDB】TiCDC canal_ Practical application of JSON
屏幕显示技术进化史
20220528【聊聊假芯片】贪便宜往往吃大亏,盘点下那些假的内存卡和固态硬盘