当前位置:网站首页>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
边栏推荐
- mysql函数获取全路径
- Pytorch learning (III)
- 开发那些事儿:如何在视频中添加文字水印?
- sqlserver SQL Server Management Studio和Transact-SQL创建账户、创建访问指定数据库的只读用户
- MySQL transaction concurrency and mvcc mechanism
- At present, the big guys are joining the two streams of flinksql, cdcmysql and Kafka, and the results are put into MySQL or KA
- Lenovo Yoga 27 2022, full upgrade of super configuration
- GameFi链游系统开发NFT技术
- Evolution of screen display technology
- Construction and practice of full stack code test coverage and use case discovery system
猜你喜欢

Infineon - GTM architecture -generic timer module

The cloud native landing practice of using rainbow for Tuowei information

ForkJoinPool

Opencv data type code table dtype

屏幕显示技术进化史

华兴证券:混合云原生架构下的 Kitex 实践

System integration project management engineer certification high frequency examination site: prepare project scope management plan

Redis入门到精通01
![Delete duplicate elements in the sorting linked list ii[unified operation of linked list nodes --dummyhead]](/img/dd/7df8f11333125290b4b30183cfff64.png)
Delete duplicate elements in the sorting linked list ii[unified operation of linked list nodes --dummyhead]

opencv数据类型代码表 dtype
随机推荐
服务器之间传文件夹,文件夹内容为空
slice
Swin-transformer --relative positional Bias
Tensorflow2 ten must know for deep learning
System integration project management engineer certification high frequency examination site: prepare project scope management plan
ForkJoinPool
基于 actix、async-graphql、rbatis、pgsql/mysql 构建 GraphQL 服务(4)-变更服务
【TiDB】TiCDC canal_ Practical application of JSON
Troubleshooting MySQL for update deadlock
Regular expressions (regular matching)
领导:谁再用 Redis 过期监听实现关闭订单,立马滚蛋!
sqlserver SQL Server Management Studio和Transact-SQL创建账户、创建访问指定数据库的只读用户
Four tips tell you how to use SMS to promote business sales?
MRO industrial products procurement management system: enable MRO enterprise procurement nodes to build a new digital procurement system
「经验」爬虫在工作中的实战应用『实现篇』
《所谓情商高,就是会说话》读书笔记
删除排序链表中的重复元素 II[链表节点统一操作--dummyHead]
Redis beginner to master 01
教你30分钟快速搭建直播间
Ambient light and micro distance detection system based on stm32f1