DEV Community

whiteand
whiteand

Posted on

Quartet 9: Allegro | Teaser

I am going to release new version of Quartet 9: Allegro data validation library.

I wanted to create library that will have four principles:

  • simplicity
  • minimalism
  • similarity of schema and TypeScript type
  • performance

Evaluation of the results

For this type

interface Message {
  type: "subscribeAcknowledgement"
  channel: string // with "channel" inside
  data: {
    id: string
    symbol: string
    price: number
    count: number
    accustic: number
    size: number
    mid: number
    last: number
    lastSize: number
    network: number
    open: number
    high: number
    low: number
    volume: number
    interest: number
    change: number
    percents: number
    someNumber: number
    grade: number
    mark: number
    velocity: number
    length: number
    height: number
    width: number
  }
}
Enter fullscreen mode Exit fullscreen mode

We have such validation function:

// validator: (value: any) => value is Message
const validator = v<Message>({
  type: "subscribeAcknowledgement",
  channel: v.and(v.string, v.test(/channel/)),
  data: {
    id: v.string,
    symbol: v.string,
    price: v.number,
    count: v.number,
    accustic: v.number,
    size: v.number,
    mid: v.number,
    last: v.number,
    lastSize: v.number,
    network: v.number,
    open: v.number,
    high: v.number,
    low: v.number,
    volume: v.number,
    interest: v.number,
    change: v.number,
    percents: v.number,
    someNumber: v.number,
    grade: v.number,
    mark: v.number,
    velocity: v.number,
    length: v.number,
    height: v.number,
    width: v.number
  }
})
Enter fullscreen mode Exit fullscreen mode

Lets compare it with one of the fastest library ajv and with one of the most popular library joi.

Ajv Validator:

const ajvValidator = ajv.compile({
  type: "object",
  required: ["type", "channel", "data"],
  properties: {
    type: {
      type: "string",
      enum: ["subscribeAcknowledgement"]
    },
    channel: {
      type: "string",
      pattern: "channel"
    },
    data: {
      type: "object",
      required: [
        "id",
        "symbol",
        "price",
        "count",
        "accustic",
        "size",
        "mid",
        "last",
        "lastSize",
        "network",
        "open",
        "high",
        "low",
        "volume",
        "interest",
        "change",
        "percents",
        "someNumber",
        "grade",
        "mark",
        "velocity",
        "length",
        "height",
        "width"
      ],
      properties: {
        id: { type: "string" },
        symbol: { type: "string" },
        price: { type: "number" },
        count: { type: "number" },
        accustic: { type: "number" },
        size: { type: "number" },
        mid: { type: "number" },
        last: { type: "number" },
        lastSize: { type: "number" },
        network: { type: "number" },
        open: { type: "number" },
        high: { type: "number" },
        low: { type: "number" },
        volume: { type: "number" },
        interest: { type: "number" },
        change: { type: "number" },
        percents: { type: "number" },
        someNumber: { type: "number" },
        grade: { type: "number" },
        mark: { type: "number" },
        velocity: { type: "number" },
        length: { type: "number" },
        height: { type: "number" },
        width: { type: "number" }
      }
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

Joi Validator:

const joiSchema = Joi.object({
  type: Joi.equal("subscribeAcknowledgement").required(),
  channel: Joi.string()
    .pattern(/channel/)
    .required(),
  data: Joi.object({
    id: Joi.string().required(),
    symbol: Joi.string().required(),
    price: Joi.number().required(),
    count: Joi.number().required(),
    accustic: Joi.number().required(),
    size: Joi.number().required(),
    mid: Joi.number().required(),
    last: Joi.number().required(),
    lastSize: Joi.number().required(),
    network: Joi.number().required(),
    open: Joi.number().required(),
    high: Joi.number().required(),
    low: Joi.number().required(),
    volume: Joi.number().required(),
    interest: Joi.number().required(),
    change: Joi.number().required(),
    percents: Joi.number().required(),
    someNumber: Joi.number().required(),
    grade: Joi.number().required(),
    mark: Joi.number().required(),
    velocity: Joi.number().required(),
    length: Joi.number().required(),
    height: Joi.number().required(),
    width: Joi.number().required()
  })
});
Enter fullscreen mode Exit fullscreen mode

And now, benchmark for this case:

joi                x   1,104 ops/sec ±15.76% (80 runs sampled)
Quartet 8          x   1,438 ops/sec ±9.27% (55 runs sampled)
ajv                x  78,991 ops/sec ±2.81% (83 runs sampled)
Quartet 9: Allegro x 149,552 ops/sec ±5.54% (78 runs sampled)
Enter fullscreen mode Exit fullscreen mode

Benchmark results

Why is it so fast? Let's look at it

validator.toString()
Enter fullscreen mode Exit fullscreen mode

What we see is an actual result:

function validator(value) {
  if (value == null) return false
  if (value.type !== "subscribeAcknowledgement") return false
  if (!validator.custom(value.channel)) return false
  if (value.data == null) return false
  if (typeof value.data.id !== 'string') return false
  if (typeof value.data.symbol !== 'string') return false
  if (typeof value.data.price !== 'number') return false
  if (typeof value.data.count !== 'number') return false
  if (typeof value.data.accustic !== 'number') return false
  if (typeof value.data.size !== 'number') return false
  if (typeof value.data.mid !== 'number') return false
  if (typeof value.data.last !== 'number') return false
  if (typeof value.data.lastSize !== 'number') return false
  if (typeof value.data.open !== 'number') return false
  if (typeof value.data.high !== 'number') return false
  if (typeof value.data.low !== 'number') return false
  if (typeof value.data.volume !== 'number') return false
  if (typeof value.data.interest !== 'number') return false
  if (typeof value.data.change !== 'number') return false
  if (typeof value.data.percents !== 'number') return false
  if (typeof value.data.someNumber !== 'number') return false
  if (typeof value.data.grade !== 'number') return false
  if (typeof value.data.mark !== 'number') return false
  if (typeof value.data.velocity !== 'number') return false
  if (typeof value.data.length !== 'number') return false
  if (typeof value.data.height !== 'number') return false
  if (typeof value.data.width !== 'number') return false

  return true
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)