Skip to content

consider memoizing getArgumentValues #383

Open
@mnpenner

Description

@mnpenner

Maybe I don't understand something, but I didn't think it would be necessary for GraphQLScalarType.parseLiteral to be called every time a field argument is resolved.

e.g., given this GraphQL query:

{
  clients(limit: 10) {
    id,
    fullName,
    birthDate,
    deathDate,
    age(asOf: "Jan 1st, 1987 @ 04:12:34.456 -0500")
  }
}

And this field:

export default new GraphQLObjectType({
    name: 'Client',
    description: `Person served`,
    fields: () => ({
        age: {
            type: GraphQLString,
            description: `Age in "#y #m" format`,
            args: {
                asOf: {
                    type: DateTime,
                    description: `Date to calculate age from`
                }
            },

            resolve: (client, {asOf}) => {
                dump(asOf.format('D-MMM-YYYY @ H:mm:ss.SSS Z'));
                ...

And this custom type:

export default new GraphQLScalarType({
    name: "DateTime",
    description: `Scalar type representing a date and time with offset, serialized as a string in ISO-8601 date format.\n\ne.g. \`"2016-05-05T20:16:06Z"\``,
    serialize(value) {
        let date;
        if(_.isNumber(value)) {
            if(value === 0) {
                return null;
            }
            date = moment.unix(value);
        } else {
            date = moment(value);
        }
        if(!date.isValid()) {
            throw new GraphQLError(`Serialization error: ${value} is not a valid date`)
        }
        return date.format();
    },
    parseValue(value) {
        // see https://gist.github.com/olange/f6c57d3ca577955fc3a51aa62f88c948
        // or https://github.com/soundtrackyourbrand/graphql-custom-datetype/blob/master/datetype.js
        // but parse it with moment.js
        throw new GraphQLError(`parseValue(${value}) not implemented`)
    },
    parseLiteral(ast) {
        if(ast.kind !== Kind.STRING && ast.kind !== Kind.INT) {
            throw new GraphQLError(`Parse error: expected date string, got ${JSON.stringify(ast.value)}`, [ast]);
        }

        let result = parseDate(ast.value);

        if(!result.isValid()) {
            throw new GraphQLError(`Invalid date: ${JSON.stringify(ast.value)}`);
        }

        return result;
    }
});

The asOf argument is parsed every time resolve is called -- i.e., 10 times for this one query because I've limited the results to 10. It so happens that my parseDate function is slow, this is kind of painful. I can memoize it on my end, but I didn't think that should be necessary.

Why aren't all the literals parsed just once when the query is received?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions