razor7
razor7

Reputation: 2355

Meteor server validated method doesn't display validation error on client

I have this server side validated method imports/api/Shops/server/methods.js:

export const shopGeocodeAddress = new ValidatedMethod({
  name: 'shops.geocodeAddress',
  validate: new SimpleSchema({
    streetName: { type: String },
    houseNumber: { type: Number },
    city: { type: String },
    state: { type: String },
    country: { type: String },
    zip: { type: String, optional: true },
  }).validator(),
  run({ streetName, houseNumber, city, state, country, zip }) {
...
...
...
...
    return location;
  },
});

To test validation error message on the client, I'm sending houseNumber as string which will get the method to fail, as it expects houseNumber as Number. So far so good. I'm getting a validation error on the server console like this:

I20190418-10:55:28.605(-3)? Exception while invoking method 'shops.geocodeAddress' { ClientError: House number must be of type Number
...
...
I20190418-10:55:28.608(-3)?   errorType: 'ClientError',
I20190418-10:55:28.608(-3)?   name: 'ClientError',
I20190418-10:55:28.608(-3)?   error: 'validation-error',
I20190418-10:55:28.609(-3)?   details: 
I20190418-10:55:28.609(-3)?    [ { name: 'houseNumber',
I20190418-10:55:28.609(-3)?        value: NaN,
I20190418-10:55:28.609(-3)?        type: 'expectedType',
I20190418-10:55:28.609(-3)?        dataType: 'Number',
I20190418-10:55:28.609(-3)?        message: 'House number debe ser del tipo Number' } ] }

But on the client I get only an internal server error like this:

details: undefined
error: 500
errorType: "Meteor.Error"
isClientSafe: true
message: "Internal server error [500]"
reason: "Internal server error"

Thus I can't indicate the client which type of error is, or which field must be changed in order for the method to be run correctly.

Is there a way to catch validation error on server methods and send them to the client?

Thanks in advise!

Upvotes: 0

Views: 338

Answers (3)

razor7
razor7

Reputation: 2355

Finally I used this answer on Meteor Forums https://forums.meteor.com/t/server-validated-method-doesnt-display-validation-error-on-client/48635/2?u=razor7

Basically I needed to create a new file to be run in server startup to route validation errors to client

import SimpleSchema from 'simpl-schema';

SimpleSchema.defineValidationErrorTransform((error) => {
  const ddpError = new Meteor.Error(error.message);
  ddpError.error = 'validation-error';
  ddpError.details = error.details;
  ddpError.reason = error.message;

  return ddpError;
});

Upvotes: 1

Paul Paulincai
Paul Paulincai

Reputation: 644

2 things: A) You can write

city: String,
state: String,
zip: { type: String, optional: true }
...

B) You say "server side validated method". Your method needs to be in the common space reachable by both Server and Client. In the method you enclose everything into Server. You call the method from the client (and reach it) but run it on the Server:

export const shopGeocodeAddress = new ValidatedMethod({
  name: 'shops.geocodeAddress',
  validate: new SimpleSchema({
    streetName: String,
    houseNumber: Number,
    city: String,
    state: String,
    country: String,
    zip: { type: String, optional: true },
  }).validator(),
  run({ streetName, houseNumber, city, state, country, zip }) {
     if (Meteor.isServer) {
      ...
      ...
      ...
      ...
    return location;
     }

  },
});

Upvotes: 1

Jankapunkt
Jankapunkt

Reputation: 8423

You can surround the s schema.validator with a try/catch and throw a custom Meteor.Error that contains better understandable information.

You can do the same with the code inside run.

If you want to do this for every validated method, you may create a simple generator function:

const createMethod = options => new ValidatedMethod({
  name: options.name,
  validate(...args) {
    try {
      new SimpleSchema(options.schema).validate(...args)
    } catch (e) {
      throw new Meteor.Error('validationError', e.reason)
    }
  },
  run(...args) {
    try {
      return options.run.call(this, ...args)
    } catch (e) {
      throw new Meteor.Error('methodError', e.reason)
    }    
  }
})

Note that you may use e.reason || e.message

Upvotes: 2

Related Questions