Take control of query parameters using TypeScript-powered utilities like queryExtractor, paramsExtractor, and fetchData. A real-world guide to scaling your dynamic pages in React & Next.js
π Overview
If you're building a modern frontend application with Next.js App Router (14/15), youβre likely dealing with query params for:
- Search
- Filter (price range, brand, category, color)
- Sort
- Pagination
This article will walk you through how to parse, build, and use query parameters using powerful, reusable TypeScript utilities like:
π paramsExtractor
π§ queryExtractor
βοΈ fetchData
π§ Why This Approach?
β
Next.js 14 compatible β works with App Router and server components
β
Type-safe and reusable
β
Easily composable with filters, pagination, and API queries
β
Works seamlessly with React Query or server-side fetching
β
Clean and maintainable for long-term scaling
π§© Example: Category Page in Next.js
const CategoryPage = async ({
searchParams,
}: {
searchParams: Promise<Record<string, string | string[]> | undefined>;
}) => {
const { filter } = await paramsExtractor({
searchParam: searchParams,
});
const stringifiedQuery = queryExtractor({
sortBy: filter?.sortBy,
extra: {
"variants.sellingPrice[gte]": filter?.min_price,
"variants.sellingPrice[lte]": filter?.max_price,
"variants.variantName": filter?.color_name
? filter.color_name.split(",").map((b) => b.trim())
: [],
"brand.brandName": filter?.brandNames
? filter.brandNames.split(",").map((b) => b.trim())
: [],
"category.categoryName": filter?.categoryNames
? filter.categoryNames.split(",").map((b) => b.trim())
: [],
},
});
const productsData = await fetchData({
route: "/product",
query: stringifiedQuery,
limit: 18,
tags: ["/product"],
});
// render your component using productsData
};
π§© What Is paramsExtractor?
If you havenβt read it yet, check out the full breakdown of paramsExtractor. It's a utility that parses searchParams from Next.js and returns a normalized object with:
- Cleaned values
- Mapped keys
- Optional conversion to array, number, or string
- Proper defaults
Perfect for server-side usage in dynamic routes like /category?brand=Apple,Samsung&sortBy=price.
π§ queryExtractor: Build Clean Query Strings
This utility transforms a QueryOptions object into a query string with support for:
- Defaults
- Nested filtering (e.g., variants.sellingPrice[gte])
- Arrays
- Optional values
β‘ fetchData: Data Fetching Made Clean
fetchData is an abstraction over fetch (or Axios) with a powerful config API:
await fetchData({
route: "/product",
query: "category=Phone&brand=Apple",
limit: 18,
tags: ["/product"],
});
More on this utility in the official guide π read now
π§΅ Connect Everything
With these 3 pieces, your Next.js dynamic page becomes scalable and maintainable:
- paramsExtractor: Server-side param parsing
- queryExtractor: Build structured queries
- fetchData: Fetch paginated, sorted, and filtered data from the backend
All of this while keeping TypeScript and React Query (or fetch) integration seamless.
π§ Using queryExtractor as a Standalone Utility
π¦ Step 1: Add the Utility Function
Hereβs the complete code for queryExtractor:
export interface QueryOptions {
searchTerm?: string;
sortBy?: string;
sortOrder?: "asc" | "desc";
page?: number;
limit?: number;
extra?: Record<string, any>;
}
export const queryExtractor = ({
searchTerm,
sortBy = "created_at",
sortOrder = "desc",
page = 1,
limit = 10,
extra = {},
}: QueryOptions): string => {
const query = new URLSearchParams();
if (searchTerm?.trim()) query.set("searchTerm", searchTerm.trim());
query.set("sortBy", sortBy);
query.set("sortOrder", sortOrder);
query.set("page", Math.max(1, page).toString());
query.set("limit", Math.max(1, limit).toString());
Object.entries(extra).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
if (Array.isArray(value)) {
value.forEach((val) => {
if (val !== "") query.append(key, String(val));
});
} else {
query.append(key, String(value));
}
}
});
return query.toString();
};
β
Step 2: Basic Example
import { queryExtractor } from "./queryExtractor";
const queryString = queryExtractor({
searchTerm: "laptop",
sortBy: "price",
sortOrder: "asc",
page: 2,
limit: 20,
extra: {
"brand": ["HP", "Dell"],
"category": "Electronics",
"price[gte]": 1000,
"price[lte]": 3000,
},
});
console.log(queryString);
// Output:
// searchTerm=laptop&sortBy=price&sortOrder=asc&page=2&limit=20&brand=HP&brand=Dell&category=Electronics&price[gte]=1000&price[lte]=3000
π‘Pro Tips
- Supports arrays (e.g. brand=HP&brand=Dell)
- Ignores empty/null/undefined values
- Safe defaults for sortBy, sortOrder, page, and limit
- Can be reused anywhere: backend services, Node.js CLI tools, etc.
π Final Thoughts
This setup:
β
Makes query management clean and DRY
β
Enhances developer experience with strong typing
β
Scales easily with complex filters
β
Plays nicely with Next.js App Router
β
Perfect for dynamic category/product/search pages
π¨βπ» About the Author
Name: JAKER HOSSAIN
Username: @jackfd120
Role: Senior Frontend Developer
Portfolio: https://www.poranfolio.space/
I write about scalable frontend architecture, modern React/Next.js patterns, and TypeScript best practices.
If you liked this post, feel free to follow or reach out via my portfolio!
Top comments (0)