Ensuring the integrity of a static array using Joi

Struggling with finding a solution for this unique kind of "array validation" I'm attempting.

My goal is to validate dynamic sets of rest parameters from function arguments against a REST api schema. Here's an example:

@Validation(Schema.update)
public async update(id: string, model: Model) => { }

The idea is to use a decorator to intercept the function call and perform validation based on the provided schema:


export function Validation(schema: ObjectSchema) {
  return (target: any, propName: string | symbol, descriptor: PropertyDescriptor): void => {
    const originalMethod = descriptor.value!;

    descriptor.value = function (...rest: any[]): ApiResult {
      const { error } = schema.validate({ ...rest });

      if (error) {
        console.error(error.message);
        return new ErrorApiResult({ statusCode: 400, message: error?.message });
      }

      return originalMethod.apply(this, arguments);
    };
  };
}

Although it currently functions well, there's one issue - defining the validation schema like below:

export const Schema = {
  update: Joi.object({
    0: Joi.string(),
    1: ModelSchema,
  }),
}

The main concern is that Joi generates validation error messages that label fields as 0.user.name instead of model.user.name, causing confusion.

I would prefer to define the endpoint schema like this:

export const Schema = {
  update: Joi.object({
    id: Joi.string().required(),
    model: ModelSchema,
  }),
}

However, I haven't figured out how to achieve this yet. Even exploring Joi.array(), it seems more suited for handling collections of objects rather than strict argument arrays.

EDIT: I attempted using the .label() method to adjust the error message labels, but faced challenges when dealing with nested keys. While it works for simple validations like the id argument, it fails to display correctly when validating properties within ModelSchema, showing "1.user.name" in error messages.

Answer №1

Although I couldn't figure out how to define Joi validation for a specific array, I found a workaround by leveraging my Joi schema metadata to construct a custom object that adheres to the schema requirements:

export function Validation(schema: ObjectSchema) {
  return (target: any, propName: string | symbol, descriptor: PropertyDescriptor): void => {
    const originalMethod = descriptor.value!;

    descriptor.value = function (...rest: any[]): ApiResult {
      const validationObject: any = {};
      const keys: string[] = Array.from((schema as any)._ids._byKey.keys());
      for (let i = 0; i < keys.length; i++) {
        validationObject[keys[i]] = rest[i];
      }
      const { error } = schema.validate(validationObject);

      if (error) {
        console.error(error.message);
        return new ErrorApiResult({ statusCode: 400, message: error?.message });
      }

      return originalMethod.apply(this, arguments);
    };
  };
}

To make this work, I had to compromise Typescript's type safety in order to access these fields dynamically at runtime. While this solution may not be ideal, it functions properly as long as the keys in the validation object align with the arguments in the function being validated.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Mastering Light and Camera Selection in Three.js

Question, In the editor found at this link, you can click on a light or camera to select it. I am familiar with using raycaster.intersectObjects(objects) to select meshes, but how can I achieve the same result for lights and cameras which do not have mesh ...

What is the most effective method to retrieve the current browser URL in Angular 2 with TypeScript?

Is there a way for me to retrieve the current URL from the browser within my Angular 2 application? Usually in JavaScript, we would use the window object for this task. Can anyone guide me on how to achieve this in Angular 2 using TypeScript? Appreciate a ...

Fetching data from a local device file system using Angular 2 and Ionic 2

Currently, I am working on developing an application that has a unique requirement - it must be able to function offline and have a synchronization mechanism with servers. This means that users should have the ability to sync the app with server data whene ...

Create a nested object within an object in Angular 2

