@@ -6,59 +6,131 @@ but this often requires coordination with the client library intended to consume
6
6
the API you're building.
7
7
8
8
Since any value going over the wire is eventually transformed into JSON, you're
9
- also limited in the data types you can use. Typically, you represent your custom
10
- scalars as strings.
9
+ also limited in the data types you can use.
11
10
12
- In Juniper, you use the ` graphql_scalar! ` macro to create a custom scalar. In
13
- this example, we're representing a user ID as a string wrapped in a custom type:
11
+ There are two ways to define custom scalars.
12
+ * For simple scalars that just wrap a primitive type, you can use the newtype pattern with
13
+ a custom derive.
14
+ * For more advanced use cases with custom validation, you can use
15
+ the ` graphql_scalar! ` macro.
14
16
15
- ``` rust
16
- use juniper :: Value ;
17
17
18
- struct UserID ( String );
18
+ ## Built-in scalars
19
19
20
- juniper :: graphql_scalar! (UserID where Scalar = <S > {
21
- description : " An opaque identifier, represented as a string"
20
+ Juniper has built-in support for:
22
21
23
- resolve (& self ) -> Value {
24
- Value :: scalar (self . 0. clone ())
25
- }
22
+ * ` i32 ` as ` Int `
23
+ * ` f64 ` as ` Float `
24
+ * ` String ` and ` &str ` as ` String `
25
+ * ` bool ` as ` Boolean `
26
+ * ` juniper::ID ` as ` ID ` . This type is defined [ in the
27
+ spec] ( http://facebook.github.io/graphql/#sec-ID ) as a type that is serialized
28
+ as a string but can be parsed from both a string and an integer.
26
29
27
- from_input_value (v : & InputValue ) -> Option <UserID > {
28
- // If there's a parse error here, simply return None. Juniper will
29
- // present an error to the client.
30
- v . as_scalar_value :: <String >(). map (| s | UserID (s . to_owned ()))
31
- }
30
+ ** Third party types** :
32
31
33
- from_str <'a >(value : ScalarToken <'a >) -> juniper :: ParseScalarResult <'a , S > {
34
- <String as juniper :: ParseScalarValue <S >>:: from_str (value )
35
- }
36
- });
32
+ Juniper has built-in support for a few additional types from common third party
33
+ crates. They are enabled via features that are on by default.
34
+
35
+ * uuid::Uuid
36
+ * chrono::DateTime
37
+ * url::Url
38
+
39
+ ## newtype pattern
40
+
41
+ Often, you might need a custom scalar that just wraps an existing type.
42
+
43
+ This can be done with the newtype pattern and a custom derive, similar to how
44
+ serde supports this pattern with ` #[transparent] ` .
45
+
46
+ ``` rust
47
+ #[derive(juniper:: GraphQLScalarValue )]
48
+ #[graphql(transparent)]
49
+ pub struct UserId (i32 );
37
50
38
51
#[derive(juniper:: GraphQLObject )]
39
52
struct User {
40
- id : UserID ,
41
- username : String ,
53
+ id : UserId ,
42
54
}
43
55
44
56
# fn main () {}
45
57
```
46
58
47
- ## Built- in scalars
59
+ That's it, you can now user ` UserId ` in your schema.
48
60
49
- Juniper has built-in support for:
61
+ The macro also allows for more customization :
50
62
51
- * ` i32 ` as ` Int `
52
- * ` f64 ` as ` Float `
53
- * ` String ` and ` &str ` as ` String `
54
- * ` bool ` as ` Boolean `
55
- * ` juniper::ID ` as ` ID ` . This type is defined [ in the
56
- spec] ( http://facebook.github.io/graphql/#sec-ID ) as a type that is serialized
57
- as a string but can be parsed from both a string and an integer.
63
+ ``` rust
64
+ /// You can use a doc comment to specify a description.
65
+ #[derive(juniper:: GraphQLScalarValue )]
66
+ #[graphql(
67
+ transparent,
68
+ // Overwrite the GraphQL type name.
69
+ name = " MyUserId" ,
70
+ // Specify a custom description.
71
+ // A description in the attribute will overwrite a doc comment.
72
+ description = " My user id description" ,
73
+ )]
74
+ pub struct UserId (i32 );
75
+
76
+ # fn main () {}
77
+ ```
78
+
79
+ ## Custom scalars
80
+
81
+ For more complex situations where you also need custom parsing or validation,
82
+ you can use the ` graphql_scalar! ` macro.
83
+
84
+ Typically, you represent your custom scalars as strings.
85
+
86
+ The example below implements a custom scalar for a custom ` Date ` type.
87
+
88
+ Note: juniper already has built-in support for the ` chrono::DateTime ` type
89
+ via ` chrono ` feature, which is enabled by default and should be used for this
90
+ purpose.
58
91
59
- ### Non-standard scalars
92
+ The example below is used just for illustration.
60
93
61
- Juniper has built-in support for UUIDs from the [ uuid
62
- crate] ( https://doc.rust-lang.org/uuid/uuid/index.html ) . This support is enabled
63
- by default, but can be disabled if you want to reduce the number of dependencies
64
- in your application.
94
+ ** Note** : the example assumes that the ` Date ` type implements
95
+ ` std::fmt::Display ` and ` std::str::FromStr ` .
96
+
97
+
98
+ ``` rust
99
+ # mod date {
100
+ # pub struct Date ;
101
+ # impl std :: str :: FromStr for Date {
102
+ # type Err = String ; fn from_str (_value : & str ) -> Result <Self , Self :: Err > { unimplemented! () }
103
+ # }
104
+ # // And we define how to represent date as a string.
105
+ # impl std :: fmt :: Display for Date {
106
+ # fn fmt (& self , _f : & mut std :: fmt :: Formatter ) -> std :: fmt :: Result {
107
+ # unimplemented! ()
108
+ # }
109
+ # }
110
+ # }
111
+
112
+ use juniper :: {Value , ParseScalarResult , ParseScalarValue };
113
+ use date :: Date ;
114
+
115
+ juniper :: graphql_scalar! (Date where Scalar = <S > {
116
+ description : " Date"
117
+
118
+ // Define how to convert your custom scalar into a primitive type.
119
+ resolve (& self ) -> Value {
120
+ Value :: scalar (self . to_string ())
121
+ }
122
+
123
+ // Define how to parse a primitive type into your custom scalar.
124
+ from_input_value (v : & InputValue ) -> Option <Date > {
125
+ v . as_scalar_value :: <String >()
126
+ . and_then (| s | s . parse (). ok ())
127
+ }
128
+
129
+ // Define how to parse a string value.
130
+ from_str <'a >(value : ScalarToken <'a >) -> ParseScalarResult <'a , S > {
131
+ <String as ParseScalarValue <S >>:: from_str (value )
132
+ }
133
+ });
134
+
135
+ # fn main () {}
136
+ ```
0 commit comments