Search Query Validation with React Router 6 and Yup and Typescript

Do you validate search queries in your frontend app? The answer is “sometimes”, but if you think of the search query as input for the application, you will change your mind.

Do you validate search queries in your frontend app? For me, the answer is “sometimes”, but if you think of the search query as input for the application the answer should always be instead.

Until now, even when I have done it, I have done it completely wrong. Or maybe it was not completely wrong as at least there was validation applied. Still, there would be ways to improve the readability, and reusability of my solutions.

The problem

Let’s see a real-world example, building a “set password page” for an application. One of the standard flows is that an end user clicks on a link in their email, in the link there is a code with which we can verify the validity of the request and send the new password. In the following example, I will add an “oobCode” and a “mode” in the URL (for integrating into a firebase reset password flow).

The (wrong?) solution

One of the ways in React (with ReactRouter 6) is the useLocation hook, which yields the search (string, like ?oobCode=123aa3&mode=resetPassword), and then parses it with the query-string package. (Although you can use useSearchParams hook as well, that won’t yield a plain JS object so it would still need some work afterward.)

const { search } = useLocation();

const query = qs.parse(search);

So next for the validation. What do we want to validate?

  • The oobCode exists and it is a string
  • The mode exists and it is one of the following:
  • resetPassword
  • recoverEmail
  • verifyEmail

So, something like this would achieve the solution:

enum AuthActionMode {

RESET_PASSWORD = "resetPassword",

RECOVER_EMAIL = "recoverEmail",

VERIFY_EMAIL = "verifyEmail",

}

export function CustomAuthActionPage() {

const [hasInvalidParams, setHasInvalidParams] = useState(false);

const { search } = useLocation();

const query = qs.parse(search);

useEffect(() => {

setHasInvalidParams(false);

if (typeof query.oobCode !== "string") {

setHasInvalidParams(true);

}

if (

typeof query.mode !== "string" ||

(typeof query.mode === "string" && !Object.values(AuthActionMode).includes(query.mode))

) {

setHasInvalidParams(true);

}

}, [query]);

if (hasInvalidParams) {

return <div>Invalid params</div>;

}

return query.mode; // do something with the params

}

Pretty ugly, right? Not to mention it is ugly, but also, we don’t get any “type hints” from Typescript as well, though it does the job, and we can be sure that our query is in the correct format.

The better solution

Then, how can we improve it? Schema validation to the rescue, there are a lot of great JavaScript (and TypeScript) based schema validation libraries, like Joi, Yup, and Zod (and I am sure the list can go on). In the following example, I will use Yup as my personal favorite (I am also using Yup with react-hook-forms for form validation as well) What were my expectations from this hook?

Generic way of working

Input: Yup schema
Output: Parsed and validated query with type safety and type hints Error object if there is any.

The first step is to extract the logic from the component into a custom hook, which would already make our component's code a bit cleaner.

export function useParsedQueryString() {

const { search } = useLocation();

const query = qs.parse(search);

return {

query,

};

}

The next step is to add the generic validation schema as input for the hook. Luckily for us Yup has a great Typescript support, so we just need to take advantage of it. It provides a generic type name ObjectSchema, which we can easily parameterize with our generic type.

export function useParsedQueryString<T extends yup.AnyObject>(

validationSchema: yup.ObjectSchema<T>,

);

The only interesting part is that the generic (T) parameter or useParsedQueryString extends the AnyObject, but it is just a type alias for {[k: string]: any;} (any object with string keys).

The next step is to create some variables to hold the current state of the query, the first will be the parsed and validated query, and the second will be the error. Both are nullable — if there is an error, the query object will be null, if there is no error, the error will be null.

export function useParsedQueryString<T extends yup.AnyObject>(

validationSchema: yup.ObjectSchema<T>,

) {

const { search } = useLocation();

const [validatedQuery, setValidatedQuery] = useState<T | null>(null);

const [validationError, setValidationError] = useState<yup.ValidationError | null>(null);

useEffect(() => {

const query = qs.parse(search);

setValidatedQuery(result as T);

}, [search]);

return {

query: validatedQuery,

error: validationError,

};

}

The last piece of code is the actual validation. The Yup schema provides a validateSync method which returns the parsed (and transformed!) object or it throws a ValidationError instance:

useEffect(() => {

const query = qs.parse(search);

try {

const result = validationSchema?.validateSync(query);

setValidatedQuery(result as T);

} catch (e) {

if (e instanceof yup.ValidationError) {

setValidationError(e);

setValidatedQuery(null);

} else {

throw e;

}

}

}, [search, validationSchema]);

With this, everything is ready to use with our shiny new search query validation hook on our component, which is fairly easy.

First, we need to construct the schema:

const queryValidationSchema = yup.object({

oobCode: yup.string().required(),

mode: yup.mixed<AuthActionMode>().oneOf(Object.values(AuthActionMode)),

});

Then we can use it in the hook:

export function CustomAuthActionPage() {

const { query, error } = useParsedQueryString(queryValidationSchema);

if (error) {

return <div>Invalid Params</div>;

}

return query.mode; // do something with the params

}

See how elegant this solution is compared to the one we started with?

And for the last requirement, type safety: with Yup, the type safety for the query result is “free” for us to just use:

As you can see with this fairly simple piece of code, we can increase the safety of our application from possible errors (as we are forced to validate and handle missing and not well-formatted queries) and also increase the developer experience with correct type hints as well.

(+1) Create a reusable enum schema

So in the previous examples I have used the following method for validating wether something is an enum value.

yup.mixed<AuthActionMode>().oneOf(Object.values(AuthActionMode)),

But what if we are validating different enums throughout our (possibly large) codebase? The solution can be very cumbersome and has a lot of boilerplate code.

What is a better solution for this? In Yup we can create custom validation methods with yup.addMethod like this:

Yup.addMethod(Yup.mixed, 'enum', function enumSchema(this, enumValue) {

return this.oneOf(Object.values(enumValue));

});

The tricky part is how we can make this in a way that TypeScript can infer the correct types for our schema? Module Augmentation for the rescue!

We should extend the Yup package MixedSchema with a new “enum” named method, which gets an enum and returns a MixedSchema (as this is the pattern for method chaining)

declare module 'yup' {

interface MixedSchema {

enum<T extends EnumValue>(enumValue: T, message?: string): Yup.MixedSchema<T[keyof T]>;

}

}

Yup.addMethod(Yup.mixed, 'enum', function enumSchema<T extends EnumValue>(this: Yup.MixedSchema, enumValue: T) {

return this.oneOf(Object.values(enumValue) as T[keyof T][]);

});

But what is EnumValue in the example? Basically that is just a helper interface to make the parameter generic and can accept an enum:

export interface EnumValue {

[key: string]: string | number;

}

And with this new custom method, our enum validation in the schema becomes more readable, and we still get the correct types for the schema!

const queryValidationSchema = yup.object({

oobCode: yup.string().required(),

mode: yup.mixed().enum(AuthActionMode)

});

TL:DR solution ready for copy and paste

import { useEffect, useState } from 'react';

import { useLocation } from 'react-router-dom';

import * as yup from 'yup';

import qs from 'query-string';

export function useParsedQueryString<T extends yup.AnyObject>(

validationSchema: yup.ObjectSchema<T> | ReturnType<typeof yup.lazy<yup.ObjectSchema<T>>>,

) {

const { search } = useLocation();

const [validatedQuery, setValidatedQuery] = useState<T | null>(null);

const [validationError, setValidationError] = useState<yup.ValidationError | null>(null);

useEffect(() => {

const query = qs.parse(search);

try {

const result = validationSchema?.validateSync(query);

setValidatedQuery(result as T);

} catch (e) {

if (e instanceof yup.ValidationError) {

setValidationError(e);

setValidatedQuery(null);

} else {

throw e;

}

}

}, [search, validationSchema]);

return {

query: validatedQuery,

error: validationError,

};

}

enum AuthActionMode {

RESET_PASSWORD = 'resetPassword',

RECOVER_EMAIL = 'recoverEmail',

VERIFY_EMAIL = 'verifyEmail',

}

const queryValidationSchema = yup.object({

oobCode: yup.string().required(),

mode: yup.mixed<AuthActionMode>().oneOf(Object.values(AuthActionMode)),

});

export function CustomAuthActionPage() {

const { query, error } = useParsedQueryString(queryValidationSchema);

if (error) {

return <div>Invalid Params</div>;

}

return null; // do something with the params

}

Author: Martin Horváth

Other articles

Sep 20, 2024

Emotional Intelligence in IT Project Management

Emotional intelligence is key in IT project management. It helps leaders manage teams, resolve conflicts, and build stronger relationships, leading to more successful projects

Oct 20, 2024

Great Leaders Anticipate, Not Just React

Ready to elevate your leadership? Discover how anticipating challenges, not just reacting, can unlock success. Don't miss out on this game-changing mindset shift!...

Aug 30, 2024

How Our StemX Editors Are Revolutionizing Ticket Sales

Discover how StemX Editors are revolutionizing ticket sales with innovative design tools! See how InterTicket's new suite boosts efficiency and transforms user experiences....

AI Estimation

Let Us Provide the Perfect Solution for Your Project

Looking for accurate project estimates? Our AI-powered app helps you plan smarter. Contact us today, and we’ll provide a customized quote for your business needs — quick, reliable, and aligned with your goals.

Other articles

Sep 20, 2024

Emotional Intelligence in IT Project Management

Emotional intelligence is key in IT project management. It helps leaders manage teams, resolve conflicts, and build stronger relationships, leading to more successful projects

Oct 20, 2024

Great Leaders Anticipate, Not Just React

Ready to elevate your leadership? Discover how anticipating challenges, not just reacting, can unlock success. Don't miss out on this game-changing mindset shift!...

Aug 30, 2024

How Our StemX Editors Are Revolutionizing Ticket Sales

Discover how StemX Editors are revolutionizing ticket sales with innovative design tools! See how InterTicket's new suite boosts efficiency and transforms user experiences....

AI Estimation

Let Us Provide the Perfect Solution for Your Project

Looking for accurate project estimates? Our AI-powered app helps you plan smarter. Contact us today, and we’ll provide a customized quote for your business needs — quick, reliable, and aligned with your goals.

Other articles

Sep 20, 2024

Emotional Intelligence in IT Project Management

Emotional intelligence is key in IT project management. It helps leaders manage teams, resolve conflicts, and build stronger relationships, leading to more successful projects

Oct 20, 2024

Great Leaders Anticipate, Not Just React

Ready to elevate your leadership? Discover how anticipating challenges, not just reacting, can unlock success. Don't miss out on this game-changing mindset shift!...

Aug 30, 2024

How Our StemX Editors Are Revolutionizing Ticket Sales

Discover how StemX Editors are revolutionizing ticket sales with innovative design tools! See how InterTicket's new suite boosts efficiency and transforms user experiences....

Jan 28, 2024

How to Choose Between Monolithic Code and Microservices

Decipher Microservices vs. Monolithic Code: Choose wisely for a seamless development journey. Plan, embrace flexibility and avoid pitfalls. Insights from a DevOps developer.

Jan 28, 2024

Website vs Web Application: Which One Do You Need?

Decipher Microservices vs. Monolithic Code: Choose wisely for a seamless development journey. Plan, embrace flexibility and avoid pitfalls. Insights from a DevOps developer.

Sep 14, 2023

A Guide to Keeping Dependencies Up-to-Date for Enhanced Security and Efficiency in Development

Learn the importance of keeping dependencies up-to-date for a secure and efficient development process...

Sep 14, 2022

No Code Pro / Contra

No-code has caused an unusual, disruptive revelation and changes in the tech space – especially coding. Are programmers and coding still relevant or not?

Aug 14, 2022

Here are Some Reasons Why You Should Become a Software Engineer

Yes, software engineering is a good job based on virtually any criteria, including salary, the number of job openings, as well as overall job satisfaction...

Jul 14, 2022

A Guide to Working Remotely at Sea: Best Practices

Putting some effort into planning will make your entire trip more enjoyable. If you’re independently setting sail, plot a course that takes your availability into account.

Jun 14, 2022

11 Essential Skills to Become a Software Developer in 2022

Key skills for programmers and software developers to learn in 2022. If you have been doing software development for some time and thinking about what makes a good programmer?

Aug 16, 2023

How to Implement Request Validation with OAPI-Codegen and Go for Robust Development

Several of our projects require back-end as well as front-end work. If a client wants us to create RESTful APIs, we start by using the OpenAPI 3 specification.

Aug 31, 2023

The Best Way to Optimize Serverless Computing

Explore the latest features of Cloud Run for seamless deployment and scaling...

Nov 16, 2023

Discover How reCAPTCHA v3 and Cloud Armor Can Protect your Website

Discover advanced bot prevention strategies using reCAPTCHA v3 and Cloud Armor. Safeguard your website with CommIT Smart's insightful guide...

Oct 12, 2023

How to Effortlessly Automate SSL Deployment with Lego and Kubernetes

Automate SSL certificate management seamlessly with Lego and Kubernetes. Learn efficient deployment practices for a secure web environment at CommIT Smart...

AI Estimation

Let Us Provide the Perfect Solution for Your Project

Looking for accurate project estimates? Our AI-powered app helps you plan smarter. Contact us today, and we’ll provide a customized quote for your business needs — quick, reliable, and aligned with your goals.

Other articles

Sep 20, 2024

Emotional Intelligence in IT Project Management

Emotional intelligence is key in IT project management. It helps leaders manage teams, resolve conflicts, and build stronger relationships, leading to more successful projects

Oct 20, 2024

Great Leaders Anticipate, Not Just React

Ready to elevate your leadership? Discover how anticipating challenges, not just reacting, can unlock success. Don't miss out on this game-changing mindset shift!...

Aug 30, 2024

How Our StemX Editors Are Revolutionizing Ticket Sales

Discover how StemX Editors are revolutionizing ticket sales with innovative design tools! See how InterTicket's new suite boosts efficiency and transforms user experiences....

AI Estimation

Let Us Provide the Perfect Solution for Your Project

Looking for accurate project estimates? Our AI-powered app helps you plan smarter. Contact us today, and we’ll provide a customized quote for your business needs — quick, reliable, and aligned with your goals.

Other articles

Sep 20, 2024

Emotional Intelligence in IT Project Management

Emotional intelligence is key in IT project management. It helps leaders manage teams, resolve conflicts, and build stronger relationships, leading to more successful projects

Oct 20, 2024

Great Leaders Anticipate, Not Just React

Ready to elevate your leadership? Discover how anticipating challenges, not just reacting, can unlock success. Don't miss out on this game-changing mindset shift!...

Aug 30, 2024

How Our StemX Editors Are Revolutionizing Ticket Sales

Discover how StemX Editors are revolutionizing ticket sales with innovative design tools! See how InterTicket's new suite boosts efficiency and transforms user experiences....

AI Estimation

Let Us Provide the Perfect Solution for Your Project

Looking for accurate project estimates? Our AI-powered app helps you plan smarter. Contact us today, and we’ll provide a customized quote for your business needs — quick, reliable, and aligned with your goals.

Get in touch

Reach Out to Us!

Have questions or want to learn more about what we do at CommIT Smart? We’d love to hear from you! Whether you’re curious about our work or just want to start a conversation, don’t hesitate to reach out. Our team is here and ready to connect — let’s talk!

Get in touch

Reach Out to Us!

Have questions or want to learn more about what we do at CommIT Smart? We’d love to hear from you! Whether you’re curious about our work or just want to start a conversation, don’t hesitate to reach out. Our team is here and ready to connect — let’s talk!

Get in touch

Reach Out to Us!

Have questions or want to learn more about what we do at CommIT Smart? We’d love to hear from you! Whether you’re curious about our work or just want to start a conversation, don’t hesitate to reach out. Our team is here and ready to connect — let’s talk!

Get in touch

Reach Out to Us!

Have questions or want to learn more about what we do at CommIT Smart? We’d love to hear from you! Whether you’re curious about our work or just want to start a conversation, don’t hesitate to reach out. Our team is here and ready to connect — let’s talk!

Get in touch

Reach Out to Us!

Have questions or want to learn more about what we do at CommIT Smart? We’d love to hear from you! Whether you’re curious about our work or just want to start a conversation, don’t hesitate to reach out. Our team is here and ready to connect — let’s talk!

We are innovators in developing most recent web technologies, blockchain, digital technologies and apps in ingenious ways.

The D&B certificate is awarded exclusively to businesses with excellent creditworthiness. Possession of the certificate guarantees that establishing a business relationship with our company carries a low financial risk.

© 2024 Commitsmart Kft. All rights reserved.

We are innovators in developing most recent web technologies, blockchain, digital technologies and apps in ingenious ways.

The D&B certificate is awarded exclusively to businesses with excellent creditworthiness. Possession of the certificate guarantees that establishing a business relationship with our company carries a low financial risk.

© 2024 Commitsmart Kft. All rights reserved.

We are innovators in developing most recent web technologies, blockchain, digital technologies and apps in ingenious ways.

The D&B certificate is awarded exclusively to businesses with excellent creditworthiness. Possession of the certificate guarantees that establishing a business relationship with our company carries a low financial risk.

© 2024 Commitsmart Kft. All rights reserved.

We are innovators in developing most recent web technologies, blockchain, digital technologies and apps in ingenious ways.

The D&B certificate is awarded exclusively to businesses with excellent creditworthiness. Possession of the certificate guarantees that establishing a business relationship with our company carries a low financial risk.

© 2024 Commitsmart Kft. All rights reserved.

We are innovators in developing most recent web technologies, blockchain, digital technologies and apps in ingenious ways.

The D&B certificate is awarded exclusively to businesses with excellent creditworthiness. Possession of the certificate guarantees that establishing a business relationship with our company carries a low financial risk.

© 2024 Commitsmart Kft. All rights reserved.