I am currently working with a model named "Professional" which includes a property that is another model called "Address": 'use strict'; import * as Address from './models/address'; export interface Professional { id?: number; ...

The JavaScript function is being triggered multiple times by the event listener, even though I explicitly reference the function

Every time I create an angular controller, I add an event listener. However, when I return to the page after leaving it, a new event listener is added because the constructor is called again. Unfortunately, when this event is triggered, it gets invoked mu ...

Updating a div dynamically in Angular 2 using data fetched from an http.get request

I have a menu column on the left side and a content section on the right side. Whenever I click on a menu item on the left side, an http.get request is triggered and I receive response data. However, I am unsure how to update the content section with this ...

Discovering the worth of a variable outside of a subscription or Promise within Ionic 3

Apologies for my English. I am encountering an issue when attempting to view the results of a REST API using both subscribe and Promise methods. Within my provider, I have the following code: Provider: import { HttpClient } from '@angular/common/h ...

Tips for verifying the presence of a specific value within an array of union types

Given an array of a specific union type, I am trying to determine if a string from a larger set that includes the union type is present in the array during runtime: const validOptions: ("foo" | "bar")[] = ["foo", "bar"] type IArrType = typeof validOptions ...

Verify in TypeScript whether a property of an object is a function with a specified signature

I am facing an issue with a function that retrieves a property from an object. // Utils.ts export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] { if (key in obj) { return obj[key as K]; } throw new Error(`Invalid obje ...

What is the method for implementing type notation with `React.useState`?

Currently working with React 16.8.3 and hooks, I am trying to implement React.useState type Mode = 'confirm' | 'deny' type Option = Number | null const [mode, setMode] = React.useState('confirm') const [option, setOption] ...

Caution: the use of findDOMNode is no longer supported in StrictMode when utilizing react-bootstrap Navbar

While attempting to utilize the Navbar component from react-bootstrap in a typescript template, I encountered the following warning in the Chrome console. index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of T ...

Is there a more efficient method to tally specific elements in a sparse array?

Review the TypeScript code snippet below: const myArray: Array<string> = new Array(); myArray[5] = 'hello'; myArray[7] = 'world'; const len = myArray.length; let totalLen = 0; myArray.forEach( arr => totalLen++); console.log(& ...

The Art of Validating Configurations Using io-ts

I have recently switched to using io-ts instead of runtypes in a fresh project. In terms of configuration validation, my approach involves creating an object that specifies the types for each part of the config; const configTypeMap = { jwtSecret: t.str ...

If the condition is met, go ahead and run the switchMap function; if not, make sure to cancel everything

Working on a web application using Angular 10 has led me to prefer Observables over Promises. However, creating a seamless flow of executions can be challenging at times. My objective is to follow this flow: Show a confirmation modal If the confirmation ...

What is the significance of utilizing generic types as values within a generic class?

Why is the compiler giving an error for the following code T' only refers to a type, but is being used as a value here: class Factory<T> { create(TCreator: (new () => T)): T { return new TCreator(); } test(json: string) ...

Encountering an Issue with Vue 3 and Vue Router 4: Uncaught TypeError - Trying to Access Undefined Properties (specifically 'push')

I'm currently working with Vue 3, Vue Router 4, and TypeScript in my project. However, I've encountered an issue while trying to utilize router.push(). Every time I attempt this, I receive a console error stating: Uncaught (in promise) TypeError: ...

Converting Next.js with MongoDB to TypeScript

I recently set up a next.js application using the mongodb template: npx create-next-app --e with-mongodb my-app Additionally, I included TypeScript in my project. Now, I am faced with the task of converting /lib/mongodb.js to TypeScript. Currently, the f ...

Looking for multiple edges using the select() function alongside the count() method in Gremlin

Is there a way to retrieve the count of selected values in a single query using gremlin? g .V("00000000000000000000000000000000").outE().as("fpEdges") .V("facade00-0000-4000-a000-000000000000").inE().as("vstEdges" ...

Issue with narrowing TypeScript arrays often encountered

When working with arrays of strings in my TypeScript code, I restrict the contents to certain letters by using a defined type like ("A" | "B")[] for letters such as A and B. However, when I have a function that takes an arbitrary array ...

Module search failed for Node 22 along with TypeScript 5.4

I have structured my project like this: Node 22 TS 5.4 In the package.json file, I have specified type: module. The tsconfig.json file is set as follows: { "target": "es2022", "esModuleInterop": true, "modul ...