Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added axum graphql example #587

Merged
merged 6 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/axum-graphql_example/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL=sqlite:./db?mode=rwc
17 changes: 17 additions & 0 deletions examples/axum-graphql_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "axum-graphql"
authors = ["Aaron Leopold <[email protected]>"]
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = [".", "entity", "migration"]

[dependencies]
tokio = { version = "1.0", features = ["full"] }
axum = "0.4.8"
dotenv = "0.15.0"
async-graphql-axum = "3.0.31"
entity = { path = "entity" }
migration = { path = "migration" }
26 changes: 26 additions & 0 deletions examples/axum-graphql_example/entity/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "entity"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
name = "entity"
path = "src/lib.rs"

[dependencies]
serde = { version = "1", features = ["derive"] }

[dependencies.async-graphql]
version = "3.0.12"

[dependencies.sea-orm]
version = "^0.6.0"
features = [
"macros",
"runtime-tokio-native-tls",
# "sqlx-postgres",
# "sqlx-mysql",
"sqlx-sqlite"
]
default-features = false
4 changes: 4 additions & 0 deletions examples/axum-graphql_example/entity/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod note;

pub use async_graphql;
pub use sea_orm;
39 changes: 39 additions & 0 deletions examples/axum-graphql_example/entity/src/note.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use async_graphql::*;
use sea_orm::{entity::prelude::*, DeleteMany};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize, SimpleObject)]
#[sea_orm(table_name = "notes")]
#[graphql(concrete(name = "Note", params()))]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
pub id: i32,
pub title: String,
pub text: String,
}

#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}

impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}

impl ActiveModelBehavior for ActiveModel {}

impl Entity {
pub fn find_by_id(id: i32) -> Select<Entity> {
Self::find().filter(Column::Id.eq(id))
}

pub fn find_by_title(title: &str) -> Select<Entity> {
Self::find().filter(Column::Title.eq(title))
}

pub fn delete_by_id(id: i32) -> DeleteMany<Entity> {
Self::delete_many().filter(Column::Id.eq(id))
}
}
14 changes: 14 additions & 0 deletions examples/axum-graphql_example/migration/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "migration"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
name = "migration"
path = "src/lib.rs"

[dependencies]
sea-schema = { version = "0.5.0", default-features = false, features = [ "migration", "debug-print" ] }
dotenv = "0.15.0"
entity = { path = "../entity" }
37 changes: 37 additions & 0 deletions examples/axum-graphql_example/migration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Running Migrator CLI

- Apply all pending migrations
```sh
cargo run
```
```sh
cargo run -- up
```
- Apply first 10 pending migrations
```sh
cargo run -- up -n 10
```
- Rollback last applied migrations
```sh
cargo run -- down
```
- Rollback last 10 applied migrations
```sh
cargo run -- down -n 10
```
- Drop all tables from the database, then reapply all migrations
```sh
cargo run -- fresh
```
- Rollback all applied migrations, then reapply all migrations
```sh
cargo run -- refresh
```
- Rollback all applied migrations
```sh
cargo run -- reset
```
- Check the status of all migrations
```sh
cargo run -- status
```
12 changes: 12 additions & 0 deletions examples/axum-graphql_example/migration/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pub use sea_schema::migration::*;

mod m20220101_000001_create_table;

pub struct Migrator;

#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(m20220101_000001_create_table::Migration)]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use entity::{
note,
sea_orm::{DbBackend, EntityTrait, Schema},
};
use sea_schema::migration::{
sea_query::{self, *},
*,
};

pub struct Migration;

fn get_seaorm_create_stmt<E: EntityTrait>(e: E) -> TableCreateStatement {
let schema = Schema::new(DbBackend::Sqlite);

schema
.create_table_from_entity(e)
.if_not_exists()
.to_owned()
}

fn get_seaorm_drop_stmt<E: EntityTrait>(e: E) -> TableDropStatement {
Table::drop().table(e).if_exists().to_owned()
}

impl MigrationName for Migration {
fn name(&self) -> &str {
"m20220101_000001_create_table"
}
}

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let stmts = vec![get_seaorm_create_stmt(note::Entity)];

for stmt in stmts {
manager.create_table(stmt.to_owned()).await?;
}

Ok(())
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let stmts = vec![get_seaorm_drop_stmt(note::Entity)];

for stmt in stmts {
manager.drop_table(stmt.to_owned()).await?;
}

Ok(())
}
}
26 changes: 26 additions & 0 deletions examples/axum-graphql_example/migration/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use migration::Migrator;
use sea_schema::migration::*;
use std::path::PathBuf;

#[cfg(debug_assertions)]
use dotenv::dotenv;

#[async_std::main]
async fn main() {
#[cfg(debug_assertions)]
dotenv().ok();

let fallback = "sqlite:./db?mode=rwc";

match std::env::var("DATABASE_URL") {
Ok(val) => {
println!("Using DATABASE_URL: {}", val);
}
Err(_) => {
std::env::set_var("DATABASE_URL", fallback);
println!("Set DATABASE_URL: {}", fallback);
}
};

cli::run_cli(Migrator).await;
}
20 changes: 20 additions & 0 deletions examples/axum-graphql_example/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use entity::sea_orm;
use sea_orm::DatabaseConnection;

pub struct Database {
pub connection: DatabaseConnection,
}

impl Database {
pub async fn new() -> Self {
let connection = sea_orm::Database::connect(std::env::var("DATABASE_URL").unwrap())
.await
.expect("Could not connect to database");

Database { connection }
}

pub fn get_connection(&self) -> &DatabaseConnection {
&self.connection
}
}
3 changes: 3 additions & 0 deletions examples/axum-graphql_example/src/graphql/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod mutation;
pub mod query;
pub mod schema;
10 changes: 10 additions & 0 deletions examples/axum-graphql_example/src/graphql/mutation/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use entity::async_graphql;

pub mod note;

pub use note::NoteMutation;

// Add your other ones here to create a unified Mutation object
// e.x. Mutation(NoteMutation, OtherMutation, OtherOtherMutation)
#[derive(async_graphql::MergedObject, Default)]
pub struct Mutation(NoteMutation);
60 changes: 60 additions & 0 deletions examples/axum-graphql_example/src/graphql/mutation/note.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use async_graphql::{Context, Object, Result};
use entity::async_graphql::{self, InputObject, SimpleObject};
use entity::note;
use entity::sea_orm::{ActiveModelTrait, Set};

use crate::db::Database;

// I normally separate the input types into separate files/modules, but this is just
// a quick example.

#[derive(InputObject)]
pub struct CreateNoteInput {
pub title: String,
pub text: String,
}

#[derive(SimpleObject)]
pub struct DeleteResult {
pub success: bool,
pub rows_affected: u64,
}

#[derive(Default)]
pub struct NoteMutation;

#[Object]
impl NoteMutation {
pub async fn create_note(
&self,
ctx: &Context<'_>,
input: CreateNoteInput,
) -> Result<note::Model> {
let db = ctx.data::<Database>().unwrap();

let note = note::ActiveModel {
title: Set(input.title),
text: Set(input.text),
..Default::default()
};

Ok(note.insert(db.get_connection()).await?)
}

pub async fn delete_note(&self, ctx: &Context<'_>, id: i32) -> Result<DeleteResult> {
let db = ctx.data::<Database>().unwrap();

let res = note::Entity::delete_by_id(id)
.exec(db.get_connection())
.await?;

if res.rows_affected <= 1 {
Ok(DeleteResult {
success: true,
rows_affected: res.rows_affected,
})
} else {
unimplemented!()
}
}
}
10 changes: 10 additions & 0 deletions examples/axum-graphql_example/src/graphql/query/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use entity::async_graphql;

pub mod note;

pub use note::NoteQuery;

// Add your other ones here to create a unified Query object
// e.x. Query(NoteQuery, OtherQuery, OtherOtherQuery)
#[derive(async_graphql::MergedObject, Default)]
pub struct Query(NoteQuery);
28 changes: 28 additions & 0 deletions examples/axum-graphql_example/src/graphql/query/note.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use async_graphql::{Context, Object, Result};
use entity::{async_graphql, note, sea_orm::EntityTrait};

use crate::db::Database;

#[derive(Default)]
pub struct NoteQuery;

#[Object]
impl NoteQuery {
async fn get_notes(&self, ctx: &Context<'_>) -> Result<Vec<note::Model>> {
let db = ctx.data::<Database>().unwrap();

Ok(note::Entity::find()
.all(db.get_connection())
.await
.map_err(|e| e.to_string())?)
}

async fn get_note_by_id(&self, ctx: &Context<'_>, id: i32) -> Result<Option<note::Model>> {
let db = ctx.data::<Database>().unwrap();

Ok(note::Entity::find_by_id(id)
.one(db.get_connection())
.await
.map_err(|e| e.to_string())?)
}
}
18 changes: 18 additions & 0 deletions examples/axum-graphql_example/src/graphql/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use async_graphql::{EmptySubscription, Schema};
use entity::async_graphql;

use crate::{
db::Database,
graphql::{mutation::Mutation, query::Query},
};

pub type AppSchema = Schema<Query, Mutation, EmptySubscription>;

/// Builds the GraphQL Schema, attaching the Database to the context
pub async fn build_schema() -> AppSchema {
let db = Database::new().await;

Schema::build(Query::default(), Mutation::default(), EmptySubscription)
.data(db)
.finish()
}
Loading