Skip to content

Repository files navigation

The Buf logo

protovalidate-es

License NPM Version

Protovalidate is the semantic validation library for Protobuf. It provides standard annotations to validate common rules on messages and fields, as well as the ability to use CEL to write custom rules. It's the next generation of protoc-gen-validate.

With Protovalidate, you can annotate your Protobuf messages with both standard and custom validation rules:

syntax = "proto3";

package acme.user.v1;

import "buf/validate/validate.proto";

message User {
  string id = 1 [(buf.validate.field).string.uuid = true];
  uint32 age = 2 [(buf.validate.field).uint32.lte = 150]; // We can only hope.
  string email = 3 [(buf.validate.field).string.email = true];
  string first_name = 4 [(buf.validate.field).string.max_len = 64];
  string last_name = 5 [(buf.validate.field).string.max_len = 64];

  option (buf.validate.message).cel = {
    id: "first_name_requires_last_name"
    message: "last_name must be present if first_name is present"
    expression: "!has(this.first_name) || has(this.last_name)"
  };
}

Once you've added @bufbuild/protovalidate to your project, validation is simple:

import { create } from "@bufbuild/protobuf";
import { createValidator } from "@bufbuild/protovalidate";
import { MoneyTransferSchema } from "./gen/banking_pb";

const transfer = create(MoneyTransferSchema);

const validator = createValidator();
const result = validator.validate(MoneyTransferSchema, transfer);
if (result.kind !== "valid") {
  // Handle failure.
}

Tip

The string.pattern rule supports regular expressions with CEL's standard RE2 syntax.

Protovalidate evaluates patterns with @bufbuild/re2, an RE2-compatible engine that executes in linear time, guarding against ReDoS. It is the default because it is scoped to what CEL and Protovalidate need, which keeps it small.

To evaluate patterns with a different engine, pass a regexMatch function. For example, with re2js, a complete port of RE2/J that offers a broader API at the cost of a larger bundle:

import { RE2JS } from "re2js";
import { createValidator } from "@bufbuild/protovalidate";

// Patterns come from schema rules, so the same handful are matched over and
// over. Caching the compiled form keeps repeat matches cheap.
const compiled = new Map<string, RE2JS>();

const validator = createValidator({
  regexMatch: (pattern: string, against: string): boolean => {
    let re = compiled.get(pattern);
    if (re === undefined) {
      re = RE2JS.compile(pattern);
      compiled.set(pattern, re);
    }
    // Use `find`, which searches anywhere in the input, to match the
    // unanchored semantics of CEL's `matches()`. `matches` requires the
    // entire input to match and would reject values that Protovalidate
    // considers valid.
    return re.matcher(against).find();
  },
});

Packages

Note that protovalidate-es requires the Protobuf runtime @bufbuild/protobuf.

Additional languages and repositories

Protovalidate isn't just for ECMAScript! You might be interested in sibling repositories for other languages:

Additionally, protovalidate's core repository provides:

Contributing

We genuinely appreciate any help! If you'd like to contribute, check out these resources:

Legal

Offered under the Apache 2 license.

Releases

Used by

Contributors

Languages