Skip to content

✅ Strapi API Validation Queries

Estas son las funciones utilizadas para aplicar la validación de datos en las APIs.

Tanto los datos que no pueden incluirse en las llamadas como los que son obligatorios estas funciones se encargan de manejar globalmente esto.

🧩 Explicación de las funciones de validación

🔒 validateKeys

Esta función genérica validateKeys<T>() verifica que un objeto no contenga propiedades no permitidas. Se aplica tanto al body general de la petición como a componentes anidados como billingAddress o mailingAddress.

export function validateKeys<T extends Record<string, unknown>>(
obj: T,
validKeys: string[],
context: string
): void {
if (!obj || typeof obj !== FIELD_OBJECT) return;
const invalidKeys = Object.keys(obj).filter(
(key) => !validKeys.includes(key)
);
if (invalidKeys.length > 0) {
throw new Error(
NOT_ALLOWED_FIELDS + context + ": " + invalidKeys.join(", ")
);
}
}

🔧 Parámetros:

  • obj: el objeto que se desea validar (por ejemplo ctx.request.body, billingAddress, etc.).
  • validKeys: arreglo de strings con las claves permitidas para ese objeto.
  • context: nombre del contexto (para mejorar el mensaje de error).

🧪 Funcionamiento:

  • Verifica que el objeto sea válido.
  • Obtiene todas las claves con Object.keys() y filtra las que no están en validKeys.
  • Si se detectan claves no válidas, lanza una excepción con el nombre del contexto y las claves incorrectas.

🛡️ Esta validación previene que el cliente envíe campos adicionales no controlados que puedan representar errores o vectores de ataque.

🐿️ EJEMPLO DE USO:

function validateRequestBody(body: UserProfileUpdateData) {
validateKeys(body, WordpressUserKeys, User.FIELD_ROOT);
if (body.phone !== undefined) {
if (typeof body.phone !== User.FIELD_OBJECT)
throw new Error(ERROR_INVALID_FORMAT + User.COMPONENT_NAME_PHONE);
validateKeys(body.phone, User.VALID_PHONE_KEYS, User.COMPONENT_NAME_PHONE);
}
const addressFields = [
{
value: body.billingAddress,
validKeys: User.VALID_BILLING_ADDRESS_KEYS,
context: User.COMPONENT_NAME_BILLING,
},
{
value: body.mailingAddress,
validKeys: User.VALID_MAILING_ADDRESS_KEYS,
context: User.COMPONENT_NAME_MAILING,
},
];
for (const { value, validKeys, context } of addressFields) {
if (value !== undefined) {
if (typeof value !== User.FIELD_OBJECT)
throw new Error(ERROR_INVALID_FORMAT + context);
validateKeys(value, validKeys, context);
if (value.phone !== undefined) {
if (typeof value.phone !== User.FIELD_OBJECT)
throw new Error(ERROR_INVALID_FORMAT + `${context}.phone`);
validateKeys(value.phone, User.VALID_PHONE_KEYS, `${context}.phone`);
}
}
}
}

✅ validateRequiredFields

La función validateRequiredFields<T>() asegura que todos los campos obligatorios estén presentes en el objeto y tengan un valor definido. Es una validación esencial en cualquier punto en donde se espera una estructura mínima para continuar el flujo de ejecución.

export function validateRequiredFields<T extends Record<string, unknown>>(
obj: T,
requiredKeys: string[]
): void {
const missingFields = requiredKeys.filter(
(key) => !(key in obj) || obj[key] === undefined || obj[key] === null
);
if (missingFields.length > 0) {
throw new Error(
MISSING_REQUIRED_PARAMETERS + ": " + missingFields.join(", ")
);
}
}

🔧 Parámetros:

  • obj: el objeto que será validado (usualmente el ctx.request.body).
  • requiredKeys: lista de claves que deben estar definidas dentro del objeto.

🧪 Funcionamiento:

  • Recorre la lista requiredKeys y verifica que:

    • La clave exista dentro del objeto.
    • Su valor no sea undefined ni null.
  • Si encuentra alguna clave faltante o sin valor, lanza un error informando qué campos están ausentes.

🚨 Esta función permite identificar errores de entrada en etapas tempranas, y garantiza que no falten datos esenciales para el funcionamiento del backend.

🐿️ EJEMPLO DE USO

validateRequiredFields(body, requiredUserKeys);

🧪 validateFieldPatterns

Esta función permite validar que los campos cumplan con un patrón específico, por ejemplo para contraseñas, correos o formatos personalizados.

type FieldPattern = {
pattern: RegExp;
message: string;
};
export function validateFieldPatterns<T extends Record<string, unknown>>(
obj: T,
patterns: Record<keyof T, FieldPattern>
): void {
const errors: string[] = [];
for (const key in patterns) {
const value = obj[key];
const { pattern, message } = patterns[key];
if (typeof value === "string" && !pattern.test(value)) {
errors.push(`${key} ${message}`);
}
}
if (errors.length) {
throw new Error(`${ERROR_INVALID_REGEX}` + errors.join("\n"));
}
}

🔧 Parámetros:

  • obj: el objeto que contiene los campos a validar.
  • patterns: un objeto con claves que coinciden con los nombres de los campos y patrones RegExp que deben cumplir.

