Zod RequestZod Request

Validating Hostname

You can validate the hostname of a request using string schemas. The hostname is extracted from the request URL (e.g., "example.com" or "api.example.com").

Using z.literal

The simplest way is to use Zod's z.literal():

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function GET(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.literal("example.com"),
  }).parseAsync(request);

  const hostname = validatedRequest.hostname; // "example.com"
}

Using z.string()

You can use any string schema to validate the hostname:

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function GET(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.string(),
  }).parseAsync(request);

  const hostname = validatedRequest.hostname; // e.g., "example.com"
}

Using String Validation Methods

You can use Zod's string validation methods to validate hostnames:

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function GET(request: Request) {
  // Validate hostname contains "api"
  const validatedRequest = await requestSchema({
    hostname: z.string().includes("api"),
  }).parseAsync(request);

  const hostname = validatedRequest.hostname;
}

Using Regular Expressions

You can use regex patterns to validate hostnames:

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function GET(request: Request) {
  // Validate hostname starts with "api."
  const validatedRequest = await requestSchema({
    hostname: z.string().regex(/^api\./),
  }).parseAsync(request);

  const hostname = validatedRequest.hostname;
}

Hostname Validation with Other Validations

You can combine hostname validation with other request validations:

import {
  requestSchema,
  protocolSchema,
  searchParamsSchema,
  bodySchema,
  headersSchema,
  httpMethodSchema,
  requestModeSchema,
} from "@nicnocquee/zod-request";
import { z } from "zod";

export async function POST(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.literal("api.example.com"),
    protocol: protocolSchema("https"),
    method: httpMethodSchema("POST"),
    mode: requestModeSchema("cors"),
    searchParams: searchParamsSchema(
      z.object({
        filter: z.string(),
      })
    ),
    body: bodySchema({
      json: z.object({
        name: z.string(),
      }),
    }),
    headers: headersSchema(
      z.object({
        authorization: z.string(),
      })
    ),
  }).parseAsync(request);

  const hostname = validatedRequest.hostname; // "api.example.com"
  const protocol = validatedRequest.protocol; // "https"
  const method = validatedRequest.method; // "POST"
  const mode = validatedRequest.mode; // "cors"
  const filter = validatedRequest.searchParamsObject?.filter;
  const name = validatedRequest.bodyObject?.name;
  const auth = validatedRequest.headersObj?.authorization;
}

Error Handling

If the request hostname doesn't match the schema, a validation error will be thrown if you use parse or parseAsync.

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function handler(request: Request) {
  try {
    const validatedRequest = await requestSchema({
      hostname: z.literal("api.example.com"),
    }).parseAsync(request);
  } catch (error) {
    // Request hostname was not "api.example.com"
    // Handle error appropriately
  }
}

Hostname Extraction

The hostname is automatically extracted from the request URL. The hostname does not include the port number, even if the URL contains one. For example, a URL like https://example.com:8080 will have a hostname of "example.com" (without the port).

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

// Request with "https://example.com:8080" will have hostname "example.com"
export async function handler(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.literal("example.com"), // Port is not included
  }).parseAsync(request);

  const hostname = validatedRequest.hostname; // "example.com" (without port)
}

Security Considerations

When building secure applications, you should validate hostnames to ensure requests are coming from expected domains:

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function secureHandler(request: Request) {
  // Enforce specific hostname for security
  const validatedRequest = await requestSchema({
    hostname: z.literal("api.example.com"),
  }).parseAsync(request);

  // Only requests from "api.example.com" will pass validation
  // Requests from other hostnames will be rejected
}

You can also use pattern matching to allow multiple subdomains:

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function handler(request: Request) {
  // Allow any subdomain of example.com
  const validatedRequest = await requestSchema({
    hostname: z.string().regex(/^[a-z0-9-]+\.example\.com$/),
  }).parseAsync(request);

  // Matches: api.example.com, www.example.com, etc.
  // Does not match: example.com, other.com, etc.
}

Common Use Cases

Validating API Endpoints

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function apiHandler(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.literal("api.example.com"),
    protocol: z.literal("https"),
  }).parseAsync(request);

  // Only HTTPS requests to api.example.com are allowed
}

Allowing Multiple Environments

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function handler(request: Request) {
  const validatedRequest = await requestSchema({
    hostname: z.enum([
      "localhost",
      "api.example.com",
      "api-staging.example.com",
    ]),
  }).parseAsync(request);

  // Allows requests from localhost, production, or staging
}

Validating Subdomains

import { requestSchema } from "@nicnocquee/zod-request";
import { z } from "zod";

export async function handler(request: Request) {
  // Only allow API subdomain
  const validatedRequest = await requestSchema({
    hostname: z.string().startsWith("api."),
  }).parseAsync(request);

  // Matches: api.example.com, api.staging.example.com
}