npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@looop/graphql-scalars

v0.5.4

Published

A collection of scalar types not included in base GraphQL.

Downloads

11

Readme

@looop/graphql-scalars

npm version Build Status

A library of custom GraphQL scalar types for creating precise type-safe GraphQL schemas.

Installation

npm install --save @looop/graphql-scalars

or

yarn add @looop/graphql-scalars

Usage

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 RegularExpression

scalar UnsignedFloat

scalar UnsignedInt

scalar URL

In your resolver map, first import them:

import {
  DateTime,
  NonPositiveInt,
  PositiveInt,
  NonNegativeInt,
  NegativeInt,
  NonPositiveFloat,
  PositiveFloat,
  NonNegativeFloat,
  NegativeFloat,
  EmailAddress,
  URL,
  PhoneNumber,
  PostalCode,
} from '@looop/graphql-scalars';

Then make sure they're in the root resolver map like this:

const myResolverMap = {
  DateTime,

  NonPositiveInt,
  PositiveInt,
  NonNegativeInt,
  NegativeInt,

  NonPositiveFloat,
  PositiveFloat,
  NonNegativeFloat,
  NegativeFloat,

  EmailAddress,
  URL,

  PhoneNumber,
  PostalCode,

  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 GraphQLScalars from '@looop/graphql-scalars';

Then make sure they're in the root resolver map like this:

const myResolverMap = {
  ...GraphQLScalars,

  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.

Usage with Apollo Server

import { ApolloServer } from 'apollo-server';
import { makeExecutableSchema } from 'graphql-tools';
// import all scalars and resolvers
import GraphQLScalars, { ScalarDefinitions } from '@looop/graphql-scalars';
// Alternatively, import individual scalars and resolvers
// import { DateTime, DateTimeScalar, ... } from "@looop/graphql-scalars"

const server = new ApolloServer({
  schema: makeExecutableSchema({
    typeDefs: [
      // use spread syntax to add scalar definitions to your schema
      ...ScalarDefinitions,
      // DateTimeScalar,
      // ...
      // ... other type definitions ...
    ],
    resolvers: {
      // use spread syntax to add scalar resolvers to your resolver map
      ...GraphQLScalars,
      // DateTime,
      // ...
      // ... remainder of resolver map ...
    },
  }),
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Using the RegularExpression scalar

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.

RegularExpression options

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);

Why?

The primary purposes these scalars, really of all types are to:

  1. 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 like ageInYears, 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.
  2. 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.

The Types

DateTime

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.

NonNegativeInt

Integers that will have a value of 0 or more. Uses parseInt().

NonPositiveInt

Integers that will have a value of 0 or less. Uses parseInt().

PositiveInt

Integers that will have a value greater than 0. Uses parseInt().

NegativeInt

Integers that will have a value less than 0. Uses parseInt().

NonNegativeFloat

Floats that will have a value of 0 or more. Uses parseFloat().

NonPositiveFloat

Floats that will have a value of 0 or less. Uses parseFloat().

PositiveFloat

Floats that will have a value greater than 0. Uses parseFloat().

NegativeFloat

Floats that will have a value less than 0. Uses parseFloat().

EmailAddress

A field whose value conforms to the standard internet email address format as specified in RFC822.

URL

A field whose value conforms to the standard URL format as specified in RFC3986.

PhoneNumber

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.

PostalCode

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.

RegularExpression

A GraphQLScalarType object generator that takes two arguments:

  • name - The name of your custom type
  • regex - The regex to be used to check against any values for fields with this new type
const MyRegexType = new RegularExpression('MyRegexType', /^ABC$/);

What's this all about?

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.

License

Released under the MIT license.

Contributing

Issues and Pull Requests are always welcome.

Please read our contribution guidelines.