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

Add Chapter 3 - GraphQL Integration Example #14

Merged
merged 63 commits into from
Jul 15, 2022
Merged
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a62f936
Revamped outline
shpun817 May 31, 2022
b37e29b
Rewrite introduction
shpun817 May 31, 2022
0030125
Remove old tutorials
shpun817 May 31, 2022
d7aed2d
Draft ch01-00
shpun817 May 31, 2022
1601c5a
Draft ch01-01
shpun817 Jun 2, 2022
8da1947
Edit SQLite (in file) example URL
shpun817 Jun 2, 2022
8f80893
Add bakery-backend source code
shpun817 May 31, 2022
f460284
Add `sea-orm-migration` dependency
shpun817 Jun 2, 2022
a1c4b7f
Create migrator module
shpun817 Jun 2, 2022
2e9abbc
Create migrations
shpun817 Jun 2, 2022
e7bccb5
Use Migrator to define the database schema
shpun817 Jun 2, 2022
e7b8dcf
Draft ch01-02
shpun817 Jun 2, 2022
6d62e73
Draft ch01-03
shpun817 Jun 7, 2022
1591908
Draft ch01-04
shpun817 Jun 7, 2022
074fc35
Insert and update
shpun817 Jun 7, 2022
463d921
Find (single entity)
shpun817 Jun 9, 2022
ec2cc8c
Delete
shpun817 Jun 9, 2022
dddb2c8
Draft ch01-06
shpun817 Jun 9, 2022
4aa363d
Draft ch01-07
shpun817 Jun 9, 2022
206679a
Remove PostgreSQL prompt symbol guide
shpun817 Jun 9, 2022
322fc91
Add reminder of executing sea-orm-cli at the project root
shpun817 Jun 9, 2022
16bb659
Specify file path of Cargo.toml
shpun817 Jun 9, 2022
00c194a
intro: "the most" -> "a"
shpun817 Jun 12, 2022
c8227cb
Remove legacy example source files
shpun817 Jun 14, 2022
2617f6e
Draft ch01-08
shpun817 Jun 14, 2022
c01ad2e
Add SeaQuery usage source code
shpun817 Jun 14, 2022
679fc6f
Add Rocket integration example source code
shpun817 Jun 16, 2022
dc4a156
Update SUMMARY
shpun817 Jun 16, 2022
28a805e
Section rename: "Fetch from Database" -> "Connect to Database"
shpun817 Jun 16, 2022
ab45421
Draft ch02-00
shpun817 Jun 16, 2022
c70b256
Remove # in SUMMARY.md
shpun817 Jun 16, 2022
6725296
Add `/reset` endpoint in Rocket integration example source code
shpun817 Jun 16, 2022
a923a0c
Draft ch02-01
shpun817 Jun 16, 2022
eefbbcd
Add templates to provide a minimal frontend
shpun817 Jun 23, 2022
cad9994
Remove non-existent handler in ch02-01
shpun817 Jun 23, 2022
17f48da
Draft ch02-02
shpun817 Jun 23, 2022
f566bb4
Add section for simple frontend using templates in plan
shpun817 Jun 23, 2022
7d5317f
Draft ch02-03
shpun817 Jun 23, 2022
f42fd8c
Add mounting new endpoint handler in ch02-02
shpun817 Jun 23, 2022
3cc3c25
Draft ch02-04
shpun817 Jun 23, 2022
3bf9e44
Draft ch02-05
shpun817 Jun 23, 2022
7735811
Merge Billy's changes
shpun817 Jun 27, 2022
1e3a658
Update ch01-00-simple-crud-getting-started.md (#10)
Jun 7, 2022
dce6160
Remove old tutorial section ch01-00-simple-crud-getting-started.md
shpun817 Jun 27, 2022
26b5389
Update GitHub workflows for building the examples
shpun817 Jun 27, 2022
e2315ad
Update README.md
shpun817 Jun 27, 2022
5abd62b
Prepare for early release (postponing the release of chapter 3)
shpun817 Jun 27, 2022
0d51094
Update SUMMARY.md for GraphQL sections
shpun817 Jul 5, 2022
6c44aac
Draft ch03-00
shpun817 Jul 5, 2022
93b1007
Draft ch03-01
shpun817 Jul 5, 2022
ece0050
Amend ch03-00
shpun817 Jul 5, 2022
f9ef21e
Draft ch03-02
shpun817 Jul 5, 2022
089dbaf
Draft ch03-03
shpun817 Jul 5, 2022
fd79185
Add GraphQL integration example source code
shpun817 Jul 5, 2022
4e4ca37
Merge branch 'master' of https://github.com/SeaQL/sea-orm-tutorial in…
shpun817 Jul 5, 2022
3202880
Merge branch 'SeaQL-master'
shpun817 Jul 5, 2022
da56330
Update GitHub workflows for building the graphql example
shpun817 Jul 5, 2022
1f267f2
Add endpoint for GraphQL playground
billy1624 Jul 7, 2022
221b8db
Include the link to the full source code of the example at the start …
shpun817 Jul 7, 2022
0cad1f8
Add GraphQL Integration example to README.md
shpun817 Jul 7, 2022
97a7952
Merge pull request #3 from billy1624/pr/14
shpun817 Jul 7, 2022
8eb283e
Draft ch03-05
shpun817 Jul 7, 2022
61ef2c2
Remove section for GraphQL subscription because it is not supported yet
shpun817 Jul 12, 2022
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
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@ jobs:
run: cargo build --manifest-path bakery-backend/Cargo.toml
- name: Build rocket-example
run: cargo build --manifest-path rocket-example/Cargo.toml
- name: Build graphql-example
run: cargo build --manifest-path graphql-example/Cargo.toml

# Try to build mdbooks
- name: Install mdbook
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["bakery-backend", "rocket-example"]
members = ["bakery-backend", "graphql-example", "rocket-example"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ The tutorials contain the following chapters:

1. [**Bakery Backend**](https://www.sea-ql.org/sea-orm-tutorial/ch01-00-build-backend-getting-started.html) - This chapter covers the basics of using SeaORM to interact with the database (a MySQL database is used for illustration). On top of this backend you can build any interface you need.
2. [**Rocket Integration**](https://www.sea-ql.org/sea-orm-tutorial/ch02-00-integration-with-rocket.html) - This chapter explains how to integrate the SeaORM backend into the Rocket framework to create a web application that provides a web API or even a simple frontend.
3. [**GraphQL Integration**](https://www.sea-ql.org/sea-orm-tutorial/ch03-00-integration-with-graphql.html) - This chapter extends the RESTful API of the web application in Chapter 2 to a GraphQL API with only one endpoint but enhanced flexibility of query.

[![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv)

1 change: 1 addition & 0 deletions graphql-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
18 changes: 18 additions & 0 deletions graphql-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "graphql-example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-graphql = "4.0.4"
async-graphql-rocket = "4.0.4"
rocket = { version = "^0.5.0-rc.2", features = ["json"] }
sea-orm = { version = "0.8.0", features = [
"sqlx-mysql",
"runtime-async-std-native-tls",
"macros",
] }
sea-orm-migration = "0.8.3"
serde_json = "1.0.81"
35 changes: 35 additions & 0 deletions graphql-example/src/entities/baker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

use async_graphql::SimpleObject;
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, SimpleObject)]
#[graphql(complex, name = "Baker")]
#[sea_orm(table_name = "baker")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub contact_details: Option<Json>,
pub bakery_id: i32,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::bakery::Entity",
from = "Column::BakeryId",
to = "super::bakery::Column::Id",
on_update = "NoAction",
on_delete = "NoAction"
)]
Bakery,
}

impl Related<super::bakery::Entity> for Entity {
fn to() -> RelationDef {
Relation::Bakery.def()
}
}

impl ActiveModelBehavior for ActiveModel {}
28 changes: 28 additions & 0 deletions graphql-example/src/entities/bakery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

use async_graphql::SimpleObject;
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, SimpleObject)]
#[graphql(complex, name = "Bakery")]
#[sea_orm(table_name = "bakery")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub profit_margin: f64,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::baker::Entity")]
Baker,
}

