WayrApp Backend & Ecosystem Documentation - v1.0.0
    Preparing search index...

    Function validateQuery

    • Convenience middleware factory for query parameter validation only

      Creates Express middleware that validates only query string parameters against a Zod schema, ignoring request body and URL parameters. This function is crucial for validating optional parameters that modify endpoint behavior, such as pagination settings, search filters, sorting options, and feature flags passed through the URL query string.

      Query parameters are typically used for optional functionality like filtering, sorting, pagination, and search. Since they come from user input in the URL, they must be validated to ensure they conform to expected formats and ranges. This validation prevents malformed queries from causing errors and helps maintain consistent API behavior.

      The middleware supports automatic type coercion, which is particularly useful for query parameters since they are always received as strings but often need to be converted to numbers, booleans, or other types. Zod's transformation capabilities make this conversion safe and predictable.

      This function is essential for implementing robust API endpoints that accept optional parameters while maintaining strict data validation and type safety throughout the application.

      Parameters

      • schema: ZodType

        Zod schema for validating query string parameters

      Returns (req: Request, _res: Response, next: NextFunction) => void

      Express middleware function that validates query parameters

      When query validation fails, forwards error to global error handler

      When non-Zod errors occur during validation

      // Pagination and search parameters
      const paginationSchema = z.object({
      page: z.string().transform(val => parseInt(val, 10)).pipe(z.number().int().min(1)).optional().default('1'),
      limit: z.string().transform(val => parseInt(val, 10)).pipe(z.number().int().min(1).max(100)).optional().default('20'),
      search: z.string().min(1).max(100).optional(),
      sortBy: z.enum(['name', 'createdAt', 'updatedAt']).optional().default('createdAt'),
      sortOrder: z.enum(['asc', 'desc']).optional().default('desc')
      });

      router.get('/courses', validateQuery(paginationSchema), courseController.list);
      // Boolean flags and filters
      const filterSchema = z.object({
      published: z.string().transform(val => val === 'true').pipe(z.boolean()).optional(),
      difficulty: z.enum(['beginner', 'intermediate', 'advanced']).optional(),
      hasVideo: z.string().transform(val => val === 'true').pipe(z.boolean()).optional(),
      minRating: z.string().transform(val => parseFloat(val)).pipe(z.number().min(0).max(5)).optional()
      });

      router.get('/courses/search', validateQuery(filterSchema), courseController.search);
      // Date range validation
      const dateRangeSchema = z.object({
      startDate: z.string().datetime('Invalid start date format').optional(),
      endDate: z.string().datetime('Invalid end date format').optional(),
      timezone: z.string().optional().default('UTC')
      }).refine(data => {
      if (data.startDate && data.endDate) {
      return new Date(data.startDate) <= new Date(data.endDate);
      }
      return true;
      }, 'Start date must be before end date');

      router.get('/analytics/progress', validateQuery(dateRangeSchema), analyticsController.getProgress);
      // Array parameters (comma-separated values)
      const arrayQuerySchema = z.object({
      tags: z.string().transform(val => val.split(',')).pipe(z.array(z.string().min(1))).optional(),
      categories: z.string().transform(val => val.split(',')).pipe(z.array(z.enum(['language', 'culture', 'grammar']))).optional(),
      exclude: z.string().transform(val => val.split(',')).pipe(z.array(z.string().uuid())).optional()
      });

      router.get('/content/filter', validateQuery(arrayQuerySchema), contentController.filter);