Use Custom Scalars instead of GraphQL primitives.
Instead of:
type Book {
publicationDate: String!
priceInCents: Int!
# cover assets are stored at
# `/covers/<coverImage>?format=<large|small>`
coverImage: String!
averageRating: Float!
id: String!
}
Try using Custom Scalars like this:
type Book {
publicationDate: DateTime!
price: USD!
coverImage: CoverImage!
averageRating: StarRating!
id: BookId!
}
DateTime
-> Elm Time
)ProductId
and UserId
might both be Strings, but if they are Custom Scalars then elm-graphql
can prevent you from passing in the wrong type.Temperature
or USD
Custom Scalar has implementation details (perhaps USD
is represented in cents, but you don't want to leak that knowledge all over your codebase, you want to keep it in one central location).Custom Scalars are a way of representing a contract.
For example, a DateTime
is just a String
under the hood. But there is a contract. When I call a value a DateTime
, I am promising that you will always be able to parse it as an ISO-8601 String (or however it is represented in your API).
The data in your GraphQL response will actually be the same. But by using the DateTime
Custom Scalar, you are telling elm-graphql
that it is safe to use it in a specific way. In this case, it may be that it can parse it as an ISO-8601 String.
Some other examples would be representing Currency (perhaps USD
). Again, the underlying representative is just a GraphQL primitive, but you've now given it semantic meaning. And elm-graphql
is able to take this semantic meaning and turn it into a specific type.
GraphQL provides a type called Id
. I recommend ignoring this type and instead creating a Custom Scalar to represent each type of Id in your domain. For example ProductId
or UserId
.
Why bother?
UserId
where the API requires a ProductId
Custom Scalars also provide a pinch point that allows you to limit all the knowledge about how to use a certain type to a single place. For example, you can use an Opaque Type to make sure that the only way USD
is displayed is by asking the USD.elm
module to display it for you. This gives you confidence that the knowledge of how to turn the underlying data representation for USD
is not leaked throughout your codebase, which reduces bugs and makes it easier to reason about your code.
If you're not using the Custom Scalar Codecs functionality in elm-graphql
. Take a look at the official Custom Scalar Codecs example and instructions.
Here's an example illustrating this with USD
.
module USD exposing (USD, codec, toString)
import Graphql.Codec exposing (Codec)
import Json.Decode exposing (Decoder)
import Json.Encode
type USD
= Dollars Int
codec : Codec USD
codec =
{ encoder = encode
, decoder = decoder
}
decoder : Decoder USD
decoder =
Json.Decode.map Dollars Json.Decode.int
encode : USD -> Json.Encode.Value
encode (Dollars dollars) =
Json.Encode.int dollars
toString : USD -> String
toString (Dollars dollars ) =
"$" ++ String.fromInt dollars
The Custom Scalar Checklist guides you through simple, actionable steps to identify where you should be using Custom Scalars in your schema.
Download it now to get the descriptions and examples of all of these types of fields which should be turned into Custom Scalars:
We'll never share your email. Unsubscribe any time.
Sign up to get my latest Elm posts and course notifications in your inbox.
Pure Elm content. Unsubscribe any time.