impl Related<super::baker::Entity> for Entity {
fn to() -> RelationDef {
Relation::Baker.def()
}
}

impl ActiveModelBehavior for ActiveModel {}
6 changes: 6 additions & 0 deletions graphql-example/src/entities/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

pub mod prelude;

pub mod baker;
pub mod bakery;
4 changes: 4 additions & 0 deletions graphql-example/src/entities/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

pub use super::baker::Entity as Baker;
pub use super::bakery::Entity as Bakery;
82 changes: 82 additions & 0 deletions graphql-example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
mod entities;
mod migrator;
mod schema;
mod setup;

use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
EmptySubscription, Schema,
};
use async_graphql_rocket::*;
use rocket::{response::content, *};
use schema::*;
use sea_orm::DbErr;
use setup::set_up_db;

type SchemaType = Schema<QueryRoot, MutationRoot, EmptySubscription>;

#[get("/")]
fn index() -> String {
"Hello, bakeries!".to_owned()
}

#[rocket::get("/graphql")]
fn graphql_playground() -> content::RawHtml<String> {
content::RawHtml(playground_source(GraphQLPlaygroundConfig::new("/graphql")))
}

#[rocket::post("/graphql", data = "<request>", format = "application/json")]
async fn graphql_request(schema: &State<SchemaType>, request: GraphQLRequest) -> GraphQLResponse {
request.execute(schema).await
}

