(book) Update and fix book compilation and tests
* Use mdbook for building the book * Update book config * Update book hierarchy to work properly with mdbook This necessitated adding place-holder index pages since mdbook does not suppoert stand-alon menu items * Update tests to use 2018 edition * Fix various compilation errors in the tests
This commit is contained in:
parent
49297afab0
commit
9623e4d326
40 changed files with 1288 additions and 564 deletions
|
@ -10,3 +10,6 @@ members = [
|
||||||
"juniper_rocket",
|
"juniper_rocket",
|
||||||
"juniper_warp",
|
"juniper_warp",
|
||||||
]
|
]
|
||||||
|
exclude = [
|
||||||
|
"docs/book/tests",
|
||||||
|
]
|
||||||
|
|
5
docs/book/.gitignore
vendored
5
docs/book/.gitignore
vendored
|
@ -1,4 +1 @@
|
||||||
_book
|
_rendered
|
||||||
_skeptic/target
|
|
||||||
node_modules
|
|
||||||
*.swp
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: "vaPicq7s2hHBZwtG5eZ1wSmlIYog8FBZ7OilJs6cXQ0fyP5FqGFdc+VG+FSNbEDqPct/v5ojrfbwhQWZVjzgyMZ+Ikrpd9QD0T2Ie4f5yHh2schpphiog7pAfRG1A56/JsGq7aZr76DsICYUGeU4d8BzjaeFC2ozoo5tE9NXpp5ENLFNuErYGwMcQ0vlLTrK2miyuDn18HasHeT5pmxZT1qN5KjxzqChTvEFbH9pQsVKv+dVQiWVifYt4beOfSxaZJmCyBJHv2MjUOyWmYPtqikVxz4dkTbS/Cyx9dK3u2AgrH2Trrl0RFa5VKQUA+06v9NC+oH8NJj72aw44JdryVTchfQw3VF27H/2xfeg3WJX87/1J1oWvCBBtFWU5UwWapXq4Tz7UjT75H7unmlnc11hwmgMklpqMpD52om8n/GLMY2wkS5/dPJpLbYWt6OnBCPtHdP2EO59Wxg1YJ73PZdsrC81z3t8c4SSUXCmzUCG7P8UrSjpBl0g3yXTtR1/fvvSU1qQLFIDN8ib4tl8KGEgbX1ipJkkgCExriuZ58wOPqOdioqNMfWxyGszqxALsL1qxcET8ZtVOzIRCGuVptV0cUujxUwM9LJBqWq4MqPVO9+98FtX6xZvMM5gUM2dq4gWI45KK/VcNEkgihoSKUyVR2OaW5sTs6d28OejOXs="
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
# Install node.
|
|
||||||
- curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
|
||||||
- sudo apt-get install -y nodejs
|
|
||||||
# Install yarn.
|
|
||||||
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
|
||||||
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
|
||||||
- sudo apt-get update && sudo apt-get install -y yarn
|
|
||||||
|
|
||||||
script:
|
|
||||||
- yarn
|
|
||||||
- yarn build
|
|
||||||
- yarn test
|
|
||||||
- touch _book/.nojekyll
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- source
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
provider: pages
|
|
||||||
skip_cleanup: true
|
|
||||||
github_token: $DEPLOY_TOKEN
|
|
||||||
on:
|
|
||||||
branch: source
|
|
||||||
rust: stable
|
|
||||||
target_branch: master
|
|
||||||
local_dir: _book/
|
|
|
@ -1,12 +1,48 @@
|
||||||
Work in progress with improved documentation and main website for Juniper.
|
# Juniper Book
|
||||||
|
|
||||||
## Requirements
|
Book containing the Juniper documentation.
|
||||||
|
|
||||||
- Node.js
|
|
||||||
- `yarn`
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
- Run `yarn build` to build your changes.
|
### Requirements
|
||||||
- Run `yarn serve` to view your changes.
|
|
||||||
- Run `yarn test` to test your changes.
|
The book is built with [mdBook](https://github.com/rust-lang-nursery/mdBook).
|
||||||
|
|
||||||
|
You can install it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install mdbook
|
||||||
|
```
|
||||||
|
|
||||||
|
### Starting a local test server
|
||||||
|
|
||||||
|
To launch a local test server that continually re-builds the book and autoreloads the page, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mdbook serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building the book
|
||||||
|
|
||||||
|
You can build the book to rendered HTML with this command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mdbook build
|
||||||
|
```
|
||||||
|
|
||||||
|
The output will be in the `./_rendered` directory.
|
||||||
|
|
||||||
|
### Running the tests
|
||||||
|
|
||||||
|
To run the tests validating all code examples in the book, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ./tests
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test setup
|
||||||
|
|
||||||
|
All Rust code examples in the book are compiled on the CI.
|
||||||
|
|
||||||
|
This is done using the [skeptic](https://github.com/budziq/rust-skeptic) library.
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"plugins": [
|
|
||||||
"anchors",
|
|
||||||
"codeblock-omit",
|
|
||||||
"codeblock-rust",
|
|
||||||
"sane-sidebar",
|
|
||||||
"-sharing"
|
|
||||||
],
|
|
||||||
"root": "./docs"
|
|
||||||
|
|
||||||
}
|
|
11
docs/book/book.toml
Normal file
11
docs/book/book.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[book]
|
||||||
|
title = "Juniper - GraphQL Server for Rust"
|
||||||
|
description = "Documentation for juniper, a GraphQL server library for Rust."
|
||||||
|
src = "content"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
build-dir = "_rendered"
|
||||||
|
create-missing = false
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
git_repository_url = "https://github.com/graphql-rs/juniper"
|
31
docs/book/content/SUMMARY.md
Normal file
31
docs/book/content/SUMMARY.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
- [Introduction](README.md)
|
||||||
|
- [Quickstart](quickstart.md)
|
||||||
|
|
||||||
|
- [Type System](types/index.md)
|
||||||
|
- [Defining objects](types/objects/defining_objects.md)
|
||||||
|
- [Complex fields](types/objects/complex_fields.md)
|
||||||
|
- [Using contexts](types/objects/using_contexts.md)
|
||||||
|
- [Error handling](types/objects/error_handling.md)
|
||||||
|
- [Other types](types/other-index.md)
|
||||||
|
- [Enums](types/enums.md)
|
||||||
|
- [Interfaces](types/interfaces.md)
|
||||||
|
- [Input objects](types/input_objects.md)
|
||||||
|
- [Scalars](types/scalars.md)
|
||||||
|
- [Unions](types/unions.md)
|
||||||
|
|
||||||
|
- [Schemas and mutations](schema/schemas_and_mutations.md)
|
||||||
|
|
||||||
|
- [Adding A Server](servers/index.md)
|
||||||
|
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
|
||||||
|
- [Warp](servers/warp.md)
|
||||||
|
- [Rocket](servers/rocket.md)
|
||||||
|
- [Iron](servers/iron.md)
|
||||||
|
- [Hyper](servers/hyper.md)
|
||||||
|
- [Third Party Integrations](servers/third-party.md)
|
||||||
|
|
||||||
|
- [Advanced Topics](advanced/index.md)
|
||||||
|
- [Non-struct objects](advanced/non_struct_objects.md)
|
||||||
|
- [Objects and generics](advanced/objects_and_generics.md)
|
||||||
|
- [Multiple operations per request](advanced/multiple_ops_per_request.md)
|
||||||
|
# - [Context switching]
|
||||||
|
# - [Dynamic type system]
|
7
docs/book/content/advanced/index.md
Normal file
7
docs/book/content/advanced/index.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Advanced Topics
|
||||||
|
|
||||||
|
The chapters below cover some more advanced scenarios.
|
||||||
|
|
||||||
|
- [Non-struct objects](advanced/non_struct_objects.md)
|
||||||
|
- [Objects and generics](advanced/objects_and_generics.md)
|
||||||
|
- [Multiple operations per request](advanced/multiple_ops_per_request.md)
|
|
@ -9,11 +9,9 @@ Using `Result`-like enums can be a useful way of reporting e.g. validation
|
||||||
errors from a mutation:
|
errors from a mutation:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct ValidationError {
|
struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
|
@ -25,7 +23,7 @@ enum SignUpResult {
|
||||||
Error(Vec<ValidationError>),
|
Error(Vec<ValidationError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(SignUpResult: () |&self| {
|
juniper::graphql_object!(SignUpResult: () |&self| {
|
||||||
field user() -> Option<&User> {
|
field user() -> Option<&User> {
|
||||||
match *self {
|
match *self {
|
||||||
SignUpResult::Ok(ref user) => Some(user),
|
SignUpResult::Ok(ref user) => Some(user),
|
|
@ -13,11 +13,10 @@ Let's make a slightly more compact but generic implementation of [the last
|
||||||
chapter](non_struct_objects.md):
|
chapter](non_struct_objects.md):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct ForumPost { title: String }
|
||||||
# #[derive(GraphQLObject)] struct ForumPost { title: String }
|
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct ValidationError {
|
struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
|
@ -26,7 +25,7 @@ struct ValidationError {
|
||||||
# #[allow(dead_code)]
|
# #[allow(dead_code)]
|
||||||
struct MutationResult<T>(Result<T, Vec<ValidationError>>);
|
struct MutationResult<T>(Result<T, Vec<ValidationError>>);
|
||||||
|
|
||||||
graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
|
juniper::graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
|
||||||
field user() -> Option<&User> {
|
field user() -> Option<&User> {
|
||||||
self.0.as_ref().ok()
|
self.0.as_ref().ok()
|
||||||
}
|
}
|
||||||
|
@ -36,7 +35,7 @@ graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
graphql_object!(MutationResult<ForumPost>: () as "ForumPostResult" |&self| {
|
juniper::graphql_object!(MutationResult<ForumPost>: () as "ForumPostResult" |&self| {
|
||||||
field forum_post() -> Option<&ForumPost> {
|
field forum_post() -> Option<&ForumPost> {
|
||||||
self.0.as_ref().ok()
|
self.0.as_ref().ok()
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ for more detailed information.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
juniper = "0.10"
|
juniper = "0.11"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Schema example
|
## Schema example
|
||||||
|
@ -27,11 +27,7 @@ types to a GraphQL schema. The most important one is the
|
||||||
[graphql_object!][jp_obj_macro] macro that is used for declaring an object with
|
[graphql_object!][jp_obj_macro] macro that is used for declaring an object with
|
||||||
resolvers, which you will use for the `Query` and `Mutation` roots.
|
resolvers, which you will use for the `Query` and `Mutation` roots.
|
||||||
|
|
||||||
!FILENAME main.rs
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
use juniper::{FieldResult};
|
use juniper::{FieldResult};
|
||||||
|
|
||||||
# struct DatabasePool;
|
# struct DatabasePool;
|
||||||
|
@ -41,14 +37,14 @@ use juniper::{FieldResult};
|
||||||
# fn insert_human(&self, human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
# fn insert_human(&self, human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
||||||
# }
|
# }
|
||||||
|
|
||||||
#[derive(GraphQLEnum)]
|
#[derive(juniper::GraphQLEnum)]
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
Jedi,
|
Jedi,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
#[graphql(description="A humanoid creature in the Star Wars universe")]
|
#[graphql(description="A humanoid creature in the Star Wars universe")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -59,7 +55,7 @@ struct Human {
|
||||||
|
|
||||||
// There is also a custom derive for mapping GraphQL input objects.
|
// There is also a custom derive for mapping GraphQL input objects.
|
||||||
|
|
||||||
#[derive(GraphQLInputObject)]
|
#[derive(juniper::GraphQLInputObject)]
|
||||||
#[graphql(description="A humanoid creature in the Star Wars universe")]
|
#[graphql(description="A humanoid creature in the Star Wars universe")]
|
||||||
struct NewHuman {
|
struct NewHuman {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -82,7 +78,7 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
graphql_object!(Query: Context |&self| {
|
juniper::graphql_object!(Query: Context |&self| {
|
||||||
|
|
||||||
field apiVersion() -> &str {
|
field apiVersion() -> &str {
|
||||||
"1.0"
|
"1.0"
|
||||||
|
@ -105,7 +101,7 @@ graphql_object!(Query: Context |&self| {
|
||||||
|
|
||||||
struct Mutation;
|
struct Mutation;
|
||||||
|
|
||||||
graphql_object!(Mutation: Context |&self| {
|
juniper::graphql_object!(Mutation: Context |&self| {
|
||||||
|
|
||||||
field createHuman(&executor, new_human: NewHuman) -> FieldResult<Human> {
|
field createHuman(&executor, new_human: NewHuman) -> FieldResult<Human> {
|
||||||
let db = executor.context().pool.get_connection()?;
|
let db = executor.context().pool.get_connection()?;
|
||||||
|
@ -118,28 +114,25 @@ graphql_object!(Mutation: Context |&self| {
|
||||||
// Request queries can be executed against a RootNode.
|
// Request queries can be executed against a RootNode.
|
||||||
type Schema = juniper::RootNode<'static, Query, Mutation>;
|
type Schema = juniper::RootNode<'static, Query, Mutation>;
|
||||||
|
|
||||||
fn main() {
|
# fn main() { }
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We now have a very simple but functional schema for a GraphQL server!
|
We now have a very simple but functional schema for a GraphQL server!
|
||||||
|
|
||||||
To actually serve the schema, see the guides for our [Hyper], [Rocket],
|
To actually serve the schema, see the guides for our various [server integrations](./servers/index.md).
|
||||||
[Warp], and [Iron] server integrations. Or you can invoke the executor directly:
|
|
||||||
|
You can also invoke the executor directly to get a result for a query:
|
||||||
|
|
||||||
## Executor
|
## Executor
|
||||||
|
|
||||||
You can invoke `juniper::execute` directly to run a GraphQL query:
|
You can invoke `juniper::execute` directly to run a GraphQL query:
|
||||||
|
|
||||||
!FILENAME main.rs
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
# // Only needed due to 2018 edition because the macro is not accessible.
|
||||||
|
# extern crate juniper;
|
||||||
use juniper::{FieldResult, Variables, EmptyMutation};
|
use juniper::{FieldResult, Variables, EmptyMutation};
|
||||||
|
|
||||||
#[derive(GraphQLEnum, Clone, Copy)]
|
#[derive(juniper::GraphQLEnum, Clone, Copy)]
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
|
@ -148,7 +141,7 @@ enum Episode {
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
graphql_object!(Query: Ctx |&self| {
|
juniper::graphql_object!(Query: Ctx |&self| {
|
||||||
field favoriteEpisode(&executor) -> FieldResult<Episode> {
|
field favoriteEpisode(&executor) -> FieldResult<Episode> {
|
||||||
// Use the special &executor argument to fetch our fav episode.
|
// Use the special &executor argument to fetch our fav episode.
|
||||||
Ok(executor.context().0)
|
Ok(executor.context().0)
|
||||||
|
@ -177,8 +170,10 @@ fn main() {
|
||||||
|
|
||||||
// Ensure the value matches.
|
// Ensure the value matches.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.as_object_value().unwrap().get_field_value("favoriteEpisode").unwrap().as_string_value().unwrap(),
|
res,
|
||||||
"NEW_HOPE",
|
graphql_value!({
|
||||||
|
"favoriteEpisode": "NEW_HONE",
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
|
@ -23,13 +23,11 @@ The query root is just a GraphQL object. You define it like any other GraphQL
|
||||||
object in Juniper, most commonly using the `graphql_object!` macro:
|
object in Juniper, most commonly using the `graphql_object!` macro:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# use juniper::FieldResult;
|
# use juniper::FieldResult;
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
struct Root;
|
struct Root;
|
||||||
|
|
||||||
graphql_object!(Root: () |&self| {
|
juniper::graphql_object!(Root: () |&self| {
|
||||||
field userWithUsername(username: String) -> FieldResult<Option<User>> {
|
field userWithUsername(username: String) -> FieldResult<Option<User>> {
|
||||||
// Look up user in database...
|
// Look up user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
|
@ -45,13 +43,11 @@ Mutations are _also_ just GraphQL objects. Each mutation is a single field that
|
||||||
usually performs some mutating side-effect, such as updating a database.
|
usually performs some mutating side-effect, such as updating a database.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# use juniper::FieldResult;
|
# use juniper::FieldResult;
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
struct Mutations;
|
struct Mutations;
|
||||||
|
|
||||||
graphql_object!(Mutations: () |&self| {
|
juniper::graphql_object!(Mutations: () |&self| {
|
||||||
field signUpUser(name: String, email: String) -> FieldResult<User> {
|
field signUpUser(name: String, email: String) -> FieldResult<User> {
|
||||||
// Validate inputs and save user in database...
|
// Validate inputs and save user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
17
docs/book/content/servers/index.md
Normal file
17
docs/book/content/servers/index.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Adding A Server
|
||||||
|
|
||||||
|
To allow using Juniper with the HTTP server of your choice,
|
||||||
|
it does **not** come with a built in HTTP server.
|
||||||
|
|
||||||
|
To actually get a server up and running, there are multiple official and
|
||||||
|
third-party integration crates that will get you there.
|
||||||
|
|
||||||
|
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
|
||||||
|
- [Warp](servers/warp.md)
|
||||||
|
- [Rocket](servers/rocket.md)
|
||||||
|
- [Iron](servers/iron.md)
|
||||||
|
- [Hyper](servers/hyper.md)
|
||||||
|
- [Third Party Integrations](servers/third-party.md)
|
||||||
|
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
|
||||||
|
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
|
||||||
|
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)
|
|
@ -31,7 +31,7 @@ set up other global data that the schema might require.
|
||||||
In this example, we won't use any global data so we just return an empty value.
|
In this example, we won't use any global data so we just return an empty value.
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
#[macro_use] extern crate juniper;
|
extern crate juniper;
|
||||||
extern crate juniper_iron;
|
extern crate juniper_iron;
|
||||||
extern crate iron;
|
extern crate iron;
|
||||||
extern crate mount;
|
extern crate mount;
|
||||||
|
@ -79,7 +79,7 @@ resolver, you need to pass this data using Juniper's [context
|
||||||
feature](context.md).
|
feature](context.md).
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
# #[macro_use] extern crate juniper;
|
# extern crate juniper;
|
||||||
# extern crate juniper_iron;
|
# extern crate juniper_iron;
|
||||||
# extern crate iron;
|
# extern crate iron;
|
||||||
# use iron::prelude::*;
|
# use iron::prelude::*;
|
9
docs/book/content/servers/official.md
Normal file
9
docs/book/content/servers/official.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Official Server Integrations
|
||||||
|
|
||||||
|
Juniper provides official integration crates for several popular Rust server
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
- [Hyper](./hyper.md)
|
||||||
|
- [Warp](./warp.md)
|
||||||
|
- [Rocket](./rocket.md)
|
||||||
|
- [Iron](./iron.md)
|
8
docs/book/content/servers/third-party.md
Normal file
8
docs/book/content/servers/third-party.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Third-Party Integrations
|
||||||
|
|
||||||
|
There are several examples or third party integration crates that are not
|
||||||
|
officially maintained by Juniper developers.
|
||||||
|
|
||||||
|
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
|
||||||
|
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
|
||||||
|
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)
|
|
@ -5,10 +5,7 @@ possible values. Simple Rust enums can be converted to GraphQL enums by using a
|
||||||
custom derive attribute:
|
custom derive attribute:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLEnum)]
|
||||||
extern crate juniper;
|
|
||||||
|
|
||||||
#[derive(GraphQLEnum)]
|
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
|
@ -24,10 +21,7 @@ you want to override this, you can use the `graphql` attribute, similar to how
|
||||||
it works when [defining objects](defining_objects.md):
|
it works when [defining objects](defining_objects.md):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLEnum)]
|
||||||
# extern crate juniper;
|
|
||||||
|
|
||||||
#[derive(GraphQLEnum)]
|
|
||||||
enum Episode {
|
enum Episode {
|
||||||
#[graphql(name="NEW_HOPE")]
|
#[graphql(name="NEW_HOPE")]
|
||||||
NewHope,
|
NewHope,
|
||||||
|
@ -44,10 +38,7 @@ Just like when defining objects, the type itself can be renamed and documented,
|
||||||
while individual enum variants can be renamed, documented, and deprecated:
|
while individual enum variants can be renamed, documented, and deprecated:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLEnum)]
|
||||||
# extern crate juniper;
|
|
||||||
|
|
||||||
#[derive(GraphQLEnum)]
|
|
||||||
#[graphql(name="Episode", description="An episode of Star Wars")]
|
#[graphql(name="Episode", description="An episode of Star Wars")]
|
||||||
enum StarWarsEpisode {
|
enum StarWarsEpisode {
|
||||||
#[graphql(deprecated="We don't really talk about this one")]
|
#[graphql(deprecated="We don't really talk about this one")]
|
20
docs/book/content/types/index.md
Normal file
20
docs/book/content/types/index.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Type System
|
||||||
|
|
||||||
|
Most of the work in working with juniper consists of mapping the
|
||||||
|
GraphQL type system to the Rust types your application uses.
|
||||||
|
|
||||||
|
Juniper provides some convenient abstractions that try to make this process
|
||||||
|
as painless as possible.
|
||||||
|
|
||||||
|
Find out more in the individual chapters below.
|
||||||
|
|
||||||
|
- [Defining objects](types/objects/defining_objects.md)
|
||||||
|
- [Complex fields](types/objects/complex_fields.md)
|
||||||
|
- [Using contexts](types/objects/using_contexts.md)
|
||||||
|
- [Error handling](types/objects/error_handling.md)
|
||||||
|
- [Other types](types/other-index.md)
|
||||||
|
- [Enums](types/enums.md)
|
||||||
|
- [Interfaces](types/interfaces.md)
|
||||||
|
- [Input objects](types/input_objects.md)
|
||||||
|
- [Scalars](types/scalars.md)
|
||||||
|
- [Unions](types/unions.md)
|
|
@ -5,19 +5,16 @@ GraphQL fields. In Juniper, you can define input objects using a custom derive
|
||||||
attribute, similar to simple objects and enums:
|
attribute, similar to simple objects and enums:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLInputObject)]
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
#[derive(GraphQLInputObject)]
|
|
||||||
struct Coordinate {
|
struct Coordinate {
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
longitude: f64
|
longitude: f64
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Root;
|
struct Root;
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
|
|
||||||
graphql_object!(Root: () |&self| {
|
juniper::graphql_object!(Root: () |&self| {
|
||||||
field users_at_location(coordinate: Coordinate, radius: f64) -> Vec<User> {
|
field users_at_location(coordinate: Coordinate, radius: f64) -> Vec<User> {
|
||||||
// Send coordinate to database
|
// Send coordinate to database
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
|
@ -33,10 +30,7 @@ Just like the [other](defining_objects.md) [derives](enums.md), you can rename
|
||||||
and add documentation to both the type and the fields:
|
and add documentation to both the type and the fields:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLInputObject)]
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
#[derive(GraphQLInputObject)]
|
|
||||||
#[graphql(name="Coordinate", description="A position on the globe")]
|
#[graphql(name="Coordinate", description="A position on the globe")]
|
||||||
struct WorldCoordinate {
|
struct WorldCoordinate {
|
||||||
#[graphql(name="lat", description="The latitude")]
|
#[graphql(name="lat", description="The latitude")]
|
||||||
|
@ -47,9 +41,9 @@ struct WorldCoordinate {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Root;
|
struct Root;
|
||||||
# #[derive(GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
|
|
||||||
graphql_object!(Root: () |&self| {
|
juniper::graphql_object!(Root: () |&self| {
|
||||||
field users_at_location(coordinate: WorldCoordinate, radius: f64) -> Vec<User> {
|
field users_at_location(coordinate: WorldCoordinate, radius: f64) -> Vec<User> {
|
||||||
// Send coordinate to database
|
// Send coordinate to database
|
||||||
# unimplemented!()
|
# unimplemented!()
|
|
@ -19,15 +19,13 @@ be done in a couple of different ways:
|
||||||
### Downcasting via accessor methods
|
### Downcasting via accessor methods
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -51,7 +49,7 @@ impl Character for Droid {
|
||||||
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
|
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_interface!(<'a> &'a Character: () as "Character" |&self| {
|
juniper::graphql_interface!(<'a> &'a Character: () as "Character" where Scalar = <S>|&self| {
|
||||||
field id() -> &str { self.id() }
|
field id() -> &str { self.id() }
|
||||||
|
|
||||||
instance_resolvers: |_| {
|
instance_resolvers: |_| {
|
||||||
|
@ -78,19 +76,17 @@ If you can afford an extra database lookup when the concrete class is requested,
|
||||||
you can do away with the downcast methods and use the context instead. Here,
|
you can do away with the downcast methods and use the context instead. Here,
|
||||||
we'll use two hashmaps, but this could be two tables and some SQL calls instead:
|
we'll use two hashmaps, but this could be two tables and some SQL calls instead:
|
||||||
|
|
||||||
FIXME: This example does not compile at the moment
|
```rust
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -115,7 +111,7 @@ impl Character for Droid {
|
||||||
fn id(&self) -> &str { self.id.as_str() }
|
fn id(&self) -> &str { self.id.as_str() }
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
|
juniper::graphql_interface!(<'a> &'a Character: Database as "Character" where Scalar = <S> |&self| {
|
||||||
field id() -> &str { self.id() }
|
field id() -> &str { self.id() }
|
||||||
|
|
||||||
instance_resolvers: |&context| {
|
instance_resolvers: |&context| {
|
||||||
|
@ -134,19 +130,17 @@ This removes the need of downcast methods, but still requires some repetition.
|
||||||
Continuing on from the last example, the trait itself seems a bit unneccesary.
|
Continuing on from the last example, the trait itself seems a bit unneccesary.
|
||||||
Maybe it can just be a struct containing the ID?
|
Maybe it can just be a struct containing the ID?
|
||||||
|
|
||||||
FIXME: This example does not compile at the moment
|
```rust
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -163,7 +157,7 @@ struct Character {
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_interface!(Character: Database |&self| {
|
juniper::graphql_interface!(Character: Database where Scalar = <S> |&self| {
|
||||||
field id() -> &str { self.id.as_str() }
|
field id() -> &str { self.id.as_str() }
|
||||||
|
|
||||||
instance_resolvers: |&context| {
|
instance_resolvers: |&context| {
|
||||||
|
@ -185,15 +179,13 @@ placeholder objects. We don't need the extra database call in this case, so
|
||||||
we'll remove it.
|
we'll remove it.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -205,7 +197,7 @@ enum Character {
|
||||||
Droid(Droid),
|
Droid(Droid),
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_interface!(Character: () |&self| {
|
juniper::graphql_interface!(Character: () where Scalar = <S> |&self| {
|
||||||
field id() -> &str {
|
field id() -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
Character::Human(Human { ref id, .. }) |
|
Character::Human(Human { ref id, .. }) |
|
|
@ -8,14 +8,13 @@ example from the last chapter, this is how you would define `Person` using the
|
||||||
macro:
|
macro:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(Person: () |&self| {
|
juniper::graphql_object!(Person: () |&self| {
|
||||||
field name() -> &str {
|
field name() -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
}
|
}
|
||||||
|
@ -33,9 +32,7 @@ field resolver. With this syntax, fields can also take arguments:
|
||||||
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -45,7 +42,7 @@ struct House {
|
||||||
inhabitants: Vec<Person>,
|
inhabitants: Vec<Person>,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(House: () |&self| {
|
juniper::graphql_object!(House: () |&self| {
|
||||||
// Creates the field inhabitantWithName(name), returning a nullable person
|
// Creates the field inhabitantWithName(name), returning a nullable person
|
||||||
field inhabitant_with_name(name: String) -> Option<&Person> {
|
field inhabitant_with_name(name: String) -> Option<&Person> {
|
||||||
self.inhabitants.iter().find(|p| p.name == name)
|
self.inhabitants.iter().find(|p| p.name == name)
|
||||||
|
@ -66,14 +63,12 @@ to `camelCase`. If you need to override the conversion, you can simply rename
|
||||||
the field. Also, the type name can be changed with an alias:
|
the field. Also, the type name can be changed with an alias:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
website_url: String,
|
website_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(Person: () as "PersonObject" |&self| {
|
juniper::graphql_object!(Person: () as "PersonObject" |&self| {
|
||||||
field name() -> &str {
|
field name() -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
}
|
}
|
|
@ -9,10 +9,7 @@ attribute. The other way is described in the [Complex fields](complex_fields.md)
|
||||||
chapter.
|
chapter.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
#[macro_use] extern crate juniper_codegen;
|
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -34,10 +31,7 @@ descriptions:
|
||||||
!FILENAME GraphQL descriptions via Rust doc comments
|
!FILENAME GraphQL descriptions via Rust doc comments
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
/// Information about a person
|
/// Information about a person
|
||||||
struct Person {
|
struct Person {
|
||||||
/// The person's full name, including both first and last names
|
/// The person's full name, including both first and last names
|
||||||
|
@ -55,10 +49,7 @@ via the `graphql` attribute. The following example is equivalent to the above:
|
||||||
!FILENAME GraphQL descriptions via attribute
|
!FILENAME GraphQL descriptions via attribute
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
#[graphql(description="Information about a person")]
|
#[graphql(description="Information about a person")]
|
||||||
struct Person {
|
struct Person {
|
||||||
#[graphql(description="The person's full name, including both first and last names")]
|
#[graphql(description="The person's full name, including both first and last names")]
|
||||||
|
@ -75,10 +66,7 @@ doc comments. This enables internal Rust documentation and external GraphQL
|
||||||
documentation to differ:
|
documentation to differ:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
#[graphql(description="This description shows up in GraphQL")]
|
#[graphql(description="This description shows up in GraphQL")]
|
||||||
/// This description shows up in RustDoc
|
/// This description shows up in RustDoc
|
||||||
struct Person {
|
struct Person {
|
||||||
|
@ -107,16 +95,13 @@ You can only use the custom derive attribute under these circumstances:
|
||||||
Let's see what that means for building relationships between objects:
|
Let's see what that means for building relationships between objects:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
#[macro_use] extern crate juniper_codegen;
|
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct House {
|
struct House {
|
||||||
address: Option<String>, // Converted into String (nullable)
|
address: Option<String>, // Converted into String (nullable)
|
||||||
inhabitants: Vec<Person>, // Converted into [Person!]!
|
inhabitants: Vec<Person>, // Converted into [Person!]!
|
||||||
|
@ -135,10 +120,7 @@ By default, struct fields are converted from Rust's standard `snake_case` naming
|
||||||
convention into GraphQL's `camelCase` convention:
|
convention into GraphQL's `camelCase` convention:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
||||||
last_name: String, // Exposed as lastName
|
last_name: String, // Exposed as lastName
|
||||||
|
@ -151,10 +133,7 @@ You can override the name by using the `graphql` attribute on individual struct
|
||||||
fields:
|
fields:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -171,10 +150,7 @@ To deprecate a field, you specify a deprecation reason using the `graphql`
|
||||||
attribute:
|
attribute:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -194,14 +170,12 @@ only deprecate object fields and enum values.
|
||||||
By default all fields in a `GraphQLObject` are included in the generated GraphQL type. To prevent including a specific field, annotate the field with `#[graphql(skip)]`:
|
By default all fields in a `GraphQLObject` are included in the generated GraphQL type. To prevent including a specific field, annotate the field with `#[graphql(skip)]`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
#
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(skip)]
|
#[graphql(skip)]
|
||||||
|
# #[allow(dead_code)]
|
||||||
password_hash: String, // This cannot be queried or modified from GraphQL
|
password_hash: String, // This cannot be queried or modified from GraphQL
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,20 @@ can use the `?` operator or the `try!` macro and things will generally just work
|
||||||
as you expect them to:
|
as you expect them to:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper;
|
# extern crate juniper;
|
||||||
|
use std::{
|
||||||
|
str,
|
||||||
|
path::PathBuf,
|
||||||
|
fs::{File},
|
||||||
|
io::{Read},
|
||||||
|
};
|
||||||
use juniper::FieldResult;
|
use juniper::FieldResult;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
struct Example {
|
struct Example {
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(Example: () |&self| {
|
juniper::graphql_object!(Example: () |&self| {
|
||||||
field contents() -> FieldResult<String> {
|
field contents() -> FieldResult<String> {
|
||||||
let mut file = File::open(&self.filename)?;
|
let mut file = File::open(&self.filename)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
@ -118,18 +120,16 @@ to clients. This can be accomplished by implementing [`IntoFieldError`](https://
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper;
|
# #[macro_use] extern crate juniper;
|
||||||
use juniper::{FieldError, IntoFieldError};
|
|
||||||
|
|
||||||
enum CustomError {
|
enum CustomError {
|
||||||
WhateverNotSet,
|
WhateverNotSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoFieldError for CustomError {
|
impl juniper::IntoFieldError for CustomError {
|
||||||
fn into_field_error(self) -> FieldError {
|
fn into_field_error(self) -> juniper::FieldError {
|
||||||
match self {
|
match self {
|
||||||
CustomError::WhateverNotSet => FieldError::new(
|
CustomError::WhateverNotSet => juniper::FieldError::new(
|
||||||
"Whatever does not exist",
|
"Whatever does not exist",
|
||||||
graphql_value!({
|
juniper::graphql_value!({
|
||||||
"type": "NO_WHATEVER"
|
"type": "NO_WHATEVER"
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -141,7 +141,7 @@ struct Example {
|
||||||
whatever: Option<bool>,
|
whatever: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_object!(Example: () |&self| {
|
juniper::graphql_object!(Example: () |&self| {
|
||||||
field whatever() -> Result<bool, CustomError> {
|
field whatever() -> Result<bool, CustomError> {
|
||||||
if let Some(value) = self.whatever {
|
if let Some(value) = self.whatever {
|
||||||
return Ok(value);
|
return Ok(value);
|
|
@ -36,7 +36,7 @@ current context object:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
#[macro_use] extern crate juniper;
|
extern crate juniper;
|
||||||
|
|
||||||
struct Database {
|
struct Database {
|
||||||
users: HashMap<i32, User>,
|
users: HashMap<i32, User>,
|
||||||
|
@ -52,7 +52,7 @@ struct User {
|
||||||
impl juniper::Context for Database {}
|
impl juniper::Context for Database {}
|
||||||
|
|
||||||
// 2. Assign Database as the context type for User
|
// 2. Assign Database as the context type for User
|
||||||
graphql_object!(User: Database |&self| {
|
juniper::graphql_object!(User: Database |&self| {
|
||||||
// 3. Use the special executor argument
|
// 3. Use the special executor argument
|
||||||
field friends(&executor) -> Vec<&User> {
|
field friends(&executor) -> Vec<&User> {
|
||||||
// 4. Use the executor to access the context object
|
// 4. Use the executor to access the context object
|
11
docs/book/content/types/other-index.md
Normal file
11
docs/book/content/types/other-index.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Other Types
|
||||||
|
|
||||||
|
The GraphQL type system provides several types in additon to objects.
|
||||||
|
|
||||||
|
Find out more about each type below:
|
||||||
|
|
||||||
|
- [Enums](./enums.md)
|
||||||
|
- [Interfaces](./interfaces.md)
|
||||||
|
- [Input objects](./input_objects.md)
|
||||||
|
- [Scalars](./scalars.md)
|
||||||
|
- [Unions](./unions.md)
|
|
@ -13,23 +13,25 @@ In Juniper, you use the `graphql_scalar!` macro to create a custom scalar. In
|
||||||
this example, we're representing a user ID as a string wrapped in a custom type:
|
this example, we're representing a user ID as a string wrapped in a custom type:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[macro_use] extern crate juniper;
|
|
||||||
|
|
||||||
use juniper::Value;
|
use juniper::Value;
|
||||||
|
|
||||||
struct UserID(String);
|
struct UserID(String);
|
||||||
|
|
||||||
graphql_scalar!(UserID {
|
juniper::graphql_scalar!(UserID {
|
||||||
description: "An opaque identifier, represented as a string"
|
description: "An opaque identifier, represented as a string"
|
||||||
|
|
||||||
resolve(&self) -> Value {
|
resolve(&self) -> Value {
|
||||||
Value::string(&self.0)
|
Value::scalar(self.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
from_input_value(v: &InputValue) -> Option<UserID> {
|
from_input_value(v: &InputValue) -> Option<UserID> {
|
||||||
// If there's a parse error here, simply return None. Juniper will
|
// If there's a parse error here, simply return None. Juniper will
|
||||||
// present an error to the client.
|
// present an error to the client.
|
||||||
v.as_string_value().map(|s| UserID(s.to_owned()))
|
v.as_scalar_value::<String>().map(|s| UserID(s.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
from_str<'a>(value: ScalarToken<'a>) -> juniper::ParseScalarResult<'a, juniper::DefaultScalarValue> {
|
||||||
|
<String as juniper::ParseScalarValue>::from_str(value)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,15 +16,13 @@ similarities and the tradeoffs:
|
||||||
### Downcasting via accessor methods
|
### Downcasting via accessor methods
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -44,7 +42,7 @@ impl Character for Droid {
|
||||||
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
|
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_union!(<'a> &'a Character: () as "Character" |&self| {
|
juniper::graphql_union!(<'a> &'a Character: () as "Character" where Scalar = <S> |&self| {
|
||||||
instance_resolvers: |_| {
|
instance_resolvers: |_| {
|
||||||
// The left hand side indicates the concrete type T, the right hand
|
// The left hand side indicates the concrete type T, the right hand
|
||||||
// side should be an expression returning Option<T>
|
// side should be an expression returning Option<T>
|
||||||
|
@ -60,17 +58,17 @@ graphql_union!(<'a> &'a Character: () as "Character" |&self| {
|
||||||
|
|
||||||
FIXME: This example does not compile at the moment
|
FIXME: This example does not compile at the moment
|
||||||
|
|
||||||
```rust,ignore
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -95,7 +93,7 @@ impl Character for Droid {
|
||||||
fn id(&self) -> &str { self.id.as_str() }
|
fn id(&self) -> &str { self.id.as_str() }
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_union!(<'a> &'a Character: Database as "Character" |&self| {
|
juniper::graphql_union!(<'a> &'a Character: Database as "Character" where Scalar = <S> |&self| {
|
||||||
instance_resolvers: |&context| {
|
instance_resolvers: |&context| {
|
||||||
&Human => context.humans.get(self.id()),
|
&Human => context.humans.get(self.id()),
|
||||||
&Droid => context.droids.get(self.id()),
|
&Droid => context.droids.get(self.id()),
|
||||||
|
@ -107,19 +105,17 @@ graphql_union!(<'a> &'a Character: Database as "Character" |&self| {
|
||||||
|
|
||||||
## Placeholder objects
|
## Placeholder objects
|
||||||
|
|
||||||
FIXME: This example does not compile at the moment
|
```rust
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
#[graphql(Context = "Database")]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -136,7 +132,7 @@ struct Character {
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_union!(Character: Database |&self| {
|
juniper::graphql_union!(Character: Database where Scalar = <S> |&self| {
|
||||||
instance_resolvers: |&context| {
|
instance_resolvers: |&context| {
|
||||||
&Human => context.humans.get(&self.id),
|
&Human => context.humans.get(&self.id),
|
||||||
&Droid => context.droids.get(&self.id),
|
&Droid => context.droids.get(&self.id),
|
||||||
|
@ -149,15 +145,13 @@ graphql_union!(Character: Database |&self| {
|
||||||
## Enums
|
## Enums
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate juniper_codegen;
|
#[derive(juniper::GraphQLObject)]
|
||||||
# #[macro_use] extern crate juniper;
|
|
||||||
#[derive(GraphQLObject)]
|
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
home_planet: String,
|
home_planet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GraphQLObject)]
|
#[derive(juniper::GraphQLObject)]
|
||||||
struct Droid {
|
struct Droid {
|
||||||
id: String,
|
id: String,
|
||||||
primary_function: String,
|
primary_function: String,
|
||||||
|
@ -169,7 +163,7 @@ enum Character {
|
||||||
Droid(Droid),
|
Droid(Droid),
|
||||||
}
|
}
|
||||||
|
|
||||||
graphql_union!(Character: () |&self| {
|
juniper::graphql_union!(Character: () where Scalar = <S> |&self| {
|
||||||
instance_resolvers: |_| {
|
instance_resolvers: |_| {
|
||||||
&Human => match *self { Character::Human(ref h) => Some(h), _ => None },
|
&Human => match *self { Character::Human(ref h) => Some(h), _ => None },
|
||||||
&Droid => match *self { Character::Droid(ref d) => Some(d), _ => None },
|
&Droid => match *self { Character::Droid(ref d) => Some(d), _ => None },
|
|
@ -1,41 +0,0 @@
|
||||||
# Summary
|
|
||||||
|
|
||||||
- [Introduction](README.md)
|
|
||||||
- [Quickstart](quickstart.md)
|
|
||||||
|
|
||||||
## Type System
|
|
||||||
|
|
||||||
- [Defining objects](types/objects/defining_objects.md)
|
|
||||||
- [Complex fields](types/objects/complex_fields.md)
|
|
||||||
- [Using contexts](types/objects/using_contexts.md)
|
|
||||||
- [Error handling](types/objects/error_handling.md)
|
|
||||||
- Other types
|
|
||||||
- [Enums](types/enums.md)
|
|
||||||
- [Interfaces](types/interfaces.md)
|
|
||||||
- [Input objects](types/input_objects.md)
|
|
||||||
- [Scalars](types/scalars.md)
|
|
||||||
- [Unions](types/unions.md)
|
|
||||||
|
|
||||||
## Schema
|
|
||||||
|
|
||||||
- [Schemas and mutations](schema/schemas_and_mutations.md)
|
|
||||||
|
|
||||||
## Adding a server
|
|
||||||
|
|
||||||
- Integrations by Juniper
|
|
||||||
- [Hyper](servers/hyper.md)
|
|
||||||
- [Warp](servers/warp.md)
|
|
||||||
- [Rocket](servers/rocket.md)
|
|
||||||
- [Iron](servers/iron.md)
|
|
||||||
- Integrations by others
|
|
||||||
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
|
|
||||||
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
|
|
||||||
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)
|
|
||||||
|
|
||||||
## Advanced Topics
|
|
||||||
|
|
||||||
- [Non-struct objects](advanced/non_struct_objects.md)
|
|
||||||
- [Objects and generics](advanced/objects_and_generics.md)
|
|
||||||
- [Context switching]
|
|
||||||
- [Dynamic type system]
|
|
||||||
- [Multiple operations per request](advanced/multiple_ops_per_request.md)
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"name": "graphql-rust.github.io",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
|
||||||
"build": "gitbook install && gitbook build",
|
|
||||||
"serve": "gitbook install && gitbook serve",
|
|
||||||
"test": "(cd _skeptic && cargo test)"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"gitbook-cli": "^2.3.2",
|
|
||||||
"gitbook-plugin-codeblock-omit": "^0.0.5",
|
|
||||||
"gitbook-plugin-codeblock-rust": "^0.0.4",
|
|
||||||
"gitbook-plugin-sane-sidebar": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "_skeptic"
|
name = "juniper_book_tests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
|
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
juniper = { git = "https://github.com/graphql-rust/juniper" }
|
juniper = { version = "0.11", path = "../../../juniper" }
|
||||||
juniper_iron = { git = "https://github.com/graphql-rust/juniper" }
|
juniper_iron = { version = "0.3", path = "../../../juniper_iron" }
|
||||||
|
|
||||||
iron = "^0.5.0"
|
iron = "^0.5.0"
|
||||||
mount = "^0.3.0"
|
mount = "^0.3.0"
|
||||||
|
@ -17,4 +18,4 @@ skeptic = "0.13"
|
||||||
skeptic = "0.13"
|
skeptic = "0.13"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
juniper_codegen = { git = "https://github.com/graphql-rust/juniper" }
|
juniper_codegen = { path = "../../../juniper_codegen" }
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate skeptic;
|
extern crate skeptic;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let files = skeptic::markdown_files_of_directory("../docs/");
|
let files = skeptic::markdown_files_of_directory("../content/types");
|
||||||
skeptic::generate_doc_tests(&files);
|
skeptic::generate_doc_tests(&files);
|
||||||
}
|
}
|
|
@ -1 +1,3 @@
|
||||||
|
#![deny(warnings)]
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));
|
include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));
|
1256
docs/book/yarn.lock
1256
docs/book/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue