0

I'm building a GraphQL Server where I need to do some sort of validation before committing data to database (MongoDB and Mongoose).

One of these checks is related to unique fields. So, a model may have one or more unique fields and I need to be able to check for that before saving into database.

So, I have build some helper functions to do it and the code is below:

Helper code:

import mongoose from "mongoose";

const isFieldUnique = (modelName, fieldName, fieldValue) => {

    let model = mongoose.model(modelName);
    let query = {};
    query[fieldName] = fieldValue;
    return model.findOne(query).exec();
};

const executeUniquePromises = (uniques, modelName, data) => {

    let promises = [];
    uniques.map(name => {

        let value = data[name];
        if (!value)
            throw new Error("Cannot test uniqueness for a null field.");

        promises.push(
            isFieldUnique(modelName, name, value)
            .then(value => {
                if (value) {
                    let error = name + ' is not unique';
                    console.log(error);
                    return error;
                }

                console.log(name + ' is unique');
                return null;
            })
            .catch(error => {
                throw new Error(error);
            })
        )
    });

    return Promise.all(promises);
};

export const checkUniqueness = (uniques, modelName, data) => {

    return new Promise((resolve, reject) => {

        executeUniquePromises(uniques, modelName, data).then(result => {

            let errors = [];

            // Check for errors
            result.map((error) =>  {
                if (error)
                    errors.push(error);
            });

            if (errors.length > 0)
                return reject(errors);
            else
                resolve();
        });
    });
}

Mongoose static create function:

import * as helper from './helper';

schema.statics.create = function (data) {

    let uniques = ['name', 'email'];
    helper.checkUniqueness(uniques,'Company', data)
    .then(result => {
            let user = new this(data);
            return company.save();
    })
    .catch(error => {
            throw new Error(error);
    });
}

GraphQL code:

const createUser = {
    type: UserType,
    description: "Create a user",
    args: {
        data: {
            name: "user",
            type: new GraphQLNonNull(UserInputType)
        }
    },
    resolve(root, args) {
        return UserModel.create(args.data);
    }
};

The helper code seens to be confused and I´m not using my usage of promises with other promises are the correct way of doing it.

Remember that I may need to check several fields for uniqueness, so that is why I´ve created the promise array.

One problem is that when I´m inserting data where there are not uniques matching I get no return in my GraphQL Server.

I want to find out a better way of doing it and discover why I´m not getting back the saved object.

halfer
  • 19,824
  • 17
  • 99
  • 186
Mendes
  • 17,489
  • 35
  • 150
  • 263

1 Answers1

3

MongoDB already handles unique out of the box. Set the field to unique: true in the Mongoose schema. You can use mongoose-beautiful-unique to make the error messages similar to the validation error messages. And finally, read this when you can't get unique: true to work.

Mika Sundland
  • 18,120
  • 16
  • 38
  • 50
  • I know about that, but this is not usefull for my GraphQL server as the error message will not be application standard. – Mendes Nov 01 '17 at 10:07
  • You can get the message exactly like the ordinary validation errors in just a few lines. Only downside is that ordinary validation is run before sending to the DB, while the unique constraint is checked by the DB itself. So ordinary validation messages will never be shown at the same time as unique validation messages. – Mika Sundland Nov 01 '17 at 12:22