#[launch]
async fn rocket() -> _ {
let db = match set_up_db().await {
Ok(db) => db,
Err(err) => panic!("{}", err),
};

let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(db)
.finish();

rocket::build()
.manage(schema)
.mount("/", routes![index, graphql_playground, graphql_request])
.register("/", catchers![not_found])
}

#[catch(404)]
pub fn not_found(req: &Request<'_>) -> String {
format!("{} not found.", req.uri())
}

#[derive(Responder)]
#[response(status = 500, content_type = "json")]
struct ErrorResponder {
message: String,
}

#[allow(clippy::from_over_into)]
impl Into<ErrorResponder> for DbErr {
fn into(self) -> ErrorResponder {
ErrorResponder {
message: self.to_string(),
}
}
}

#[allow(clippy::from_over_into)]
impl Into<ErrorResponder> for String {
fn into(self) -> ErrorResponder {
ErrorResponder { message: self }
}
}

#[allow(clippy::from_over_into)]
impl Into<ErrorResponder> for &str {
fn into(self) -> ErrorResponder {
self.to_owned().into()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use sea_orm_migration::prelude::*;

pub struct Migration;

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

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.if_not_exists()
.table(Bakery::Table)
.col(
ColumnDef::new(Bakery::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(Bakery::Name).string().not_null())
.col(ColumnDef::new(Bakery::ProfitMargin).double().not_null())
.to_owned(),
)
.await
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Bakery::Table).to_owned())
.await
}
}

#[derive(Iden)]
pub enum Bakery {
Table,
Id,
Name,
ProfitMargin,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use sea_orm_migration::prelude::*;

use super::m20220602_000001_create_bakery_table::Bakery;

pub struct Migration;

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

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.if_not_exists()
.table(Baker::Table)
.col(
ColumnDef::new(Baker::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(Baker::Name).string().not_null())
.col(ColumnDef::new(Baker::ContactDetails).json())
.col(ColumnDef::new(Baker::BakeryId).integer().not_null())
.foreign_key(
ForeignKey::create()
.name("fk-baker-bakery_id")
.from(Baker::Table, Baker::BakeryId)
.to(Bakery::Table, Bakery::Id),
)
.to_owned(),
)
.await
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Baker::Table).to_owned())
.await
}
}

#[derive(Iden)]
pub enum Baker {
Table,
Id,
Name,
ContactDetails,
BakeryId,
}
16 changes: 16 additions & 0 deletions graphql-example/src/migrator/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use sea_orm_migration::prelude::*;

mod m20220602_000001_create_bakery_table;
mod m20220602_000002_create_baker_table;

pub struct Migrator;

#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220602_000001_create_bakery_table::Migration),
Box::new(m20220602_000002_create_baker_table::Migration),
]
}
}
Loading