A library of custom GraphQL scalar types for creating precise type-safe GraphQL schemas.
npm install --save graphql-scalars
or
yarn add graphql-scalars
To use these scalars you'll need to add them in two places, your schema and your resolvers map.
NOTE: The new RegularExpression
scalar will be used a little differently and is explained below.
In your schema:
scalar DateTime
scalar EmailAddress
scalar NegativeFloat
scalar NegativeInt
scalar NonNegativeFloat
scalar NonNegativeInt
scalar NonPositiveFloat
scalar NonPositiveInt
scalar PhoneNumber
scalar PositiveFloat
scalar PositiveInt
scalar PostalCode
scalar UnsignedFloat
scalar UnsignedInt
scalar URL
scalar ObjectID
scalar BigInt
scalar Long
scalar GUID
scalar HexColorCode
scalar HSL
scalar HSLA
scalar IPv4
scalar IPv6
scalar ISBN
scalar MAC
scalar Port
scalar RGB
scalar RGBA
scalar USCurrency
scalar JSON
scalar JSONObject
In your resolver map, first import them:
import {
DateTimeResolver,
EmailAddressResolver,
NegativeFloatResolver,
NegativeIntResolver,
NonNegativeFloatResolver,
NonNegativeIntResolver,
NonPositiveFloatResolver,
NonPositiveIntResolver,
PhoneNumberResolver,
PositiveFloatResolver,
PositiveIntResolver,
PostalCodeResolver,
UnsignedFloatResolver,
UnsignedIntResolver,
URLResolver,
BigIntResolver,
LongResolver,
GUIDResolver,
HexColorCodeResolver,
HSLResolver,
HSLAResolver,
IPv4Resolver,
IPv6Resolver,
ISBNResolver,
MACResolver,
PortResolver,
RGBResolver,
RGBAResolver,
USCurrencyResolver,
JSONResolver,
JSONObjectResolver,
ObjectIDResolver,
} from 'graphql-scalars';
Then make sure they're in the root resolver map like this:
const myResolverMap = {
ObjectID: ObjectIDResolver,
DateTime: DateTimeResolver,
NonPositiveInt: NonPositiveIntResolver,
PositiveInt: PositiveIntResolver,
NonNegativeInt: NonNegativeIntResolver,
NegativeInt: NegativeIntResolver,
NonPositiveFloat: NonPositiveFloatResolver,
PositiveFloat: PositiveFloatResolver,
NonNegativeFloat: NonNegativeFloatResolver,
NegativeFloat: NegativeFloatResolver,
UnsignedFloat: UnsignedFloatResolver,
UnsignedInt: UnsignedIntResolver,
BigInt: BigIntResolver,
Long: LongResolver,
EmailAddress: EmailAddressResolver,
URL: URLResolver,
PhoneNumber: PhoneNumberResolver,
PostalCode: PostalCodeResolver,
GUID: GUIDResolver,
HexColorCode: HexColorCodeResolver,
HSL: HSLResolver,
HSLA: HSLAResolver,
RGB: RGBResolver,
RGBA: RGBAResolver,
IPv4: IPv4Resolver,
IPv6: IPv6Resolver,
MAC: MACResolver,
Port: PortResolver,
ISBN: ISBNResolver,
USCurrency: USCurrencyResolver,
JSON: JSONResolver,
JSONObject: JSONObjectResolver,
Query: {
// more stuff here
},
Mutation: {
// more stuff here
},
};
NOTE: NonNegativeFloat
and NonNegativeInt
are also available under the aliases UnsignedFloat
and UnsignedInt
, respectively.
Alternatively, use the default import and ES6's spread operator syntax:
import { resolvers } from 'graphql-scalars';
Then make sure they're in the root resolver map like this:
const myResolverMap = {
...resolvers,
Query: {
// more stuff here
},
Mutation: {
// more stuff here
},
};
That's it. Now you can use these scalar types in your schema definition like this:
type Person {
birthDate: DateTime
ageInYears: PositiveInt
heightInInches: PositiveFloat
minimumHourlyRate: NonNegativeFloat
currentlyActiveProjects: NonNegativeInt
email: EmailAddress
homePage: URL
phoneNumber: PhoneNumber
homePostalCode: PostalCode
}
These scalars can be used just like the base, built-in ones.
import { ApolloServer } from 'apollo-server';
import { makeExecutableSchema } from 'graphql-tools';
// import all scalars and resolvers
import { typeDefs, resolvers } from 'graphql-scalars';
// Alternatively, import individual scalars and resolvers
// import { DateTimeResolver, DateTimeTypeDefinition, ... } from "graphql-scalars"
const server = new ApolloServer({
schema: makeExecutableSchema({
typeDefs: [
// use spread syntax to add scalar definitions to your schema
...typeDefs,
// DateTimeTypeDefinition,
// ...
// ... other type definitions ...
],
resolvers: {
// use spread syntax to add scalar resolvers to your resolver map
...resolvers,
// DateTimeResolver,
// ...
// ... remainder of resolver map ...
},
}),
});
server.listen().then(({ url }) => {
console.log(`๐ Server ready at ${url}`);
});
import { ApolloServer } from 'apollo-server';
import { makeExecutableSchema } from 'graphql-tools';
// import all scalars and resolvers
import { typeDefs, resolvers, mocks } from 'graphql-scalars';
// Alternatively, import individual scalars and resolvers
// import { DateTimeResolver, DateTimeTypeDefinition, DateTimeMock, ... } from "graphql-scalars"
const server = new ApolloServer({
typeDefs: [
// use spread syntax to add scalar definitions to your schema
...typeDefs,
// DateTimeTypeDefinition,
// ...
// ... other type definitions ...
],
resolvers: {
// use spread syntax to add scalar resolvers to your resolver map
...resolvers,
// DateTimeResolver,
// ...
// ... remainder of resolver map ...
},
mocks: {
// use spread syntax to add scalar resolvers to your resolver map
...mocks,
// DateTimeMock,
// ...
// ... other mocks ...
}
});
const { ApolloServer } = require('apollo-server-express');
// Import individual scalars and resolvers
const { DateTimeResolver, DateTimeTypeDefinition } = require('graphql-scalars');
const server = new ApolloServer({
typeDefs: [
DateTimeTypeDefinition,
...yourTypeDefs,
],
resolvers: [
{ DateTime: DateTimeResolver }, // <-- Notable difference here
...yourResolvers,
],
});
server.listen().then(({ url }) => {
console.log(`๐ Server ready at ${url}`);
});
First an explanation: To create a new scalar type to the GraphQL schema language, you must create an
instance of a new GraphQLScalarType
object that implements three general functions/methods:
serialize
, parseValue
and parseLiteral
which are used at different stages of processing your
GraphQL types during queries and mutations. So creating a new scalar looks like this:
const MyScalar = new GraphQLScalarType({
'MyScalar',
description: 'A description of my scalar',
serialize(value) {
// ...
return value;
},
parseValue(value) {
// ...
return value;
},
parseLiteral(ast) {
// ...
return ast.value;
}
});
Given this, if we want to create a new type that is essentially the same except for one little
customizable aspect (e.g., a regular expression type that has all the same code except the regex is
different) then we need to dynamically generate a new GraphQLScalarType
object given some
parameters. That's the approach we take here.
Therefore the RegularExpression
scalar type is really a GraphQLScalarType
object generator
that takes two arguments:
- a name
- the regex you want it to use
So to create a new scalar for a given regex, you will do this:
const MyRegexType = new RegularExpression('MyRegexType', /^ABC$/);
Now MyRegexType
is your new GraphQL scalar type that will enforce a value of, in this case, "ABC".
Add your new scalar type to your resolver map:
export default {
MyRegexType,
};
And to your schema:
scalar MyRegexType
That's it. Now you can use MyRegexType
as a type in the rest of your schema.
There is an optional third options
argument to the RegularExpression constructor that can be used like this:
const options = {
errorMessage: (regex, value) => {
if (process.env.NODE_ENV === 'production')
return `Value is invalid format: ${value} `;
else
return `Value does not match the regular expression ${regex}: ${value}`;
},
};
const MyRegexType = new RegularExpression('MyRegexType', /^ABC$/, options);
The primary purposes these scalars, really of all types are to:
- Communicate to users of your schema exactly what they can expect or to at least reduce
ambiguity in cases where that's possible. For example if you have a
Person
type in your schema and that type has as field likeageInYears
, the value of that can only be null or a positive integer (or float, depending on how you want your schema to work). It should never be zero or negative. - Run-time type checking. GraphQL helps to tighten up the contract between client and server. It does this with strong typing of the interface (or schema). This helps us have greater confidence about what we're receiving from the server and what the server is receiving from the client.
This package adds to the base options available in GraphQL to support types that are reasonably common in defining schemas or interfaces to data.
Use real JavaScript Dates for GraphQL fields. Currently you can use a String or an Int (e.g., a timestamp in milliseconds) to represent a date/time. This scalar makes it easy to be explicit about the type and have a real JavaScript Date returned that the client can use without doing the inevitable parsing or conversion themselves.
Integers that will have a value of 0 or more. Uses parseInt()
.
Integers that will have a value of 0 or less. Uses parseInt()
.
Integers that will have a value greater than 0. Uses parseInt()
.
Integers that will have a value less than 0. Uses parseInt()
.
Floats that will have a value of 0 or more. Uses parseFloat()
.
Floats that will have a value of 0 or less. Uses parseFloat()
.
Floats that will have a value greater than 0. Uses parseFloat()
.
Floats that will have a value less than 0. Uses parseFloat()
.
A field whose value conforms to the standard internet email address format as specified in RFC822.
A field whose value conforms to the standard URL format as specified in
RFC3986, and it uses real JavaScript URL
objects.
A field whose value conforms to the standard E.164 format as specified in
E.164 specification. Basically this is +17895551234
.
The very powerful
libphonenumber
library is available to take
that format, parse and display it in whatever display format you want. It can also be used to
parse user input and get the E.164 format to pass into a schema.
A field whose value conforms to the mongodb object id format as explained in the documentation
We're going to start with a limited set as suggested here and here.
Which gives us the following countries:
- US - United States
- UK - United Kingdom
- DE - Germany
- CA - Canada
- FR - France
- IT - Italy
- AU - Australia
- NL - Netherlands
- ES - Spain
- DK - Denmark
- SE - Sweden
- BE - Belgium
- IN - India
This is really a practical decision of weight (of the package) vs. completeness.
In the future we might expand this list and use the more comprehensive list found here.
A long integer type for graphql-js. This implementation gives you more than 32 bits rather than the default 32-bit GraphQLInt. It uses native BigInt
implementation of JavaScript.
The GraphQL spec limits its Int type to 32-bits. Maybe you've seen this error before:
Issue on graphql-js
GraphQLError: Argument "num" has invalid value 9007199254740990.
Expected type "Int", found 9007199254740990.
Based on graphql-bigint
A field whose value is a generic Globally Unique Identifier.
A field whose value is a hexadecimal.
A field whose value is a hex color code.
A field whose value is a CSS HSL color.
A field whose value is a IPv4 address.
A field whose value is a IPv6 address.
A field whose value is a ISBN-10 or ISBN-13 number.
A field whose value is a IEEE 802 48-bit MAC address.
A field whose value is a valid TCP port within the range of 0 to 65535.
A field whose value is a CSS RGB color.
A field whose value is a CSS RGBA color.
A US currency string, such as $21.25.
The JSON
scalar type represents JSON values as specified by ECMA-404.
Uses graphql-type-json
The JSONObject
scalar type represents JSON objects as specified by ECMA-404.
Uses graphql-type-json
Includes IBAN specifications for the following countries:
- AD - Andorra
- AE - United Arab Emirates
- AL - Albania
- AO - Angola
- AT - Austria
- AZ - Azerbaijan
- BA - Bosnia and Herzegovina
- BE - Belgium
- BF - Burkina Faso
- BG - Bulgaria
- BH - Bahrain
- BI - Burundi
- BJ - Benin
- BR - Brazil
- BY - Belarus
- CH - Switzerland
- CI - Cรดte d'Ivoire
- CM - Cameroon
- CR - Costa Rica
- CV - Cabo Verde
- CY - Cyprus
- DE - Germany
- DK - Denmark
- DO - Dominican Republic
- DZ - Algeria
- EE - Estonia
- ES - Spain
- FI - Finland
- FO - Faroe Islands
- FR - France
- GB - United Kingdom of Great Britain and Northern Ireland
- GE - Georgia
- GI - Gibraltar
- GL - Greenland
- GR - Greece
- GT - Guatemala
- HR - Croatia
- HU - Hungary
- IE - Ireland
- IL - Israel
- IQ - Iraq
- IR - Iran (Islamic Republic of)
- IS - Iceland
- IT - Italy
- JO - Jordan
- KW - Kuwait
- KZ - Kazakhstan
- LB - Lebanon
- LC - Saint Lucia
- LI - Liechtenstein
- LT - Lithuania
- LU - Luxembourg
- LV - Latvia
- MC - Monaco
- MD - Moldova, Republic of
- ME - Montenegro
- MG - Madagascar
- MK - North Macedonia
- ML - Mali
- MR - Mauritania
- MT - Malta
- MU - Mauritius
- MZ - Mozambique
- NL - Netherlands
- NO - Norway
- PK - Pakistan
- PL - Poland
- PS - Palestine, State of
- PT - Portugal
- QA - Qatar
- RO - Romania
- RS - Serbia
- SA - Saudi Arabia
- SC - Seychelles
- SE - Sweden
- SI - Slovenia
- SK - Slovakia
- SM - San Marino
- SN - Senegal
- ST - Sao Tome and Principe
- SV - El Salvador
- TL - Timor-Leste
- TN - Tunisia
- TR - Turkey
- UA - Ukraine
- VA - Holy See
- VG - Virgin Islands (British)
- XK - Kosovo
A GraphQLScalarType
object generator that takes two arguments:
name
- The name of your custom typeregex
- The regex to be used to check against any values for fields with this new type
const MyRegexType = new RegularExpression('MyRegexType', /^ABC$/);
GraphQL is a wonderful new approach to application data and API layers that's gaining momentum. If you have not heard of it, start here and check out Apollo also.
However, for all of GraphQL's greatness. It is missing a couple things that we have (and you might) find very useful in defining your schemas. Namely GraphQL has a limited set of scalar types and we have found there are some additional scalar types that are useful in being more precise in our schemas. Thankfully, those sharp GraphQL folks provided a simple way to add new custom scalar types if needed. That's what this package does.
NOTE: We don't fault the GraphQL folks for these omissions. They have kept the core small and clean. Arguably not every project needs these additional scalar types. But we have, and now you can use them too if needed.
Released under the MIT license.
Issues and Pull Requests are always welcome.
Please read our contribution guidelines.
This library was originally published as @okgrow/graphql-scalars
.
It was created and maintained by the company ok-grow
.
We, The Guild, took over the maintaince of that library later on.
We also like to say thank you to @adriano-di-giovanni for being extremely generous and giving us the graphql-scalars
name on npm which was previously owned by his own library.