Skip to content

Commit 9167654

Browse files
LegNeatoarlyon
andauthored
Add support for GraphQL Schema Language (#676)
Co-authored-by: Alexander Lyon <[email protected]>
1 parent 40ad17c commit 9167654

File tree

10 files changed

+592
-5
lines changed

10 files changed

+592
-5
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ see the [actix][actix_examples], [hyper][hyper_examples], [rocket][rocket_exampl
4848

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

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

58+
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.
59+
5960
## Integrations
6061

6162
### Data types
@@ -91,6 +92,8 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
9192
[playground]: https://github.com/prisma/graphql-playground
9293
[iron]: http://ironframework.io
9394
[graphql_spec]: http://facebook.github.io/graphql
95+
[schema_language]: https://graphql.org/learn/schema/#type-language
96+
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
9497
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs
9598
[tokio]: https://github.com/tokio-rs/tokio
9699
[actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples

docs/book/content/quickstart.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

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

5+
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.
6+
57
## Installation
68

79
!FILENAME Cargo.toml
@@ -193,6 +195,8 @@ fn main() {
193195
}
194196
```
195197

198+
[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
199+
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
196200
[hyper]: servers/hyper.md
197201
[warp]: servers/warp.md
198202
[rocket]: servers/rocket.md

docs/book/content/schema/schemas_and_mutations.md

+51
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Schemas
22

3+
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.
4+
35
A schema consists of three types: a query object, a mutation object, and a subscription object.
46
These three define the root query fields, mutations and subscriptions of the schema, respectively.
57

@@ -60,6 +62,55 @@ impl Mutations {
6062
# fn main() { }
6163
```
6264

65+
# Outputting schemas in the [GraphQL Schema Language][schema_language]
66+
67+
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):
68+
69+
```rust
70+
# // Only needed due to 2018 edition because the macro is not accessible.
71+
# #[macro_use] extern crate juniper;
72+
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};
73+
74+
struct Query;
75+
76+
#[juniper::graphql_object]
77+
impl Query {
78+
fn hello(&self) -> FieldResult<&str> {
79+
Ok("hello world")
80+
}
81+
}
82+
83+
fn main() {
84+
// Define our schema in Rust.
85+
let schema = RootNode::new(
86+
Query,
87+
EmptyMutation::<()>::new(),
88+
EmptySubscription::<()>::new(),
89+
);
90+
91+
// Convert the Rust schema into the GraphQL Schema Language.
92+
let result = schema.as_schema_language();
93+
94+
let expected = "\
95+
type Query {
96+
hello: String!
97+
}
98+
99+
schema {
100+
query: Query
101+
}
102+
";
103+
assert_eq!(result, expected);
104+
}
105+
```
106+
107+
Note the `schema-language` feature may be turned off if you do not need this functionality to reduce dependencies and speed up
108+
compile times.
109+
110+
111+
[schema_language]: https://graphql.org/learn/schema/#type-language
112+
[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
113+
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
63114
[section]: ../advanced/subscriptions.md
64115
[EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html
65116
<!--TODO: Fix This URL when the EmptySubscription become available in the Documentation -->

juniper/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Features
44

5+
- 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))
6+
- 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.
7+
58
- Normalization for the subscriptions_endpoint_url in the `graphiql_source`.
69
(See [#628](https://github.com/graphql-rust/juniper/pull/628) for more details)
710

juniper/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ path = "benches/bench.rs"
2525

2626
[features]
2727
expose-test-schema = ["serde_json"]
28+
schema-language = ["graphql-parser-integration"]
29+
graphql-parser-integration = ["graphql-parser"]
2830
default = [
2931
"bson",
3032
"chrono",
3133
"url",
3234
"uuid",
35+
"schema-language",
3336
]
3437
scalar-naivetime = []
3538

@@ -46,6 +49,7 @@ serde_json = { version="1.0.2", optional = true }
4649
static_assertions = "1.1"
4750
url = { version = "2", optional = true }
4851
uuid = { version = "0.8", optional = true }
52+
graphql-parser = {version = "0.3.0", optional = true }
4953

5054
[dev-dependencies]
5155
bencher = "0.1.2"

juniper/src/schema/meta.rs

+32
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ pub struct Field<'a, S> {
169169
pub deprecation_status: DeprecationStatus,
170170
}
171171

172+
impl<'a, S> Field<'a, S> {
173+
/// Returns true if the type is built-in to GraphQL.
174+
pub fn is_builtin(&self) -> bool {
175+
// "used exclusively by GraphQL’s introspection system"
176+
self.name.starts_with("__")
177+
}
178+
}
179+
172180
/// Metadata for an argument to a field
173181
#[derive(Debug, Clone)]
174182
pub struct Argument<'a, S> {
@@ -182,6 +190,14 @@ pub struct Argument<'a, S> {
182190
pub default_value: Option<InputValue<S>>,
183191
}
184192

193+
impl<'a, S> Argument<'a, S> {
194+
/// Returns true if the type is built-in to GraphQL.
195+
pub fn is_builtin(&self) -> bool {
196+
// "used exclusively by GraphQL’s introspection system"
197+
self.name.starts_with("__")
198+
}
199+
}
200+
185201
/// Metadata for a single value in an enum
186202
#[derive(Debug, Clone)]
187203
pub struct EnumValue {
@@ -368,6 +384,22 @@ impl<'a, S> MetaType<'a, S> {
368384
}
369385
}
370386

387+
/// Returns true if the type is built-in to GraphQL.
388+
pub fn is_builtin(&self) -> bool {
389+
if let Some(name) = self.name() {
390+
// "used exclusively by GraphQL’s introspection system"
391+
{
392+
name.starts_with("__") ||
393+
// <https://facebook.github.io/graphql/draft/#sec-Scalars>
394+
name == "Boolean" || name == "String" || name == "Int" || name == "Float" || name == "ID" ||
395+
// Our custom empty markers
396+
name == "_EmptyMutation" || name == "_EmptySubscription"
397+
}
398+
} else {
399+
false
400+
}
401+
}
402+
371403
pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> {
372404
schema
373405
.lookup_type(&self.as_type())

juniper/src/schema/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
pub mod meta;
44
pub mod model;
55
pub mod schema;
6+
pub mod translate;

0 commit comments

Comments
 (0)