@connectrpc/validate
Version:
Flexible, efficient request validation for Connect
81 lines (80 loc) • 3.39 kB
JavaScript
// Copyright 2025 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createValidateInterceptor = createValidateInterceptor;
const connect_1 = require("@connectrpc/connect");
const protovalidate_1 = require("@bufbuild/protovalidate");
/**
* Creates an Interceptor that ensures that RPC request messages match the constraints
* expressed in their Protobuf schemas. It does not validate response messages.
*
* By default, the Interceptor uses a validator that is created using `createValidator`
* without any options. To use a different validator use the `validator` option.
*
* RPCs with invalid request messages short-circuit with an error.
* The error always uses Code.InvalidArgument and has a detailed representation
* of the error attached as a error detail.
*
* This interceptor is primarily intended for use on handlers.
* Client-side use is possible, but discouraged unless the client
* always has an up-to-date schema.
*/
function createValidateInterceptor(opt) {
const validator = opt?.validator ?? (0, protovalidate_1.createValidator)();
return (next) => {
return (req) => {
if (req.stream === false) {
validate(validator, req.method.input, req.message);
return next(req);
}
return next({
...req,
message: {
[Symbol.asyncIterator]: () => {
const it = req.message[Symbol.asyncIterator]();
const validateIt = {
async next(...[value]) {
const next = await it.next(value);
if (next.value) {
validate(validator, req.method.input, next.value);
}
return next;
},
};
if (it.return) {
validateIt.return = (value) => it.return(value);
}
if (it.throw) {
validateIt.throw = (value) => it.throw(value);
}
return validateIt;
},
},
});
};
};
}
function validate(validator, desc, msg) {
const result = validator.validate(desc, msg);
if (result.kind === "valid") {
return;
}
const details = [];
if (result.violations) {
const [value, desc] = (0, protovalidate_1.violationsToProto)(result.violations);
details.push({ desc, value });
}
throw new connect_1.ConnectError(result.error.message, connect_1.Code.InvalidArgument, undefined, details, result.error);
}
;