First Commit
This commit is contained in:
commit
bab592edf1
|
@ -0,0 +1,4 @@
|
|||
HOST=0.0.0.0
|
||||
PORT=8080
|
||||
DATABASE_URL="postgres://user:password@server:port/database"
|
||||
RUST_LOG=info,actix=info
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.env
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "axum-async-graphql"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-graphql = { version = "7.0.5", default-features = false, features = [
|
||||
"chrono",
|
||||
"uuid",
|
||||
'dataloader',
|
||||
'dynamic-schema',
|
||||
'graphiql',
|
||||
'playground',
|
||||
] }
|
||||
uuid = { version = "1.8.0", features = ["serde", "v4"] }
|
||||
axum = { version = "0.7.5", features = ["macros"] }
|
||||
sqlx = { version = "0.7.4", features = [
|
||||
"runtime-tokio-native-tls",
|
||||
"uuid",
|
||||
"postgres",
|
||||
"chrono",
|
||||
"macros",
|
||||
] }
|
||||
anyhow = "1.0.53"
|
||||
async-graphql-axum = "7.0.5"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.14"
|
||||
serde = "1.0.136"
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
|
|
@ -0,0 +1,2 @@
|
|||
-- Add down migration script here
|
||||
DROP TABLE typen;
|
|
@ -0,0 +1,5 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE typen (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR NOT NULL
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- Add down migration script here
|
||||
DROP TABLE hersteller;
|
|
@ -0,0 +1,5 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE hersteller (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR NOT NULL
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- Add down migration script here
|
||||
DROP TABLE modelle;
|
|
@ -0,0 +1,9 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE modelle (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
typ_id SERIAL,
|
||||
hersteller_id SERIAL,
|
||||
CONSTRAINT fk_typ FOREIGN KEY(typ_id) REFERENCES typen(id),
|
||||
CONSTRAINT fk_hersteller FOREIGN KEY (hersteller_id) REFERENCES hersteller(id)
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
extern crate log;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_graphql::{http::GraphiQLSource, EmptySubscription, Schema};
|
||||
use async_graphql_axum::GraphQL;
|
||||
use axum::{
|
||||
response::{self, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use dotenv::dotenv;
|
||||
use mutations::Mutation;
|
||||
use queries::Query;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::env;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
mod models;
|
||||
mod mutations;
|
||||
mod queries;
|
||||
|
||||
async fn ping() -> String {
|
||||
format!(
|
||||
"I am healthy: {} v{}",
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
env!("CARGO_PKG_VERSION")
|
||||
)
|
||||
}
|
||||
|
||||
async fn graphiql() -> impl IntoResponse {
|
||||
response::Html(GraphiQLSource::build().endpoint("/").finish())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
dotenv().ok();
|
||||
env_logger::init();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set");
|
||||
let db_pool = PgPool::connect(&database_url).await?;
|
||||
|
||||
let schema = Schema::build(Query::default(), Mutation::default(), EmptySubscription)
|
||||
.data(db_pool)
|
||||
.finish();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/ping", get(ping))
|
||||
.route("/", get(graphiql).post_service(GraphQL::new(schema)));
|
||||
|
||||
println!("GraphiQL IDE: http://localhost:8000");
|
||||
|
||||
axum::serve(TcpListener::bind("127.0.0.1:8000").await?, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
use anyhow::Result;
|
||||
use async_graphql::SimpleObject;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
||||
#[derive(SimpleObject, FromRow, Deserialize, Serialize)]
|
||||
pub struct Hersteller {
|
||||
/// Die Datenbank-ID
|
||||
pub id: i32,
|
||||
|
||||
/// Der Name eines Herstellers
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Hersteller {
|
||||
pub async fn create(pool: &PgPool, name: &str) -> Result<Hersteller> {
|
||||
let row = sqlx::query!(
|
||||
"INSERT INTO hersteller(name) VALUES ($1) RETURNING id",
|
||||
name
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(Hersteller {
|
||||
id: row.id,
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read_one(pool: &PgPool, id: &i32) -> Result<Hersteller> {
|
||||
let row = sqlx::query_as!(Hersteller, "SELECT * FROM hersteller WHERE id = $1", id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn read_all(pool: &PgPool) -> Result<Vec<Hersteller>> {
|
||||
let rows = sqlx::query_as!(Hersteller, "SELECT id, name FROM hersteller")
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub async fn update(pool: &PgPool, id: &i32, name: &str) -> Result<Hersteller> {
|
||||
sqlx::query!("UPDATE hersteller SET name=$1 WHERE id = $2", name, id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
let t = Hersteller::read_one(pool, id).await?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, id: &i32) -> Result<()> {
|
||||
sqlx::query!("DELETE FROM hersteller WHERE id = $1", id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod hersteller;
|
||||
pub mod modell;
|
||||
pub mod typ;
|
|
@ -0,0 +1,104 @@
|
|||
use anyhow::Result;
|
||||
use async_graphql::SimpleObject;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
||||
#[derive(SimpleObject, FromRow, Deserialize, Serialize)]
|
||||
pub struct Modell {
|
||||
/// Die Datenbank-ID
|
||||
pub id: i32,
|
||||
|
||||
/// Der Typ
|
||||
typ_id: i32,
|
||||
|
||||
/// Der Hersteller
|
||||
hersteller_id: i32,
|
||||
|
||||
/**
|
||||
* Der Name eines Modells
|
||||
* Das ist ein Zusatz
|
||||
*/
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Modell {
|
||||
pub async fn create(
|
||||
pool: &PgPool,
|
||||
typ_id: i32,
|
||||
hersteller_id: i32,
|
||||
name: &str,
|
||||
) -> Result<Modell> {
|
||||
let row = sqlx::query!(
|
||||
"INSERT INTO modelle(typ_id, hersteller_id, name) VALUES ($1, $2, $3) RETURNING id",
|
||||
typ_id,
|
||||
hersteller_id,
|
||||
name
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(Modell {
|
||||
id: row.id,
|
||||
typ_id,
|
||||
hersteller_id,
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read_one(pool: &PgPool, id: &i32) -> Result<Modell> {
|
||||
let row = sqlx::query_as!(Modell, "SELECT * FROM modelle WHERE id = $1", id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn read_all(pool: &PgPool) -> Result<Vec<Modell>> {
|
||||
let rows = sqlx::query_as!(
|
||||
Modell,
|
||||
"SELECT id, name, hersteller_id, typ_id FROM modelle"
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub async fn read_by_typ(pool: &PgPool, typ_id: &i32) -> Result<Vec<Modell>> {
|
||||
let row = sqlx::query_as!(Modell, "SELECT * FROM modelle WHERE typ_id = $1", typ_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn read_by_hersteller(pool: &PgPool, hersteller_id: &i32) -> Result<Vec<Modell>> {
|
||||
let row = sqlx::query_as!(
|
||||
Modell,
|
||||
"SELECT * FROM modelle WHERE hersteller_id = $1",
|
||||
hersteller_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn update(pool: &PgPool, id: &i32, name: &str) -> Result<Modell> {
|
||||
sqlx::query!("UPDATE modelle SET name=$1 WHERE id = $2", name, id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
let t = Modell::read_one(pool, id).await?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, id: &i32) -> Result<()> {
|
||||
sqlx::query!("DELETE FROM modelle WHERE id = $1", id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
use anyhow::Result;
|
||||
use async_graphql::SimpleObject;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
||||
#[derive(SimpleObject, FromRow, Deserialize, Serialize)]
|
||||
pub struct Typ {
|
||||
/// Die Datenbank-ID
|
||||
pub id: i32,
|
||||
|
||||
/// Der Name eines Typs
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Typ {
|
||||
pub async fn create(pool: &PgPool, name: &str) -> Result<Typ> {
|
||||
let row = sqlx::query!("INSERT INTO typen(name) VALUES ($1) RETURNING id", name)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(Typ {
|
||||
id: row.id,
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read_one(pool: &PgPool, id: &i32) -> Result<Typ> {
|
||||
let row = sqlx::query_as!(Typ, "SELECT * FROM typen WHERE id = $1", id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn read_all(pool: &PgPool) -> Result<Vec<Typ>> {
|
||||
let rows = sqlx::query_as!(Typ, "SELECT id, name FROM typen")
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub async fn update(pool: &PgPool, id: &i32, name: &str) -> Result<Typ> {
|
||||
sqlx::query!("UPDATE typen SET name=$1 WHERE id = $2", name, id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
let t = Typ::read_one(pool, id).await?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, id: &i32) -> Result<()> {
|
||||
sqlx::query!("DELETE FROM typen WHERE id = $1", id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
use crate::models::hersteller::Hersteller;
|
||||
use async_graphql::{Context, FieldResult};
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HerstellerMutation;
|
||||
|
||||
#[async_graphql::Object]
|
||||
impl HerstellerMutation {
|
||||
async fn create_hersteller(&self, ctx: &Context<'_>, name: String) -> FieldResult<Hersteller> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let row = Hersteller::create(pool, &name).await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn delete_hersteller(&self, ctx: &Context<'_>, id: i32) -> FieldResult<bool> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
|
||||
Hersteller::delete(pool, &id).await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn update_hersteller(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
id: i32,
|
||||
name: String,
|
||||
) -> FieldResult<Hersteller> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
|
||||
let row = Hersteller::update(pool, &id, &name).await?;
|
||||
Ok(row)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
pub mod hersteller;
|
||||
pub mod typ;
|
||||
|
||||
use async_graphql::MergedObject;
|
||||
|
||||
#[derive(MergedObject, Default)]
|
||||
pub struct Mutation(typ::TypMutation, hersteller::HerstellerMutation);
|
|
@ -0,0 +1,29 @@
|
|||
use crate::models::typ::Typ;
|
||||
use async_graphql::{Context, FieldResult};
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TypMutation;
|
||||
|
||||
#[async_graphql::Object]
|
||||
impl TypMutation {
|
||||
async fn create_typ(&self, ctx: &Context<'_>, name: String) -> FieldResult<Typ> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let row = Typ::create(pool, &name).await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn delete_typ(&self, ctx: &Context<'_>, id: i32) -> FieldResult<bool> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
|
||||
Typ::delete(pool, &id).await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn update_typ(&self, ctx: &Context<'_>, id: i32, name: String) -> FieldResult<Typ> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
|
||||
let row = Typ::update(pool, &id, &name).await?;
|
||||
Ok(row)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
use async_graphql::{Context, FieldResult, Object};
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
use crate::models::hersteller::Hersteller;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HerstellerQuery {
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
#[Object(extends)]
|
||||
impl HerstellerQuery {
|
||||
// #[graphql(external)]
|
||||
// async fn id(&self) -> &i32 {
|
||||
// &self.id
|
||||
// }
|
||||
|
||||
// async fn modell<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Modell>> {
|
||||
// let pool = ctx.data::<PgPool>().unwrap();
|
||||
// let rows = Modell::read_by_typ(pool, &self.id).await?;
|
||||
// Ok(rows)
|
||||
// }
|
||||
|
||||
async fn alle_hersteller<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Hersteller>> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let rows = Hersteller::read_all(pool).await?;
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
async fn hersteller<'a>(&self, ctx: &'a Context<'_>, id: i32) -> FieldResult<Hersteller> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let row = Hersteller::read_one(pool, &id).await?;
|
||||
Ok(row)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
pub mod hersteller;
|
||||
pub mod modell;
|
||||
pub mod typ;
|
||||
|
||||
use async_graphql::MergedObject;
|
||||
|
||||
#[derive(MergedObject, Default)]
|
||||
pub struct Query(
|
||||
typ::TypQuery,
|
||||
modell::ModellQuery,
|
||||
hersteller::HerstellerQuery,
|
||||
);
|
|
@ -0,0 +1,34 @@
|
|||
use async_graphql::{Context, FieldResult, Object};
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
use crate::models::modell::Modell;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModellQuery {
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
#[Object(extends)]
|
||||
impl ModellQuery {
|
||||
// #[graphql(external)]
|
||||
// async fn id(&self) -> &i32 {
|
||||
// &self.id
|
||||
// }
|
||||
|
||||
async fn modelle<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Modell>> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let rows = Modell::read_all(pool).await?;
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
async fn modell<'a>(&self, ctx: &'a Context<'_>, id: i32) -> FieldResult<Modell> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let row = Modell::read_one(pool, &id).await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
// #[graphql(entity)]
|
||||
// async fn find_modell_by_id(&self, id: i32) -> Modell {
|
||||
// Modell { id }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
use async_graphql::{Context, FieldResult, Object};
|
||||
use sqlx::postgres::PgPool;
|
||||
|
||||
use crate::models::typ::Typ;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TypQuery {
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
#[Object(extends)]
|
||||
impl TypQuery {
|
||||
// #[graphql(external)]
|
||||
// async fn id(&self) -> &i32 {
|
||||
// &self.id
|
||||
// }
|
||||
// async fn modell<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Modell>> {
|
||||
// let pool = ctx.data::<PgPool>().unwrap();
|
||||
// let rows = Modell::read_by_typ(pool, &self.id).await?;
|
||||
// Ok(rows)
|
||||
// }
|
||||
|
||||
async fn typen<'a>(&self, ctx: &'a Context<'_>) -> FieldResult<Vec<Typ>> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let rows = Typ::read_all(pool).await?;
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
async fn typ<'a>(&self, ctx: &'a Context<'_>, id: i32) -> FieldResult<Typ> {
|
||||
let pool = ctx.data::<PgPool>()?;
|
||||
let row = Typ::read_one(pool, &id).await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
// #[graphql(entity)]
|
||||
// async fn find_typ_by_id<'a>(&self, ctx: &'a Context<'_>, id: i32) -> FieldResult<Typ> {
|
||||
// let pool = ctx.data::<PgPool>().unwrap();
|
||||
// let row = Typ::read_one(pool, &id).await?;
|
||||
// Ok(row)
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue