Logo for Johan Baaij Software Development

Johan Baaij

is developing software for the web with Ruby on Rails, Vue.js and other open-source technologies.

VeeValidate server side validations

Sat Feb 01 2020

Originally published on dev.to.


In this post we'll look at how to add a VeeValidate rule for validating a value with an API endpoint. A scenario where such a rule is useful could be checking if a username is unique.

Rather than displaying an error after the form is submitted, we'd like to inform the user right away that their username is already taken.

We'll start with this simple component using a ValidationObserver and ValidationProvider.

<template>
  <ValidationObserver v-slot="{ invalid }">
    <ValidationProvider name="username" rules="required" v-slot="{ errors }">
      <p>
        <input placeholder="username" v-model="username" type="text">
        <br>
        <span id="error">{{ errors[0] }}</span>
      </p>
    </ValidationProvider>
    <button @click="sendForm" :disabled="invalid" type="button">Submit</button>
  </ValidationObserver>
</template>

<script>
import { ValidationProvider, ValidationObserver, extend } from "vee-validate";
import { required } from "vee-validate/dist/rules";

extend("required", {
  ...required
});

export default {
  data: function() {
    return {
      username: null
    };
  },
  components: {
    ValidationProvider,
    ValidationObserver
  },
  methods: {
    sendForm() {
      alert(`Thanks ${this.username}`)
    }
  }
};
</script>

Let's add the method that calls our API. For this example I'll use the Github API to look up usernames. The endpoint URI is https://api.github.com/users/:username.

Github returns a 404 when the username is not found, in our case that means the field is valid. Whatever your situation is, this method should return true if valid and false if not.

export default {
  // ...
  methods: {
    async isUsernameUnique() {
      try {
        const response = await axios.get(
          `https://api.github.com/users/${this.username}`
        );
        return false;
      } catch (err) {
        if (err.response.status === 404) {
          return true;
        }
      }
    }
  }
  // ...
}

Now that we have the method in place we can tell VeeValidate to use it in our new rule.

export default {
  // ...
  mounted() {
    extend("unique", {
      validate: this.isUsernameUnique,
      message: "Username already taken"
    });
  }
  // ...
}

Lastly, we add the rule to the ValidationProvider.

<ValidationProvider name="username" rules="required|unique" :debounce="500" v-slot="{ errors }">
  <p>
    <input placeholder="username" v-model="username" type="text">
    <br>
    <span id="error">{{ errors[0] }}</span>
  </p>
</ValidationProvider>

Note that I've added a :debounce attribute. This ensures we won't be overflowing the API with requests at every keypress but rather every 500ms.

You can find the entire source code for this example on CodeSandbox.

Edit vee-validate-api-validation-example

Using handleSubmit

To prevent the user from sending the form before we have heard back from our API we can use handleSubmit. It takes our own sendForm method as an argument and using it is very straightforward.

<ValidationObserver v-slot="{ invalid, handleSubmit }">
  <!-- the rest of our form -->
  <button @click="handleSubmit(sendForm)" :disabled="invalid" type="button">Submit</button>
</ValidationObserver>

Hopefully this simple example will be useful for writing your own server side validation rules.