{"id":41237489,"url":"https://github.com/gblikas/querykit","last_synced_at":"2026-01-23T01:09:07.899Z","repository":{"id":311719228,"uuid":"975806892","full_name":"gblikas/querykit","owner":"gblikas","description":"Search like you do in Github.","archived":false,"fork":false,"pushed_at":"2025-09-28T05:19:07.000Z","size":346,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-28T07:14:21.090Z","etag":null,"topics":["ai","database","nextjs","npm","npm-package","postgres","search","vercel"],"latest_commit_sha":null,"homepage":"https://www.querykit.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gblikas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-30T23:50:37.000Z","updated_at":"2025-09-15T22:34:07.000Z","dependencies_parsed_at":"2025-08-26T07:19:50.047Z","dependency_job_id":"0af24756-25a2-4c0a-ad78-e4e8d89fdf74","html_url":"https://github.com/gblikas/querykit","commit_stats":null,"previous_names":["gblikas/querykit"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/gblikas/querykit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gblikas%2Fquerykit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gblikas%2Fquerykit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gblikas%2Fquerykit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gblikas%2Fquerykit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gblikas","download_url":"https://codeload.github.com/gblikas/querykit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gblikas%2Fquerykit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28676632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T01:00:35.747Z","status":"ssl_error","status_checked_at":"2026-01-23T01:00:19.529Z","response_time":144,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","database","nextjs","npm","npm-package","postgres","search","vercel"],"created_at":"2026-01-23T01:09:06.179Z","updated_at":"2026-01-23T01:09:07.873Z","avatar_url":"https://github.com/gblikas.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# QueryKit\n\nQueryKit is a modern query toolkit for Lucene-style search, designed to give developers a head-start for dynamic search. QueryKit simplifies how you build and execute fielded searchs across different databases and ORMs. It provides a unified, intuitive SDK for filtering, sorting, and transforming data, and handles the heavy lifting of parsing and translating those queries to your data source. \n\n## What Does QueryKit Do?\n\nQueryKit allows developers to define queries in a high-level, readable format and then run those queries anywhere (in the browser, on the server, or via CLI). Instead of writing different query logic for each layer of your stack, you can use QueryKit's consistent API to:\n\n- **Filter and sort data with a Lucene-like query language** – For example, `status:done AND in:todos`. \n\n- **Translate high-level queries to concrete implementations** – QueryKit takes your schema definition and converts it into the appropriate form for the environment. \n\n- **Unify front‑end and back‑end filtering logic** – The same QueryKit query can be run in a front-end app for client-side filtering or on a server for database queries. This ensures consistency in how data is filtered across your application.\n\n## Quick Start\n\nBelow are various examples of how to use QueryKit.\n\n**Drizzle ORM**\n\n```typescript\n\n// schema.ts\nimport { serial, text, pgTable } from 'drizzle-orm/pg-core';\nimport { type InferSelectModel, type InferInsertModel } from 'drizzle-orm'\n\nconst users = pgTable('users', {\n  id: serial('id').primaryKey(),\n  name: text('name').notNull(),\n  status: text('status').notNull().default('draft')\n});\n\ntype SelectUser = InferSelectModel\u003ctypeof users\u003e;\n\n// example.ts\nimport { SelectUser } from './schema';\nimport { createQueryKit } from 'querykit';\nimport { drizzleAdapter } from 'querykit/adapters/drizzle';\n\n// Create a QueryKit instance with Drizzle adapter\nconst qk = createQueryKit({\n  adapter: drizzleAdapter,\n  schema: { users },\n});\n\n// Build a query using the Lucene-like query syntax\nconst query = qk.query('users')\n  .where('status:done AND name:\"John *\"')\n  .orderBy('name', 'asc')\n  .limit(10);\n\n// Execute the query against your database\nconst results = await query.execute();\n```\n\n## Query Syntax\n\nQueryKit uses Liqe Query Language (LQL), which is a Lucene-like syntax for filtering data:\n\n```\n# Basic field queries\nstatus:active         # Field equals value (case insensitive)\nstatus:\"Active\"       # Field equals value (case sensitive)\n\n# Comparison operators\npriority:\u003e2               # Greater than\npriority:\u003e=2              # Greater than or equal\npriority:\u003c2               # Less than\npriority:\u003c=2              # Less than or equal\npriority:=2               # Exact equals\n\n# Ranges\ndueDate:[2023-01-01 TO 2023-01-31]      # Inclusive range\ndueDate:{2023-01-01 TO 2023-01-31}      # Exclusive range\n\n# Pattern matching\nname:/Todo.*/         # Regular expression\ntitle:intro*          # Wildcard matching\n\n# Boolean operators\nstatus:active AND priority:\u003e2     # AND operator\nstatus:active OR status:pending   # OR operator\nNOT expired:true      # NOT operator\n-expired:true         # Alternative NOT syntax\n\n# Grouping\n(status:active OR status:pending) AND priority:\u003c3\n\n# Implicit AND (space between expressions)\nstatus:active priority:\u003e2\n```\n\n## Installation\n\n```bash\nnpm install querykit\n```\n\n## Dependencies\n\nQueryKit leverages several key dependencies to provide its functionality:\n\n- [**Liqe**](https://github.com/gajus/liqe): A lightweight and performant Lucene-like parser, serializer, and search engine. QueryKit uses Liqe for parsing the query syntax into an abstract syntax tree (AST) that can then be translated to various target languages.\n\n- [**Drizzle ORM**](https://github.com/drizzle-team/drizzle-orm): A TypeScript ORM that's used for SQL database interactions. QueryKit's Drizzle adapter translates queries into Drizzle ORM queries.\n\nAdditional dependencies will be added as the project evolves.\n\n## Security Features\n\nQueryKit provides configurable security guardrails to protect your database from potentially harmful queries while maintaining flexibility. These features can be configured during initialization:\n\n\u003e **IMPORTANT DISCLAIMER**: While QueryKit provides guardrails to help protect against common query-related vulnerabilities, it is not a comprehensive security solution. These features are provided as helpful tools, not guarantees. You are still responsible for implementing proper authentication, authorization, and other security measures in your application. QueryKit does not guarantee protection against all forms of database attacks or query exploits.\n\n```typescript\nimport { createQueryKit } from 'querykit';\nimport { drizzleAdapter } from 'querykit/adapters/drizzle';\n\nconst qk = createQueryKit({\n  adapter: drizzleAdapter,\n  schema: { users },\n  security: {\n    // Field restrictions\n    allowedFields: ['name', 'email', 'priority', 'status'], // Only these fields can be queried\n    denyFields: ['password', 'secretKey'],            // These fields can never be queried\n    \n    // Value restrictions - deny specific values for fields\n    denyValues: {\n      status: ['deleted', 'banned'],      // Block queries for deleted/banned records\n      role: ['superadmin', 'system'],     // Prevent querying privileged roles\n      'user.type': ['internal', 'bot']    // Supports dot-notation for nested fields\n    },\n    \n    // Field name restrictions\n    allowDotNotation: true,    // Set to false to block \"table.field\" or \"json.path\" queries\n    \n    // Query complexity limits\n    maxQueryDepth: 5,          // Maximum nesting level of expressions\n    maxClauseCount: 20,        // Maximum number of clauses (AND/OR operations)\n    \n    // Resource protection\n    defaultLimit: 100,         // Default result limit if none specified\n    maxLimit: 1000,            // Maximum allowed limit for pagination\n    \n    // Value sanitization\n    maxValueLength: 100,       // Maximum string length for query values\n    sanitizeWildcards: true,   // Prevent regex DoS with wildcards in LIKE queries\n    \n    // Performance safeguards\n    queryTimeout: 5000,        // Timeout in milliseconds for query execution\n  }\n});\n```\n\nBy default, QueryKit applies sensible security defaults even without explicit configuration:\n\n```typescript\n// Default security configuration\nconst DEFAULT_SECURITY = {\n  // Field restrictions - by default, all schema fields are allowed\n  allowedFields: [],           // Empty means \"use schema fields\"\n  denyFields: [],              // Empty means no denied fields\n  denyValues: {},              // Empty means no denied values for any field\n  allowDotNotation: true,      // Allow \"table.field\" and \"json.path\" notation\n  \n  // Query complexity limits\n  maxQueryDepth: 10,           // Maximum nesting level of expressions\n  maxClauseCount: 50,          // Maximum number of clauses (AND/OR operations)\n  \n  // Resource protection\n  defaultLimit: 100,           // Default result limit if none specified\n  maxLimit: 1000,              // Maximum allowed limit for pagination\n  \n  // Value sanitization\n  maxValueLength: 1000,        // Maximum string length for query values\n  sanitizeWildcards: true,     // Prevent regex DoS with wildcards in LIKE queries\n  \n  // Performance safeguards\n  queryTimeout: 30000,         // 30 second timeout by default\n}\n```\n\nSecurity configurations can be stored in a separate file and imported:\n\n```typescript\n// security-config.json\n{\n  \"allowedFields\": [\"name\", \"email\", \"priority\", \"status\"],\n  \"denyValues\": {\n    \"status\": [\"deleted\", \"banned\"],\n    \"role\": [\"superadmin\", \"system\"]\n  },\n  \"maxQueryDepth\": 5,\n  \"maxClauseCount\": 20,\n  \"defaultLimit\": 100\n}\n\n// In your app\nimport securityConfig from './security-config.json';\n\nconst qk = createQueryKit({\n  adapter: drizzleAdapter,\n  schema: { users },\n  security: securityConfig\n});\n```\n\n### Additional Security Recommendations\n\nWhen using QueryKit in production, consider these additional security practices:\n\n1. **Implement Authentication and Authorization**: QueryKit doesn't handle auth - integrate with your existing auth system.\n2. **Use Rate Limiting**: Limit the number of queries a user can make in a given time period.\n3. **Audit Logging**: Log all queries for security monitoring and debugging.\n4. **Field-Level Access Control**: Use dynamic allowedFields based on user roles/permissions.\n5. **Separate Query Context**: Consider separate QueryKit instances with different security settings for different contexts (admin vs. user).\n\n### Controlling Dot Notation in Field Names\n\nQueryKit supports dot notation in field names (e.g., `user.name`, `metadata.tags`) which is useful for:\n\n- **Table-qualified columns**: When joining tables with overlapping column names (`users.id` vs `orders.id`)\n- **JSON/JSONB fields**: Querying nested data in PostgreSQL JSON columns (`metadata.dimensions.width`)\n- **Related data**: Accessing data through ORM relations (`order.customer.name`)\n\nHowever, you may want to **disable dot notation** for public-facing APIs:\n\n```typescript\nconst qk = createQueryKit({\n  adapter: drizzleAdapter,\n  schema: { products },\n  security: {\n    allowDotNotation: false,  // Reject queries like \"user.password\" or \"config.secret\"\n    allowedFields: ['name', 'price', 'category', 'inStock']\n  }\n});\n\n// ✅ Allowed: Simple field names\nqk.query('products').where('name:\"Widget\" AND price:\u003c100');\n\n// ❌ Rejected: Dot notation\nqk.query('products').where('user.password:\"secret\"');\n// Error: Dot notation is not allowed in field names. Found \"user.password\" - use a simple field name without dots instead.\n```\n\n**When to disable dot notation:**\n\n| Scenario | Recommendation |\n|----------|---------------|\n| Public search API | Disable - prevents probing internal table structures |\n| Admin dashboard | Enable - admins may need cross-table queries |\n| Simple flat schema | Disable - simplifies security model |\n| JSON/JSONB columns | Enable - needed for nested data access |\n| Multi-tenant app | Disable - prevents `tenant.secret` style access |\n\n**Concrete example - Public e-commerce search:**\n\n```typescript\n// For a public product search endpoint, disable dot notation\n// to prevent users from attempting queries like:\n// - \"orders.creditCard\" (accessing other tables)\n// - \"internal.costPrice\" (accessing internal JSON fields)\n// - \"admin.notes\" (accessing admin-only data)\n\nconst publicSearchKit = createQueryKit({\n  adapter: drizzleAdapter,\n  schema: { products },\n  security: {\n    allowDotNotation: false,\n    allowedFields: ['name', 'description', 'price', 'category'],\n    denyValues: {\n      category: ['internal', 'discontinued']\n    }\n  }\n});\n```\n\n## Roadmap\n\n### Core Parsing Engine and DSL\n- [x] Implement Lucene-style query syntax parser using Liqe\n- [x] Create type-safe query building API\n- [x] Develop internal AST representation\n- [x] Implement consistent syntax for logical operators (AND, OR, NOT)\n- [x] Support standard comparison operators (==, !=, \u003e, \u003e=, \u003c, \u003c=)\n\n### First Adapters\n- [x] Drizzle ORM integration\n- [x] Implement SQL translation layer\n- [ ] In-memory JavaScript filtering\n- [x] Query validation and error handling\n- [x] Support for schema-aware queries\n\n### Advanced Features\n- [ ] CLI tools for testing and debugging\n- [x] Performance optimizations for SQL generation\n- [x] Support for complex nested expressions\n- [ ] Custom function support\n- [ ] Pagination helpers\n\n### Ecosystem Expansion\n- [ ] Frontend query builder components\n- [ ] Additional ORM adapters\n- [ ] Server middleware for Express/Fastify\n- [ ] TypeScript SDK generation\n\n## Contributing\n\nSee the [CONTRIBUTING.md](CONTRIBUTING.md) file for details on how to get started.\n\n## License\n\nThis project is licensed under the GPL License - see the [LICENSE](LICENSE) file for details. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgblikas%2Fquerykit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgblikas%2Fquerykit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgblikas%2Fquerykit/lists"}