Sqler
Sqler is the SQL query builder used internally by Finch for MySQL and SQLite operations. It provides a fluent Dart API that generates safe, parameterized SQL queries without string concatenation.
Why use Sqler instead of raw SQL strings?
- User input is always wrapped in
QVar(), which ensures proper escaping and prevents SQL injection. - Queries are Dart objects — they can be composed, reused, and tested independently.
- The same query code works for both MySQL and SQLite; only the
toSQL()type parameter changes.
Sqler is available as a standalone package at pub.dev/packages/sqler.
Features
- Fluent Interface: Build SQL queries using method chaining for better readability
- Type-Safe: Construct queries with compile-time safety
- Comprehensive Support: SELECT, INSERT, UPDATE, DELETE operations
- Advanced Clauses: WHERE, JOIN, GROUP BY, HAVING, ORDER BY, LIMIT
- Parameterized Queries: Support for named parameters to prevent SQL injection
- Proper Escaping: Automatic field quoting and value escaping
- Complex Operations: Subqueries, CASE statements, aggregate functions
- MySQL Optimized: Specifically designed for MySQL syntax and features
- SQLite Optimized: Specifically designed for SQLite syntax and features
Installation
Add this to your package's pubspec.yaml file:
dependencies:
sqler:
Or
dart pub add sqler
Then run:
dart pub get
Quick Start
import 'package:sqler/sqler.dart';
void main() {
// Create Table
var books = MTable(
name: 'books',
fields: [
MFieldInt(name: 'id', isPrimaryKey: true, isAutoIncrement: true),
MFieldVarchar(name: 'name', length: 255),
MFieldVarchar(name: 'author', length: 255),
MFieldInt(name: 'publication_year'),
MFieldDate(name: 'published_date'),
MFieldText(name: 'content'),
],
);
// Simple SELECT query
var query = Sqler()
.addSelect(QSelect('name'))
.addSelect(QSelect('published_date'))
.from(QField('books'))
.where(WhereOne(QField('publication_year'), QO.EQ, QVar(1980)))
.orderBy(QOrder('name'))
.limit(10);
print(query.toSQL());
/// Print for SQLite:
print(query.toSQL<Sqlite>());
}
SQLite
To generate a SQL query specifically in SQLite format, pass Sqlite as a type parameter to the toSQL function:
query.toSQL<Sqlite>()
This ensures that the SQL syntax produced is compatible with SQLite.
Core Classes
Sqler
The main query builder class that provides a fluent interface for constructing SQL queries.
QField
Represents database fields with proper quoting:
QField('users.name') // users.`name`
QField('table_name') // `table_name`
QVar
Represents values with proper escaping:
QVar('string value') // 'string value'
QVar(123) // 123
QVar(true) // true
QVar(null) // NULL
QSelect
Represents SELECT fields with optional aliases:
QSelect('name') // `name`
QSelect('users.name', 'user_name') // users.`name` AS `user_name`
Usage Examples
Basic SELECT Query
var query = Sqler()
.addSelect(QSelect('id'))
.addSelect(QSelect('name'))
.addSelect(QSelect('email'))
.from(QField('users'));
print(query.toSQL());
// SELECT `id`, `name`, `email` FROM `users`
SELECT with WHERE Conditions
var query = Sqler()
.addSelect(QSelect('*'))
.from(QField('users'))
.where(AndWhere([
WhereOne(QField('active'), QO.EQ, QVar(true)),
WhereOne(QField('age'), QO.GT, QVar(18))
]));
print(query.toSQL());
// SELECT * FROM `users` WHERE ( ( `active` = true ) ) AND ( ( `age` > 18 ) )
SELECT with JOINs
var query = Sqler()
.addSelect(QSelect('users.name'))
.addSelect(QSelect('profiles.bio'))
.from(QField('users'))
.join(LeftJoin('profiles', On([
Condition(QField('users.id'), QO.EQ, QField('profiles.user_id'))
])))
.where(WhereOne(QField('users.active'), QO.EQ, QVar(true)));
print(query.toSQL());
// SELECT `users`.`name`, `profiles`.`bio` FROM `users`
// LEFT JOIN `profiles` ON ( ( `users`.`id` = `profiles`.`user_id` ) )
// WHERE ( `users`.`active` = true )
INSERT Operations
// Single record insert
var query = Sqler()
.insert(QField('users'), [
{
'name': QVar('John Doe'),
'email': QVar('[email protected]'),
'active': QVar(true),
'age': QVar(30)
}
]);
print(query.toSQL());
// INSERT INTO `users` (`name`, `email`, `active`, `age`)
// VALUES ('John Doe', '[email protected]', true, 30)
// Multiple records insert
var query = Sqler()
.insert(QField('users'), [
{
'name': QVar('John Doe'),
'email': QVar('[email protected]')
},
{
'name': QVar('Jane Smith'),
'email': QVar('[email protected]')
}
]);
print(query.toSQL());
// INSERT INTO `users` (`name`, `email`)
// VALUES ('John Doe', '[email protected]'), ('Jane Smith', '[email protected]')
UPDATE Operations
var query = Sqler()
.update(QField('users'), {
'name': QVar('Updated Name'),
'email': QVar('[email protected]')
})
.where(WhereOne(QField('id'), QO.EQ, QVar(1)));
print(query.toSQL());
// UPDATE `users` SET `name` = 'Updated Name', `email` = '[email protected]'
// WHERE ( `id` = 1 )
DELETE Operations
var query = Sqler()
.delete()
.from(QField('users'))
.where(WhereOne(QField('active'), QO.EQ, QVar(false)));
print(query.toSQL());
// DELETE FROM `users` WHERE ( `active` = false )
Complex Queries with GROUP BY and HAVING
var query = Sqler()
.addSelect(QSelect('department'))
.addSelect(QSelectFunc('COUNT', [QField('id')], 'employee_count'))
.from(QField('employees'))
.where(WhereOne(QField('active'), QO.EQ, QVar(true)))
.groupBy(QField('department'))
.having(HavingOne(QSelectFunc('COUNT', [QField('id')]), QO.GT, QVar(5)))
.orderBy(QOrder('employee_count', OrderDirection.DESC));
print(query.toSQL());
// SELECT `department`, COUNT(`id`) AS `employee_count` FROM `employees`
// WHERE ( `active` = true ) GROUP BY `department`
// HAVING ( COUNT(`id`) > 5 ) ORDER BY `employee_count` DESC
Parameterized Queries
var query = Sqler()
.addSelect(QSelect('*'))
.from(QField('users'))
.where(WhereOne(QField('name'), QO.EQ, QParam('user_name')))
.param('user_name', QVar('John Doe'));
print(query.toSQL());
// SELECT * FROM `users` WHERE ( `name` = :user_name )
var params = query.getParams();
// {'user_name': QVar('John Doe')}
Operators
The QO class provides various comparison operators:
QO.EQ- Equal (=)QO.NE- Not Equal (!=)QO.GT- Greater Than (>)QO.GTE- Greater Than or Equal (>=)QO.LT- Less Than (<)QO.LTE- Less Than or Equal (<=)QO.LIKE- LIKE pattern matchingQO.NOT_LIKE- NOT LIKE pattern matchingQO.IN- IN listQO.NOT_IN- NOT IN listQO.IS_NULL- IS NULLQO.IS_NOT_NULL- IS NOT NULL
WHERE Conditions
Basic Conditions
WhereOne(QField('status'), QO.EQ, QVar('active'))
Combined Conditions
AndWhere([
WhereOne(QField('active'), QO.EQ, QVar(true)),
WhereOne(QField('age'), QO.GTE, QVar(18))
])
OrWhere([
WhereOne(QField('role'), QO.EQ, QVar('admin')),
WhereOne(QField('role'), QO.EQ, QVar('moderator'))
])
JOIN Types
InnerJoin- INNER JOINLeftJoin- LEFT JOINRightJoin- RIGHT JOIN
.join(InnerJoin('table2', On([
Condition(QField('table1.id'), QO.EQ, QField('table2.table1_id'))
])))
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License.