const sql = 'SELECT * FROM `users`'
if (username) {
sql += ' WHERE `username` = ' + username
}
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
The AdonisJs Query Builder provides a unified syntax to interact with SQL databases using JavaScript methods.
This guide is a reference to all the available methods on the Query Builder.
See the database Getting Started guide for the list of supported databases, config options and how to debug your SQL queries. |
Writing SQL queries can be tedious, even if you are proficient with SQL.
Imagine all your queries are written for MySQL, and at a later time you’re asked to migrate everything to PostgreSQL. You’d have to rewrite/amend your MySQL queries to ensure they still work well with PostgreSQL.
Query Builder abstracts away connection specific syntax so you’re free to concentrate on your app functionality instead of variations in SQL dialects.
Another issue can be building incremental queries with conditional blocks:
const sql = 'SELECT * FROM `users`'
if (username) {
sql += ' WHERE `username` = ' + username
}
const query = Database.table('users')
if (username) {
query.where('username', username)
}
Here’s a basic example using the Query Builder to chain different methods:
const Database = use('Database')
class UserController {
async index (request, response) {
return await Database
.table('users')
.where('username', 'john')
.first()
}
}
The select
method defines the fields to be selected for a given query:
await Database.select('id', 'username').from('users')
// or
await Database.select('*').from('users')
select `id`, `username` from `users`
select * from `users`
You can define query aliases like so:
await Database.select('username as uname')
Query Builder offers numerous dynamic methods to add where clauses.
It also supports subqueries by passing a closure or another query instead of the actual value.
For detailed where
information, see Knex’s documentation.
Passing undefined to the where clause causes an error during SQL compilation, so ensure dynamic values are not undefined before passing them.
|
const users = await Database.from('users').where('id', 1)
// Or
const users = await Database.from('users').where({ id: 1 })
You can pass a comparison operator to the where
clause like so:
const adults = await Database
.from('users')
.where('age', '>', 18)
You can pass a callback to the where
clause to group all clauses contained withing the callback:
await Database.from('users').where(function () {
this
.where('id', 1)
.orWhere('id', '>', 10)
})
select * from `users` where (`id` = 1 or `id` > 10)
await Database
.from('users')
.whereNot('age', '>', 15)
// or
await Database
.from('users')
.whereNot({username: 'foo'})
await Database
.from('users')
.whereIn('id', [1,2,3])
await Database
.from('users')
.whereNotIn('id', [1,2,3])
await Database
.from('users')
.whereNull('deleted_at')
await Database
.from('users')
.whereNotNull('created_at')
await Database.from('users').whereExists(function () {
this.from('accounts').where('users.id', 'accounts.user_id')
})
await Database.from('users').whereNotExists(function () {
this.from('accounts').where('users.id', 'accounts.user_id')
})
await Database
.table('users')
.whereBetween('age', [18, 32])
await Database
.table('users')
.whereNotBetween('age', [45, 60])
Convenience helper for .where(Database.raw(query))
:
await Database
.from('users')
.whereRaw('id = ?', [20])
await Database
.table('users')
.innerJoin('accounts', 'users.id', 'accounts.user_id')
You can also pass a callback to construct the join:
await Database
.table('users')
.innerJoin('accounts', function () {
this
.on('users.id', 'accounts.user_id')
.orOn('users.id', 'accounts.owner_id')
})
Database
.select('*')
.from('users')
.leftJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.leftOuterJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.rightJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.rightOuterJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.outerJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.fullOuterJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('users')
.crossJoin('accounts', 'users.id', 'accounts.user_id')
await Database
.select('*')
.from('accounts')
.joinRaw('natural full join table1').where('id', 1)
await Database
.table('users')
.distinct('age')
await Database
.table('users')
.groupBy('age')
await Database
.table('users')
.groupByRaw('age, status')
await Database
.table('users')
.orderBy('id', 'desc')
await Database
.table('users')
.orderByRaw('col NULLS LAST DESC')
groupBy() must be called before having() .
|
await Database
.table('users')
.groupBy('age')
.having('age', '>', 18)
await Database
.table('users')
.offset(11)
.limit(10)
The insert
operation creates a row and returns its newly created id
:
const userId = await Database
.table('users')
.insert({username: 'foo', ...})
In the case of bulk inserts, the id
of the first record is returned (this is a limitation with MySQL itself; see LAST_INSERT_ID):
// BULK INSERT
const firstUserId = await Database
.from('users')
.insert([{username: 'foo'}, {username: 'bar'}])
The into
method is a more readable alternative than using table/from
when inserting database rows:
const userId = await Database
.insert({username: 'foo', ...})
.into('users')
For PostgreSQL, you have to define the returning column explicitly (all other database clients ignore this statement):
const userId = await Database
.insert({ username: 'virk' })
.into('users')
.returning('id')
All update operations return the number of affected rows:
const affectedRows = await Database
.table('users')
.where('username', 'tutlage')
.update('lastname', 'Virk')
To update multiple columns, pass those columns/values as an object:
const affectedRows = await Database
.table('users')
.where('username', 'tutlage')
.update({ lastname: 'Virk', firstname: 'Aman' })
Delete operations also return the number of affected rows:
const affectedRows = await Database
.table('users')
.where('username', 'tutlage')
.delete()
As delete is reserved a reserved keyword in JavaScript, you can also use the alternative del() method.
|
Truncate removes all table rows, resetting the table auto increment id to 0
:
await Database.truncate('users')
Query Builder provides convenient methods to paginate database results.
const users = await Database
.from('users')
.forPage(1, 10)
const results = await Database
.from('users')
.paginate(2, 10)
The output of the paginate method is different from the forPage method.
|
{
total: '',
perPage: '',
lastPage: '',
page: '',
data: [{...}]
}
If using PostgreSQL, the total key will be a string since JavaScript is unable to handle bigint natively (see this issue for a recommended solution).
|
Database transactions are safe operations which are not reflected in the database until you explicitly commit your changes.
The beginTransaction
method returns the transaction object, which can be used to perform any queries:
const trx = await Database.beginTransaction()
await trx.insert({username: 'virk'}).into('users')
await trx.commit() // insert query will take place on commit
await trx.rollback() // will not insert anything
You can also wrap your transactions inside a callback:
await Database.transaction(async (trx) => {
await trx.insert({username: 'virk'}).into('users')
})
You do not have to call commit or rollback manually inside this callback.
|
If any of your queries throws an error, the transaction rolls back automatically, otherwise, it is committed.
Query Builder exposes the full power of Knex’s aggregate methods.
const count = await Database
.from('users')
.count() // returns array
const total = count[0]['count(*)'] // returns number
// COUNT A COLUMN
const count = await Database
.from('users')
.count('id') // returns array
const total = count[0]['count("id")'] // returns number
// COUNT COLUMN AS NAME
const count = await Database
.from('users')
.count('* as total') // returns array
const total = count[0].total // returns number
countDistinct
is the same as count
, but adds a distinct
expression:
const count = await Database
.from('users')
.countDistinct('id') // returns array
const total = count[0]['count(distinct "id")'] // returns number
await Database.from('users').min('age') // returns array
await Database.from('users').min('age as a') // returns array
await Database.from('users').max('age') // returns array
await Database.from('users').max('age as a') // returns array
await Database.from('cart').sum('total') // returns array
await Database.from('cart').sum('total as t') // returns array
await Database.from('cart').sumDistinct('total') // returns array
await Database.from('cart').sumDistinct('total as t') // returns array
await Database.from('users').avg('age') // returns array
await Database.from('users').avg('age as age') // returns array
await Database.from('users').avgDistinct('age') // returns array
await Database.from('users').avgDistinct('age as age') // returns array
Increase the column value by 1
:
await Database
.table('credits')
.where('id', 1)
.increment('balance', 10)
Decrease the column value by 1
:
await Database
.table('credits')
.where('id', 1)
.decrement('balance', 10)
The AdonisJs Query Builder also extends Knex’s query aggregates with helpful shortcut methods for common aggregate queries. These helper methods end the query builder chain and return a value.
All helpers accept a column name to be used for aggregation. When possible, Query Builder will choose a default for the column name.
Some methods, such as sum()
, require a column name.
The underlying Knex query builder defines the methods: count()
, countDistinct()
, avg()
, avgDistinct()
, sum()
, sumDistinct()
, min()
, and max()
. To avoid confusion and naming collisions, Query Builder prefixes its aggregate helper methods with get
(e.g. getCount
).
const total = await Database
.from('users')
.getCount() // returns number
const total = await Database
.from('users')
.getCountDistinct('id') // returns number
await Database.from('users').getMin('age') // returns a number
await Database.from('users').getMax('age') // returns number
await Database.from('cart').getSum('total') // returns number
await Database.from('cart').getSumDistinct('total') // returns number
await Database.from('users').getAvg('age') // returns number
await Database.from('users').getAvgDistinct('age') // returns number
The pluck
method will return an array of values for the selected column:
const usersIds = await Database.from('users').pluck('id')
Clones the current query chain for later usage:
const query = Database
.from('users')
.where('username', 'virk')
.clone()
// later
await query
Returns information for a given column:
const username = await Database
.table('users')
.columnInfo('username')
const subquery = Database
.from('accounts')
.where('account_name', 'somename')
.select('account_name')
const users = await Database
.from('users')
.whereIn('id', subquery)
select * from `users` where `id` in (select `account_name` from `accounts` where `account_name` = 'somename')
The Database.raw
method should be used for running raw SQL queries:
await Database
.raw('select * from users where username = ?', [username])
Database connections can be closed by calling the close
method. By default, this method closes all open database connections.
To close selected connections, pass an array of connection names:
Database.close() // all
Database.close(['sqlite', 'mysql'])