Skip to the content.

QueryKit

RubyMine

Gem Version Documentation License: MIT

A fluent, intuitive query builder and micro-ORM for Ruby inspired by .NET’’s SqlKata. Perfect for projects where Active Record feels like overkill.

Features

Installation


gem install querykit

Then your preferred database driver.

# SQLite
gem install sqlite3

# PostgreSQL
gem install pg

# MySQL
gem install mysql2

Quick Start

See demo.rb for more extensive examples.

require 'querykit'

# Configure once
QueryKit.setup(:sqlite, database: 'app.db')

# Query builder
users = QueryKit.connection.get(
  QueryKit.connection.query('users')
    .where('age', '>', 18)
    .order_by('name')
    .limit(10)
)

# Repository pattern (query scoping)
class UserRepository < QueryKit::Repository
  table 'users'
  model User
end

repo = UserRepository.new
user = repo.find(1)
users = repo.where('age', '>', 18)

Documentation

Full documentation site: https://kiebor81.github.io/querykit

API documentation (YARD): Generate locally with rake doc

Why QueryKit?

vs Active Record: Much lighter, no DSL, no magic. Just build queries and execute them.

vs Sequel: Simpler API, fewer features by design. If you need a full ORM, use Sequel.

vs Raw SQL: Type-safe, composable queries with protection against SQL injection.

Security

QueryKit uses parameterized queries by default, protecting against SQL injection when used correctly:

# SAFE - Values are automatically parameterized
db.query('users').where('email', user_input)

# UNSAFE - Never interpolate user input
db.raw("SELECT * FROM users WHERE email = '#{user_input}'")

# SAFE - Use placeholders with raw SQL
db.raw('SELECT * FROM users WHERE email = ?', user_input)

See Security Best Practices for detailed guidance.

Philosophy

Extensions

QueryKit supports optional extensions that add advanced features without bloating the core:

require 'querykit/extensions/case_when'

# Load extensions at startup
QueryKit.use_extensions(QueryKit::CaseWhenExtension)

# Or load multiple extensions
QueryKit.use_extensions([Extension1, Extension2])

Available extensions:

Extensions use Ruby’s prepend to cleanly override methods without monkey-patching.

What QueryKit Doesn’t Do

These features are intentionally excluded to maintain simplicity:

For advanced SQL features, use raw SQL:

# Common Table Expressions (CTEs)
db.raw(<<~SQL, user_id)
  WITH ranked_orders AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as rn
    FROM orders
  )
  SELECT * FROM ranked_orders WHERE rn = 1 AND user_id = ?
SQL

# Window Functions
db.raw('SELECT *, AVG(salary) OVER (PARTITION BY department) as dept_avg FROM employees')

# Upsert (SQLite)
db.raw('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET name=excluded.name', id, name)

Contributing

See CONTRIBUTING.md for guidelines.

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.

Acknowledgments

Inspired by SqlKata (.NET) and Arel (Ruby).

License

This is a personal project, but suggestions and bug reports are welcome via issues.

MIT License - see LICENSE for details.