🧪 Funcionamiento:

  • Itera sobre los campos definidos en patterns.
  • Verifica si el valor correspondiente es un string y cumple con el patrón.
  • Si no lo cumple, agrega el error al listado y al final lanza una excepción si hay alguno.

🐿️ EJEMPLO DE USO en src/constants/regex.ts -> Aquí deben agregarse los regex para usarse

export const FIELD_VALIDATIONS = {
password: {
pattern:
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~`]).{8,}$/,
message:
"must be at least 8 characters long and include an uppercase letter, a lowercase letter, a number, and a special character.",
},
};

🌐 validateLang

Valida que el idioma proporcionado esté entre los soportados: 'es', 'en' y 'pt'. Si no lo está, retorna 'en' por defecto.

export const validateLang = (lang?: string): "es" | "en" | "pt" => {
const allowedLangs = ["es", "en", "pt"];
return allowedLangs.includes(lang || "")
? (lang as "es" | "en" | "pt")
: "en";
};

🔧 Parámetro:

  • lang: string opcional que representa el idioma solicitado.

🧪 Funcionamiento:

  • Verifica si el idioma está dentro de los aceptados.
  • Si no, retorna 'en' como predeterminado.

🐿️ EJEMPLO DE USO

const lang = validateLang(body.lang);
const { subject, text, html } = EMAIL_CONTENT[lang].updated;

En este ejemplo usamos los email, para dependiendo al idioma enviado por el usuario cambia el texto del email para adecuarse a su lenguaje.

El textos del email se maneja como constantes las cuales varían dependiendo de lo que se requiera como lo mostraba el ejemplo anterior

export const USER_STRIPE_EMAIL_PLUGIN = "email";
export const USER_PASSWORDS_RESET_MESSAGE =
"If an account with this email exists, a recovery code has been sent.";
export const EMAIL_CONTENT = {
en: {
recovery: {
subject: "Your recovery code",
text: (code: string, vertical: string) =>
`Your code to reset your password for the "${vertical}" vertical is: ${code}`,
html: (code: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Hello,</p>
<p>Your recovery code is:</p>
<h2 style="color: #FF6600;">${code}</h2>
<p>This code will expire in 15 minutes.</p>
<p>If you didn't request this, you can safely ignore this message.</p>
</div>
`,
},
updated: {
subject: "Your password has been updated",
text: (vertical: string) =>
`Your password for the "${vertical}" vertical has been successfully updated. If you did not perform this action, please contact support immediately.`,
html: (vertical: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Hello,</p>
<p>This is a confirmation that your password for the <strong>${vertical}</strong> vertical has been successfully updated.</p>
<p>If you did not request this change, please contact our support team immediately.</p>
<p style="margin-top: 16px;">Thank you,</p>
<p>The Support Team</p>
</div>
`,
},
},
es: {
recovery: {
subject: "Tu código de recuperación",
text: (code: string, vertical: string) =>
`Tu código para restablecer la contraseña del vertical "${vertical}" es: ${code}`,
html: (code: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Hola,</p>
<p>Tu código de recuperación es:</p>
<h2 style="color: #FF6600;">${code}</h2>
<p>Este código expirará en 15 minutos.</p>
<p>Si no solicitaste esto, puedes ignorar este mensaje.</p>
</div>
`,
},
updated: {
subject: "Tu contraseña ha sido actualizada",
text: (vertical: string) =>
`Tu contraseña para el vertical "${vertical}" se ha actualizado correctamente. Si no realizaste esta acción, contacta al soporte inmediatamente.`,
html: (vertical: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Hola,</p>
<p>Este es un aviso de que tu contraseña del vertical <strong>${vertical}</strong> se ha actualizado correctamente.</p>
<p>Si no solicitaste este cambio, contacta al equipo de soporte inmediatamente.</p>
<p style="margin-top: 16px;">Gracias,</p>
<p>El equipo de soporte</p>
</div>
`,
},
},
pt: {
recovery: {
subject: "Seu código de recuperação",
text: (code: string, vertical: string) =>
`Seu código para redefinir a senha do vertical "${vertical}" é: ${code}`,
html: (code: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Olá,</p>
<p>Seu código de recuperação é:</p>
<h2 style="color: #FF6600;">${code}</h2>
<p>Este código expirará em 15 minutos.</p>
<p>Se você não solicitou isso, pode ignorar esta mensagem.</p>
</div>
`,
},
updated: {
subject: "Sua senha foi atualizada",
text: (vertical: string) =>
`Sua senha para o vertical "${vertical}" foi atualizada com sucesso. Se você não realizou essa ação, entre em contato com o suporte imediatamente.`,
html: (vertical: string) => `
<div style="font-family: sans-serif; line-height: 1.5;">
<p>Olá,</p>
<p>Esta é uma confirmação de que sua senha para o vertical <strong>${vertical}</strong> foi atualizada com sucesso.</p>
<p>Se você não solicitou essa alteração, entre em contato com nossa equipe de suporte imediatamente.</p>
<p style="margin-top: 16px;">Obrigado,</p>
<p>Equipe de Suporte</p>
</div>
`,
},
},
};