Skip to content

Commit

Permalink
Add support for GraphQL Schema Language (#676)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexander Lyon <arlyon@me.com>
  • Loading branch information
LegNeato and arlyon committed Jun 6, 2020
1 parent 40ad17c commit 9167654
Show file tree
Hide file tree
Showing 10 changed files with 592 additions and 5 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ see the [actix][actix_examples], [hyper][hyper_examples], [rocket][rocket_exampl

Juniper supports the full GraphQL query language according to the
[specification][graphql_spec], including interfaces, unions, schema
introspection, and validations.
It does not, however, support the schema language. Consider using [juniper-from-schema][] for generating code from a schema file.
introspection, and validations. It can also output the schema in the [GraphQL Schema Language][schema_language].

As an exception to other GraphQL libraries for other languages, Juniper builds
non-null types by default. A field of type `Vec<Episode>` will be converted into
`[Episode!]!`. The corresponding Rust type for e.g. `[Episode]` would be
`Option<Vec<Option<Episode>>>`.

Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.

## Integrations

### Data types
Expand Down Expand Up @@ -91,6 +92,8 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
[playground]: https://github.com/prisma/graphql-playground
[iron]: http://ironframework.io
[graphql_spec]: http://facebook.github.io/graphql
[schema_language]: https://graphql.org/learn/schema/#type-language
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs
[tokio]: https://github.com/tokio-rs/tokio
[actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples
Expand Down
4 changes: 4 additions & 0 deletions docs/book/content/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This page will give you a short introduction to the concepts in Juniper.

Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.

## Installation

!FILENAME Cargo.toml
Expand Down Expand Up @@ -193,6 +195,8 @@ fn main() {
}
```

[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[hyper]: servers/hyper.md
[warp]: servers/warp.md
[rocket]: servers/rocket.md
Expand Down
51 changes: 51 additions & 0 deletions docs/book/content/schema/schemas_and_mutations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Schemas

Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.

A schema consists of three types: a query object, a mutation object, and a subscription object.
These three define the root query fields, mutations and subscriptions of the schema, respectively.

Expand Down Expand Up @@ -60,6 +62,55 @@ impl Mutations {
# fn main() { }
```

# Outputting schemas in the [GraphQL Schema Language][schema_language]

Many tools in the GraphQL ecosystem require the schema to be defined in the [GraphQL Schema Language][schema_language]. You can generate a [GraphQL Schema Language][schema_language] representation of your schema defined in Rust using the `schema-language` feature (on by default):

```rust
# // Only needed due to 2018 edition because the macro is not accessible.
# #[macro_use] extern crate juniper;
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};

struct Query;

#[juniper::graphql_object]
impl Query {
fn hello(&self) -> FieldResult<&str> {
Ok("hello world")
}
}

fn main() {
// Define our schema in Rust.
let schema = RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);

// Convert the Rust schema into the GraphQL Schema Language.
let result = schema.as_schema_language();

let expected = "\
type Query {
hello: String!
}
schema {
query: Query
}
";
assert_eq!(result, expected);
}
```

Note the `schema-language` feature may be turned off if you do not need this functionality to reduce dependencies and speed up
compile times.


[schema_language]: https://graphql.org/learn/schema/#type-language
[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[section]: ../advanced/subscriptions.md
[EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html
<!--TODO: Fix This URL when the EmptySubscription become available in the Documentation -->
Expand Down
3 changes: 3 additions & 0 deletions juniper/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Features

- Added support for outputting the Rust schema in the [GraphQL Schema Language](https://graphql.org/learn/schema/#type-language). ([#676](https://github.com/graphql-rust/juniper/pull/676))
- This is controlled by the `schema-language` feature and is on by default. It may be turned off if you do not need this functionality to reduce dependencies and speed up compile times.

- Normalization for the subscriptions_endpoint_url in the `graphiql_source`.
(See [#628](https://github.com/graphql-rust/juniper/pull/628) for more details)

Expand Down
4 changes: 4 additions & 0 deletions juniper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ path = "benches/bench.rs"

[features]
expose-test-schema = ["serde_json"]
schema-language = ["graphql-parser-integration"]
graphql-parser-integration = ["graphql-parser"]
default = [
"bson",
"chrono",
"url",
"uuid",
"schema-language",
]
scalar-naivetime = []

Expand All @@ -46,6 +49,7 @@ serde_json = { version="1.0.2", optional = true }
static_assertions = "1.1"
url = { version = "2", optional = true }
uuid = { version = "0.8", optional = true }
graphql-parser = {version = "0.3.0", optional = true }

[dev-dependencies]
bencher = "0.1.2"
Expand Down
32 changes: 32 additions & 0 deletions juniper/src/schema/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ pub struct Field<'a, S> {
pub deprecation_status: DeprecationStatus,
}

impl<'a, S> Field<'a, S> {
/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
// "used exclusively by GraphQL’s introspection system"
self.name.starts_with("__")
}
}

/// Metadata for an argument to a field
#[derive(Debug, Clone)]
pub struct Argument<'a, S> {
Expand All @@ -182,6 +190,14 @@ pub struct Argument<'a, S> {
pub default_value: Option<InputValue<S>>,
}

impl<'a, S> Argument<'a, S> {
/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
// "used exclusively by GraphQL’s introspection system"
self.name.starts_with("__")
}
}

/// Metadata for a single value in an enum
#[derive(Debug, Clone)]
pub struct EnumValue {
Expand Down Expand Up @@ -368,6 +384,22 @@ impl<'a, S> MetaType<'a, S> {
}
}

/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
if let Some(name) = self.name() {
// "used exclusively by GraphQL’s introspection system"
{
name.starts_with("__") ||
// <https://facebook.github.io/graphql/draft/#sec-Scalars>
name == "Boolean" || name == "String" || name == "Int" || name == "Float" || name == "ID" ||
// Our custom empty markers
name == "_EmptyMutation" || name == "_EmptySubscription"
}
} else {
false
}
}

pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> {
schema
.lookup_type(&self.as_type())
Expand Down
1 change: 1 addition & 0 deletions juniper/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
pub mod meta;
pub mod model;
pub mod schema;
pub mod translate;

0 comments on commit 9167654

Please sign in to comment.