Validating Search Params
You can validate the search params (URL query parameters) of a request by creating the schema for the search params using the searchParamsSchema function. If the validation is successful, the returned validated request will have a searchParamsObject property that contains the validated search parameters.
Basic Usage
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
// create the schema for the search params
const searchParams = searchParamsSchema(
z.object({
name: z.string(),
age: z.string(),
})
);
export async function GET(request: Request) {
// create the schema for the request
const schema = requestSchema({
searchParams,
});
const validatedRequest = await schema.parseAsync(request);
// the searchParamsObject property contains the validated search parameters
const { name, age } = validatedRequest.searchParamsObject;
}Optional Parameters
Use .optional() to make search parameters optional:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
name: z.string().optional(),
page: z.string().optional(),
})
);
export async function GET(request: Request) {
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
// Missing parameters will be undefined
const name = validatedRequest.searchParamsObject.name; // string | undefined
const page = validatedRequest.searchParamsObject.page; // string | undefined
}Parameter Transformations
You can transform search parameter values using Zod's transformation methods:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
age: z.string().transform((val) => Number(val)),
tags: z.string().transform((val) => val.split(",")),
})
);
export async function GET(request: Request) {
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
const age = validatedRequest.searchParamsObject.age; // number
const tags = validatedRequest.searchParamsObject.tags; // string[]
}Type Coercion
Since URL search parameters are always strings, you may need to coerce them to other types:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
page: z.string().transform(Number), // Convert to number
active: z.string().transform((val) => val === "true"), // Convert to boolean
ids: z.string().transform((val) => val.split(",").map(Number)), // Convert to number array
})
);
export async function GET(request: Request) {
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
const page = validatedRequest.searchParamsObject.page; // number
const active = validatedRequest.searchParamsObject.active; // boolean
const ids = validatedRequest.searchParamsObject.ids; // number[]
}Validation Rules
Apply Zod validation rules to search parameters:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
email: z.string().email(),
age: z.string().regex(/^\d+$/).transform(Number),
limit: z.string().transform(Number).pipe(z.number().min(1).max(100)),
})
);
export async function GET(request: Request) {
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
const email = validatedRequest.searchParamsObject.email; // Valid email string
const age = validatedRequest.searchParamsObject.age; // Number (validated as digits)
const limit = validatedRequest.searchParamsObject.limit; // Number between 1-100
}Empty Search Params
The function handles empty search params gracefully:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
name: z.string().optional(),
})
);
export async function GET(request: Request) {
// Works even if URL has no query string: /api/users
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
const name = validatedRequest.searchParamsObject.name; // undefined
}Special Characters and URL Encoding
The function automatically handles URL-encoded special characters:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
query: z.string(),
value: z.string(),
})
);
export async function GET(request: Request) {
// URL: /api/search?query=hello%20world&value=a%2Bb%3Dc%26d
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
const query = validatedRequest.searchParamsObject.query; // "hello world"
const value = validatedRequest.searchParamsObject.value; // "a+b=c&d"
}Multiple Values for Same Key
If a URL has multiple values for the same parameter, you must use an array schema. Otherwise, a validation error will be thrown:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
// ❌ This will throw an error if multiple values exist
const searchParams = searchParamsSchema(
z.object({
tag: z.string(), // Single value schema
})
);
// ✅ Use an array schema to accept multiple values
const searchParamsWithArray = searchParamsSchema(
z.object({
tag: z.array(z.string()), // Array schema
})
);
export async function GET(request: Request) {
// URL: /api/posts?tag=first&tag=second
const validatedRequest = await requestSchema({
searchParams: searchParamsWithArray,
}).parseAsync(request);
// All values are extracted as an array
const tags = validatedRequest.searchParamsObject.tag; // ["first", "second"]
}Use z.string() if you want to make sure that there is only one value for the
key. If you want to accept multiple values, use z.array(z.string()).
Important:
- If multiple values exist and the schema is not an array, a validation error is thrown
- If multiple values exist and the schema is an array, all values are returned as an array
- If a single value exists and the schema is an array, it's returned as an array with one element
- If no value exists and the schema is an optional array, it returns
undefined
Only Defined Parameters
The function only extracts search parameters that are defined in your schema. Other parameters in the URL are ignored:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
name: z.string(),
})
);
export async function GET(request: Request) {
// URL: /api/users?name=John&extra=value&another=param
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
// Only "name" is extracted, "extra" and "another" are ignored
const name = validatedRequest.searchParamsObject.name; // "John"
}Error Handling
Invalid search parameter values will throw a Zod validation error:
import { searchParamsSchema, requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";
const searchParams = searchParamsSchema(
z.object({
age: z.string().transform((val) => {
const num = Number(val);
if (Number.isNaN(num)) {
throw new Error("Invalid number");
}
return num;
}),
})
);
export async function GET(request: Request) {
try {
const validatedRequest = await requestSchema({
searchParams,
}).parseAsync(request);
} catch (error) {
// Handle validation error
if (error instanceof z.ZodError) {
// Invalid search parameter format
}
}
}Combining with Other Validations
You can combine search params validation with other request validations:
import {
searchParamsSchema,
requestSchema,
bodySchema,
headersSchema,
httpMethodSchema,
} from "@nicnocquee/zod-request";
import { z } from "zod";
export async function POST(request: Request) {
const validatedRequest = await requestSchema({
searchParams: searchParamsSchema(
z.object({
filter: z.string(),
})
),
body: bodySchema({
json: z.object({
name: z.string(),
}),
}),
headers: headersSchema(
z.object({
authorization: z.string(),
})
),
method: httpMethodSchema("POST"),
}).parseAsync(request);
const filter = validatedRequest.searchParamsObject.filter;
const name = validatedRequest.body.name;
const auth = validatedRequest.headersObj.authorization;
const method = validatedRequest.method;
}