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 support for GraphQL Schema Language #676

Merged
merged 17 commits into from
Jun 6, 2020
Merged
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;
Loading