Giter Site home page Giter Site logo

validate-joi's Introduction

feathers-validate-joi

Feathers hook utility for schema validation and sanitization using Joi. Joi error messages are converted to web/mobile friendly formats, and optionally translated for clarity or internationalization.

Build Status Coverage Status

Installation

npm install feathers-validate-joi --save

yarn add feathers-validate-joi

Usage Example

const Joi = require('joi');
const validate = require('feathers-validate-joi');

const name = Joi.string().trim().min(5).max(30)
  .regex(/^[\sa-zA-Z0-9]*$/, 'letters, numbers and spaces').required();
const password = Joi.string().trim().min(2).max(30).required();
const schema = Joi.object().keys({
  name: name,
  password,
  confirmPassword: password.label('Confirm password'),
});
const joiOptions = { convert: true, abortEarly: false };

(1) Validate sanitize data. The client receives any errors in a format suitable for forms which also seems to be recommend by Feathers.

export.before = {
  create: [ validate.form(schema, joiOptions) ],
  update: [ validate.form(schema, joiOptions) ],
  patch: [ validate.form(schema, joiOptions) ]
};

(2) Errors are returned in a Mongoose format.

export.before = {
  create: [ validate.mongoose(schema, joiOptions) ],
  update: [ validate.mongoose(schema, joiOptions) ],
  patch: [ validate.mongoose(schema, joiOptions) ]
};

(3) Internationalize or clarify Joi error messages.

function i18n(str) { return str; } // internationalization

const translations = {
  'string.min': () => i18n('"${key}" must be ${limit} or more chars.'),
  'string.regex.base': (context) => {
    switch (context.pattern.toString()) {
      case /^[\sa-zA-Z0-9]{5,30}$/.toString():
        return i18n('"${key}" must consist of letters, digits or spaces.');
    }
  }
};

export.before = {
  create: [ validate.mongoose(schema, joiOptions, translations) ],
  update: [ validate.mongoose(schema, joiOptions, translations) ],
  patch: [ validate.mongoose(schema, joiOptions, translations) ]
};

Note: Data values in the $set operator are not validated. You could use joi-errors-for-forms for that.

Validate Anything in the Hook Context

As of version 3.1.0, you can validate anything in the hook context using the getContext and setContext options.

const objectId = require('./some-custom-validator')
const schema = Joi.object({
  userId: objectId(),
});

const joiOptions = {
  getContext(context) {
    return context.params.query;
  },
  setContext(context, newValues) {
    Object.assign(context.params.query, newValues);
  },
};

export.before = {
  find: [ validate.mongoose(schema, joiOptions, translations) ]
};

validateProvidedData Hook

The validateProvidedData hook is just like validate.form, but it only validates the attributes from the schema which are actually present in the request's data object. In short, it allows partial validation of the schema attributes. Using it as a hook looks like this:

const validate = require('@featehrs-plus/validate-joi')
const attrs = require('./faqs.model')

const hooks = {
  before: {
    patch: [
      validate.validateProvidedData(attrs, { abortEarly: false })
    ]
  }
}

The above example supposes that you have an /faqs service with a model that looks like the following. Notice how the attrs are defined as a separate object, then they are used in the schema and made available in the export. The validateProvidedData hook uses the individual attrs to validate each individual item in the request's data object.

// src/services/faqs/faqs.model.js
const Joi = require('joi')
const { objectId } = require('@feathers-plus/validate-joi-mongodb')

const attrs = {
  _id: objectId(),
  question: Joi.string().disallow(null).required(),
  answer: Joi.string().disallow(null).required(),
  isPublic: Joi.boolean().default(false),
  createdBy: objectId().disallow(null).required()
}

module.exports = {
  attrs,
  schema: Joi.object(attrs)
}

Motivation

Data must be validated and sanitized before the database is changed. The client must be informed of any errors using a schema friendly to web/mobile apps.

This repo helps implement this in Feathers CRUD hooks.

API Reference

The joiOptions object is passed directly to the schema, internally. You can see all of the available options and defaults in the joi documentation. Here is a summary of the defaults:

const joiDefaults = {
  abortEarly: true,
  allowUnknown: false,
  cache: true,
  convert: true,
  debug: false,
  externals: true,
  noDefaults: false,
  nonEnumerables: false,
  presence: 'optional',
  skipFunctions: false,
  stripUnknown: false,
  getContext: undefined,
  setContext: undefined,
};

Tests

npm test to run tests.

npm run cover to run tests plus coverage.

A Note on Internationalization

The options in Joi.validate(value, schema, options, cb)supports a language option with which you can change Joi error messages in bulk.

You can then internationalize your field names and regex descriptions in the schema, e.g.

Joi.string().regex(/^[\sa-zA-Z0-9]$/, i18n('letters, number and spaces')).label(i18n('Confirm password'))

These are suitable methods to internationalize the majority of Joi error messages.

Contributors

License

MIT. See LICENSE.

validate-joi's People

Contributors

adamvr avatar betinho89 avatar daffl avatar dependabot[bot] avatar eddyystop avatar luongvm avatar marshallswain avatar mkovel avatar schettino avatar toddbluhm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

validate-joi's Issues

validateProvidedData with mongoose format

As specified on the doc, the validate.validateProvidedData method provides the same result as validate.form.

However, I am using validate.mongoose because I prefer this format.

Because of this, I can not use the partial validation feature you propose.

Is that a reason to not support mongoose format for validateProvidedData?

Thanks

Custom errors for each key

Are custom error messages for each key supported?

I tried the following as per the documentation for joi, but it seems to continue to throw a generic "Bad Request: Invalid Data" message:

const Firstname = Joi.string().trim().min(3).max(50).alphanum().required().error(() => "Firstname is invalid");

I also tried this:

const Firstname = Joi.string().trim().min(3).max(50).alphanum().required().error(new Error( "Firstname is invalid"));

Both of the above were validated against "s", with the same "Invalid Data" error message as a result.

Validator not working with Feathers v4

Relevant files:

services/index.js:

const schemas = require('../schemas')

module.exports = app => {

  // Configure the service and hooks.
  app.get('service')('users').hooks({

    before: {
      create: [schemas.validateUser(), hashPassword('password')],
    }
  })
}

schemas/index.js:

const Joi = require('joi')
const validate = require('feathers-hooks-validate-joi')

// Common options for all validations.
const options = {
  abortEarly: false,
  convert: true,
  allowUnknown: false,
  stripUnknown: true
}

const username = Joi.string().trim().alphanum().min(4).max(30).required()

const password = Joi.string().trim()
  .regex(/^[\sa-zA-Z0-9]+$/, 'letters, numbers, spaces')
  .min(4).max(30).required()

const email = Joi.string().trim().email().required()

const user = Joi.object().keys({username, email, password})

module.exports = {
  validateUser: () => validate.form(user, options)
}

Submitting an invalid JSON to create the system return the correct error messages.

But submitting a valid JSON to create, the system generates an error:

{
  "name": "GeneralError",
  "message": "next is not a function",
  "code": 500,
  "className": "general-error",
  "data": {},
  "errors": {}
}

feathers-hooks-validate-joi Validator

I have a feathersjs API with a messages service. I want to validate the message model with feathers-hooks-validate-joi module.

Here is my user-hooks.js file:

const validate = require('feathers-hooks-validate-joi');
const {schema} = require('./user.validator');

module.exports = {
before: {
create: [validate.form(schema)],
//others method fields
}
};
Here is my user.validator.js file:

const Joi = require('joi');

const schema = Joi.object().keys({
email: Joi.string().trim().min(2).required(),
password: Joi.string().trim().min(2).required()
});

module.exports = schema;
When I try to post a message via curl:

curl 'http://localhost:3030/users/' -H 'Content-Type: application/json' --data-binary '{ "email": "[email protected]", "password": "World" }'

I receive this error message:

{
"name": "GeneralError",
"message": "Invalid schema content: ",
"code": 500,
"className": "general-error",
"data": {},
"errors": {}
}
Please can you help me to fix this problem

TypeError: next is not a function

error: TypeError: next is not a function
at /home/myproject/backend-api/node_modules/feathers-hooks-validate-joi/index.js:26:9
at internals.Object._validateWithOptions (/home/myproject/backend-api/node_modules/joi/lib/types/any/index.js:654:20)
at module.exports.internals.Any.root.validate (/home/myproject/backend-api/node_modules/joi/lib/index.js:121:23)
at Object.validatorInner (/home/myproject/backend-api/node_modules/feathers-hooks-validate-joi/index.js:14:9)
at current.then (/home/myproject/backend-api/node_modules/@feathersjs/commons/lib/hooks.js:115:46)
at process._tickCallback (internal/process/next_tick.js:68:7)

[email protected]
[email protected]

Improved joiOptions object handling

Two suggestions:

  1. When a null or undefined value is passed as joiObject, just default it to empty object. That's what joi does. Currently the code throws exception if an object type is not passed.

  2. Invert the check for convert option.
    According to joi API docs, convert option defaults to true:

    convert - when true, attempts to cast values to the required types (e.g. a string to a number). Defaults to true.
    

    The exception would be that passing an empty object as joiOptions means that convert is enabled by default, thus the hook should replace the data with the converted value by default.

`validateSchema.form()` validates only `data.$set` in `patch()` hooks

If I do .patch(id, { field: value }), the validation don't work because it uses utils.get from the deprecated package feathers-hooks-utils wich looks for data in data.$set.

If I do .patch(id, { $set: { field: value } }) it feels like wrong REST and e.g. auth.hashPassword() dont work because it only hashes passwords in data.password.

I now use .patch(id, { $set: fields, …fields }) which is awkward but works in my case.

Validating patch method data

Hi guys, I'm interested to know if you've found a good way of validating patch method data with joi. The problem is, of course, that you can't validate the whole schema on a patch request since you don't have the entire object to work with, just the patch data. The way I see it, there's a couple of ways of doing it:

  1. Running joi in something like 'patch mode,' where you would skip required() validations and run joi.validate() with noDefaults: true. This would allow you to validate patch data in isolation but as far as I can tell there's no way of skipping required validations in vanilla joi (you can hack your way around it a bit with requiredKeys() but then you would need to sneak in required fields through meta() or have a separate list of required keys rather than using required()).

  2. Looking up the previous version of the object and running the patch and object through something like https://github.com/MarkHerhold/json-patch-joi. The problem with this approach is that you will need to load the whole previous version each time you want to run a patch request.

Any ideas there?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.