npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rabstack/rab-query-decorators

v0.8.0

Published

Query decorators for rab-api that integrate rab-query with schema validation

Readme

@rabstack/rab-query-decorators

Query decorators that seamlessly integrate @rabstack/rab-api with @rabstack/rab-query.

Provides the @Query decorator with built-in query parameter parsing, validation, and type safety.

Features

  • Seamless Integration: Connects rab-api routing with rab-query parsing
  • Type-Safe: Full TypeScript support with generic types
  • Joi Validation: Optional schema validation for query parameters
  • Zero Configuration: Works out of the box with sensible defaults

Installation

npm install @rabstack/rab-query-decorators @rabstack/rab-api @rabstack/rab-query joi

Quick Start

import { Query } from '@rabstack/rab-query-decorators';
import { AtomApiGet } from '@rabstack/rab-api';
import Joi from 'joi';

@Query('/users', {
  querySchema: Joi.object({
    name: Joi.string().optional(),
    email: Joi.string().email().optional(),
    age: Joi.number().optional(),
  }),
  permission: 'canListUsers',
})
export class ListUsersController implements AtomApiGet {
  async execute(request) {
    // request.query is automatically parsed and validated
    const { page, pageSize, orderBy, ...filters } = request.query;

    return this.userService.findMany({
      where: filters,
      take: pageSize,
      skip: (page - 1) * pageSize,
      orderBy,
    });
  }
}

API Reference

Query(path, options?)

Decorator for GET routes with integrated query parameter parsing.

Parameters:

  • path (string): The route path
  • options (ControllerRouteDefinitionOptions): Optional configuration
    • querySchema (Joi.ObjectSchema): Joi schema for validation
    • permission (string): Permission required to access this route
    • ... other rab-api options

Returns: ClassDecorator

buildQueryValidationSchema(baseSchema, options?)

Builds a Joi validation schema for query parameters.

Parameters:

  • baseSchema (Joi.ObjectSchema): Your data model schema
  • options (object): Optional configuration
    • maxPageSize (number): Maximum allowed page size (default: 1000)

Returns: Joi.ObjectSchema

validateQuery(query, baseSchema, options?)

Validates and transforms query parameters.

Parameters:

  • query (any): Raw query object
  • baseSchema (Joi.ObjectSchema): Your data model schema
  • options (object): Optional configuration

Returns: { value: T, error?: Joi.ValidationError }

Usage Examples

Basic Usage (No Validation)

import { Query } from '@rabstack/rab-query-decorators';

@Query('/products')
export class ListProductsController implements AtomApiGet {
  async execute(request) {
    // Query parameters are automatically parsed
    // URL: /products?name__contains=phone&price__gte=100
    console.log(request.query);
    // {
    //   name: { contains: 'phone', mode: 'insensitive' },
    //   price: { gte: 100 },
    //   page: 1,
    //   pageSize: 10
    // }

    return this.productService.findMany({ where: request.query });
  }
}

With Joi Validation

import { Query } from '@rabstack/rab-query-decorators';
import Joi from 'joi';

const userQuerySchema = Joi.object({
  name: Joi.string().max(100),
  email: Joi.string().email(),
  age: Joi.number().min(0).max(150),
  status: Joi.string().valid('active', 'inactive', 'pending'),
  createdAt: Joi.date(),
});

@Query('/users', {
  querySchema: userQuerySchema,
  permission: 'users:read',
})
export class SearchUsersController implements AtomApiGet {
  async execute(request) {
    // Validated and type-safe query
    return this.userService.search(request.query);
  }
}

Nested Object Schema

import { Query } from '@rabstack/rab-query-decorators';
import Joi from 'joi';

const orderQuerySchema = Joi.object({
  customer: Joi.object({
    name: Joi.string(),
    email: Joi.string().email(),
  }),
  product: Joi.object({
    name: Joi.string(),
    category: Joi.string(),
    price: Joi.number(),
  }),
  status: Joi.string().valid('pending', 'shipped', 'delivered'),
});

@Query('/orders', { querySchema: orderQuerySchema })
export class ListOrdersController implements AtomApiGet {
  async execute(request) {
    // URL: /orders?customer.name__contains=John&product.price__gte=100
    // Parses to: { customer: { name: { contains: 'John' } }, product: { price: { gte: 100 } } }
    return this.orderService.findMany({ where: request.query });
  }
}

Custom Page Size Limit

import { Query, buildQueryValidationSchema } from '@rabstack/rab-query-decorators';
import Joi from 'joi';

const schema = buildQueryValidationSchema(
  Joi.object({
    name: Joi.string(),
  }),
  { maxPageSize: 50 } // Limit to 50 items per page
);

@Query('/items', { querySchema: schema })
export class ListItemsController implements AtomApiGet {
  async execute(request) {
    // pageSize is capped at 50
    return this.itemService.findMany(request.query);
  }
}

Manual Validation

If you need more control, use validateQuery directly:

import { validateQuery } from '@rabstack/rab-query-decorators';
import Joi from 'joi';

const schema = Joi.object({
  name: Joi.string(),
  age: Joi.number(),
});

const { value, error } = validateQuery(req.query, schema);

if (error) {
  throw new ValidationError(error.message);
}

// Use validated query
const results = await db.findMany({ where: value });

Supported Query Features

All features from @rabstack/rab-query are automatically supported:

Operators

  • Comparison: equals, not, in, notIn, lt, lte, gt, gte
  • String: contains, startsWith, endsWith, search
  • Array: has, hasSome, hasEvery, isEmpty
  • Null: isSet

Logical Operators

  • OR - Match any condition
  • AND - Match all conditions
  • NOT - Negate conditions

Pagination

  • page - Page number (default: 1)
  • pageSize - Items per page (default: 10, max: 1000)

Sorting

  • orderBy - Sort by fields (supports nested fields)

Query String Format

# Basic operator
?name__contains=John

# Nested fields
?user.profile.age__gte=18

# Logical operators
?age__gt__or=18&age__lt__or=65

# Arrays
?status__in=active,pending,approved

# Pagination
?page=2&pageSize=20

# Sorting
?orderBy=name__asc,createdAt__desc

Integration with rab-api

This package decouples @rabstack/rab-api from @rabstack/rab-query.

Before (tightly coupled):

// rab-api had direct dependency on rab-query
import { Query } from '@rabstack/rab-api';

After (decoupled):

// rab-api is independent
// rab-query is independent
// query-decorators connects them
import { Query } from '@rabstack/rab-query-decorators';

TypeScript Support

Full type safety with generic types:

import { RabQueryParams } from '@rabstack/rab-query-decorators';

interface User {
  name: string;
  email: string;
  age: number;
}

@Query('/users')
export class ListUsersController implements AtomApiGet {
  async execute(request: { query: RabQueryParams<User> }) {
    // request.query is fully typed
    return this.userService.findMany(request.query);
  }
}

Best Practices

  1. Always use querySchema: Validate user input to prevent invalid queries
  2. Set maxPageSize: Protect your API from excessive page sizes
  3. Use TypeScript: Leverage type safety for better developer experience
  4. Document your API: Share supported query parameters with API consumers

Dependencies

This package requires:

  • @rabstack/rab-api (peer dependency)
  • @rabstack/rab-query (peer dependency)
  • joi (peer dependency)
  • reflect-metadata (peer dependency)

Related Packages

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.