2.0.2 • Published 5 months ago

@origins-digital/query-filter-core v2.0.2

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

@origins-digital/query-filter-core

A core package for building type-safe query filters in TypeScript.

Installation

npm install @origins-digital/query-filter-core

Features

  • Type-safe query filtering
  • Support for complex filter conditions
  • Built-in comparison operators
  • OpenAPI/Swagger integration
  • Flexible filter grouping (AND/OR)
  • Support for nested object filtering
  • Zod schema validation

Usage

Basic Filtering with Zod

import { QueryFilter } from '@origins-digital/query-filter-core';
import { z } from 'zod';

// Define your schema with Zod
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  age: z.number(),
  isActive: z.boolean(),
});

type User = z.infer<typeof UserSchema>;

// Simple equality filter
const filter: QueryFilter<User> = {
  name: { eq: 'John' },
  age: { gt: 18 },
  isActive: { is: true },
};

// Validate filter with Zod
const validateFilter = (filter: unknown) => {
  return UserSchema.parse(filter);
};

Complex Filtering with Zod

import { z } from 'zod';
import { QueryFilter } from '@origins-digital/query-filter-core';

// Define nested schemas
const ProfileSchema = z.object({
  bio: z.string(),
  location: z.string(),
});

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  age: z.number(),
  isActive: z.boolean(),
  profile: ProfileSchema,
});

type User = z.infer<typeof UserSchema>;

// Complex filter with nested objects
const filter: QueryFilter<User> = {
  or: [
    { name: { contains: 'John' } },
    {
      and: [
        { age: { gt: 18 } },
        { isActive: { is: true } },
        { profile: { bio: { contains: 'developer' } } },
      ],
    },
  ],
};

Using ApiQuerifyZodSchema and ApiQueryZodSchema

import { z } from 'zod';
import {
  ApiQuerifyZodSchema,
  ApiQueryZodSchema,
} from '@origins-digital/query-filter-core';

// Define your entity schema with Zod
const UserSchema = z.object({
  name: z.string().describe('User full name'),
  age: z.number().describe('User age in years'),
  isActive: z.boolean().describe('Whether the user is active'),
  createdAt: z.date().describe('User creation date'),
  profile: z.object({
    bio: z.string().max(500).describe('User biography'),
    location: z.string().describe('User location'),
  }),
});

// Define query parameters schema
const UserQueryParamsSchema = z.object({
  page: z.number().min(1).default(1).describe('Page number'),
  limit: z.number().min(1).max(100).default(10).describe('Items per page'),
  sortBy: z
    .enum(['name', 'age', 'createdAt'])
    .default('createdAt')
    .describe('Field to sort by'),
  sortOrder: z.enum(['asc', 'desc']).default('desc').describe('Sort order'),
});

// Controller usage
import { Controller, Get, Query } from '@nestjs/common';
import { ApiQuery } from '@nestjs/swagger';
import { QueryFilter } from '@origins-digital/query-filter-core';

@Controller('users')
export class UserController {
  @Get()
  @ApiQuerifyZodSchema(UserSchema, {
    description: 'Filter users by various criteria',
    example: {
      name: { contains: 'John' },
      age: { gt: 18 },
      isActive: { is: true },
    },
  })
  async getUsers(
    @Query('filter') filter?: QueryFilter<z.infer<typeof UserSchema>>,
  ) {
    if (filter) {
      // Your implementation here
    }
  }

  @Get('search')
  @ApiQueryZodSchema(UserQueryParamsSchema)
  async searchUsers(@Query() query: z.infer<typeof UserQueryParamsSchema>) {
    // Your implementation here
    // query.page, query.limit, query.sortBy, query.sortOrder are typed
  }
}

Custom Zod Validations

import { z } from 'zod';
import { QueryFilter } from '@origins-digital/query-filter-core';

// Define custom validations
const UserSchema = z.object({
  name: z.string().min(2).max(50),
  age: z.number().min(0).max(120),
  email: z.string().email(),
  isActive: z.boolean(),
  createdAt: z.date(),
  profile: z.object({
    bio: z.string().max(500),
    location: z.string(),
  }),
});

// Create filter validation schema
const FilterSchema = z.object({
  name: z
    .object({
      eq: z.string().optional(),
      contains: z.string().optional(),
      startsWith: z.string().optional(),
      endsWith: z.string().optional(),
    })
    .optional(),
  age: z
    .object({
      gt: z.number().optional(),
      lt: z.number().optional(),
      gte: z.number().optional(),
      lte: z.number().optional(),
      in: z.array(z.number()).optional(),
    })
    .optional(),
  isActive: z
    .object({
      is: z.boolean().optional(),
      isNot: z.boolean().optional(),
    })
    .optional(),
  profile: z
    .object({
      bio: z
        .object({
          contains: z.string().optional(),
        })
        .optional(),
    })
    .optional(),
});

// Validate filter
const validateFilter = (filter: unknown) => {
  return FilterSchema.parse(filter);
};

API Reference

QueryFilter

type QueryFilter<T> = {
  and?: QueryFilter<T>[];
  or?: QueryFilter<T>[];
  [K in keyof T]?: FilterFieldComparison<T[K]>;
};

FilterFieldComparison

type FilterFieldComparison<FieldType> = {
  // Common operators
  eq?: FieldType;
  neq?: FieldType;
  gt?: FieldType;
  gte?: FieldType;
  lt?: FieldType;
  lte?: FieldType;
  in?: FieldType[];
  notIn?: FieldType[];
  is?: FieldType | null;
  isNot?: FieldType | null;

  // String specific operators
  contains?: string;
  startsWith?: string;
  endsWith?: string;
  mode?: 'default' | 'insensitive';
};

Filter Operators

Common Operators

  • eq: Equal to
  • neq: Not equal to
  • gt: Greater than
  • gte: Greater than or equal to
  • lt: Less than
  • lte: Less than or equal to
  • in: In array
  • notIn: Not in array
  • is: Is (for boolean and null)
  • isNot: Is not (for boolean and null)

String Operators

  • contains: Contains substring
  • startsWith: Starts with
  • endsWith: Ends with
  • mode: Case sensitivity ('default' or 'insensitive')

Contributing

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

1.1.1

7 months ago

1.1.0

12 months ago

1.1.6

6 months ago

1.1.5

6 months ago

1.1.4

6 months ago

1.1.3

6 months ago

1.1.2

7 months ago

2.0.2

5 months ago

2.0.1

5 months ago

2.0.0

6 months ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.2

2 years ago

1.0.3

2 years ago

1.0.1

2 years ago