August release - 2020

In the last few weeks, we have fixed a handful of issues and also shipped with some new features and improvements. This blog post summarizes the highlights of the release.

ORM helpers to work with relationships

In the true spirit of making it easier to work with Model relationships, we shipped new methods to count related rows , filter by relationships and apply group limit during preload.

Imagining, you have a blog with categories, posts and comments. Following are the code examples for some of the known use cases.

Get categories list with counts of posts inside them

Many popular blogs shows the count of posts for a category or a tag.

You can achieve the similar results using the following query.

const categories = await Category
.query()
.withCount('posts')
categories.forEach((category) => {
console.log(category.$extras.posts_count)
})

Check for relationship existence

Another frequent use case is to limit the number of parent model records based upon the existence of its relationships.

For example: Show all posts that has received one or more comments.

const posts = await Post
.query()
.has('comments')

The has method has more variants. We recommend reading the docs for same.

Preloading group limit

The groupLimit method uses SQL window functions to limit the number of rows for preloaded relationships.

Continuing with the blog categories and the posts example. Lets fetch all categories, along with the latest 3 posts in each category.

const categories = await Category
.query()
.preload('posts', (posts) => {
posts
.groupOrderBy('posts.created_at', 'desc')
.groupLimit(3) // 👈
})

The groupLimit is not similar to just applying the limit clause on the query. The regular limit clause will fetch a total of 3 posts across all the categories. Whereas, we want 3 post from each category.

New validator rules

The validator has received a bunch of new validation rules related to date-time validation.

after and before rules

The after and the before rules allows you to enforce a date to be after/before a specified date time or offset. Example:

{
checkin_date: schema.date({}, [
rules.after(4, 'days')
])
}

You can also use the today and tomorrow keywords with the after rule.

{
checkin_date: schema.date({}, [
rules.after('today')
])
}

Similarly, the before rule enforces the date to be before the specified date or offset.

afterField and beforeField rules

Another variant is to compare the date with the value of an existing field. This is super helpful for forms with before and after date columns.

{
checkin_date: schema.date({}, [
rules.after('today')
]),
checkout_date: schema.date({}, [
rules.afterField('checkin_date')
])
}

blacklist

The blacklist rule dis-allows certain values. It is the opposite of the enum schema type. A practical use case is to blacklist certain usernames.

{
username: schema.string({}, [
rules.blacklist([
'super',
'admin',
'root',
'bot',
'hacker'
])
])
}

Support for Basic Auth

We have also added the another auth driver that uses the HTTP basic auth for authenticating requests. Using it is as simple as dropping the auth middleware on a route.

start/routes.ts
Route
.get('posts', async ({ auth }) => {
return `You are logged in as ${auth.user!.email}`
})
.middleware('auth:basic')

Trap events

One of the primary goals of AdonisJS is to make it easier for you to test your applications. That's why along with the option of trapping emails, we now also allow trapping emitter events.

import User from 'App/Models/User'
import Event from '@ioc:Adonis/Core/Event'
Event.trap('new:user', (user) => {
assert.instanceOf(user, User)
})

To trap all the events, you can make use of the trapAll method.

Event.trapAll((event, data) => {
})

Once done with the test, you can call the restore method to dispose traps.

Event.restore()