Helpers

AdonisJS bundles the utilities used by the framework or the ecosystem packages into a Helpers module and makes them available to your application code.

Since these utilities are already installed and used by the framework, the helpers module does not add any additional bloat to your node_modules.

String helpers

The string helpers expose the following transformation methods.

camelCase

Convert a string to its camelCase version.

import { string } from '@ioc:Adonis/Core/Helpers'
string.camelCase('hello-world') // helloWorld

snakeCase

Convert a string to its snake_case version.

import { string } from '@ioc:Adonis/Core/Helpers'
string.snakeCase('helloWorld') // hello_world

dashCase

Convert a string to its dash-case version. Optionally, you can also capitalize the first letter of each segment.

import { string } from '@ioc:Adonis/Core/Helpers'
string.dashCase('helloWorld') // hello-world
string.dashCase('helloWorld', { capitalize: true }) // Hello-World

pascalCase

Convert a string to its PascalCase version.

import { string } from '@ioc:Adonis/Core/Helpers'
string.pascalCase('helloWorld') // HelloWorld

capitalCase

Capitalize a string

import { string } from '@ioc:Adonis/Core/Helpers'
string.capitalCase('helloWorld') // Hello World

sentenceCase

Convert string to a sentence

import { string } from '@ioc:Adonis/Core/Helpers'
string.sentenceCase('hello-world') // Hello world

dotCase

Convert string to its dot.case version.

import { string } from '@ioc:Adonis/Core/Helpers'
string.dotCase('hello-world') // hello.world

noCase

Remove all sorts of casing

import { string } from '@ioc:Adonis/Core/Helpers'
string.noCase('hello-world') // hello world
string.noCase('hello_world') // hello world
string.noCase('helloWorld') // hello world

titleCase

Convert a sentence to title case

import { string } from '@ioc:Adonis/Core/Helpers'
string.titleCase('Here is a fox') // Here Is a Fox

pluralize

Pluralize a word.

import { string } from '@ioc:Adonis/Core/Helpers'
string.pluralize('box') // boxes
string.pluralize('i') // we

You can also define your own irregular rules using the defineIrregularRule method. The method accepts the singular version as the first argument and the plural version as the second argument.

import { string } from '@ioc:Adonis/Core/Helpers'
string.defineIrregularRule('auth', 'auth')
string.plural('auth') // auth

You can also define your own uncountable rules using the defineUncountableRule method.

import { string } from '@ioc:Adonis/Core/Helpers'
string.defineUncountableRule('login')
string.plural('login') // home

truncate

Truncate a string after a given number of characters

import { string } from '@ioc:Adonis/Core/Helpers'
string.truncate(
'This is a very long, maybe not that long title',
12
) // This is a ve...

By default, the string is truncated exactly after the given characters. However, you can instruct the method to wait for the words to complete.

string.truncate(
'This is a very long, maybe not that long title',
12,
{
completeWords: true
}
) // This is a very...

Also, it is possible to customize the suffix.

string.truncate(
'This is a very long, maybe not that long title',
12,
{
completeWords: true,
suffix: ' <a href="/1"> Read more </a>',
}
) // This is a very <a href="/1"> Read more </a>

excerpt

The excerpt method is the same as the truncate method. However, it strips the HTML from the string.

import { string } from '@ioc:Adonis/Core/Helpers'
string.excerpt(
'<p>This is a <strong>very long</strong>, maybe not that long title</p>',
12
) // This is a very...

condenseWhitespace

Condense whitespaces from a given string. The method removes the whitespace from the left, right, and multiple whitespaces between the words.

import { string } from '@ioc:Adonis/Core/Helpers'
string.condenseWhitespace(' hello world ')
// hello world

escapeHTML

Escape HTML from the string

import { string } from '@ioc:Adonis/Core/Helpers'
string.escapeHTML('<p> foo © bar </p>')
// &lt;p&gt; foo © bar &lt;/p&gt;

Additionally, you can also encode non-ASCII symbols.

import { string } from '@ioc:Adonis/Core/Helpers'
string.escapeHTML(
'<p> foo © bar </p>',
{
encodeSymbols: true
}
)
// &lt;p&gt; foo &#xA9; bar &lt;/p&gt;

encodeSymbols

Encode symbols. Checkout he for available options

import { string } from '@ioc:Adonis/Core/Helpers'
string.encodeSymbols('foo © bar')
// foo &#xA9; bar

toSentence

Join an array of words with a separator.

import { string } from '@ioc:Adonis/Core/Helpers'
string.toSentence([
'route',
'middleware',
'controller'
]) // route, middleware, and controller
string.toSentence([
'route',
'middleware'
]) // route and middleware

You can define the following options to customize the output.

  • separator is the value between two words except the last one.
  • pairSeparator is the value between the first and the last word. Used, only when there are two words
  • lastSeparator is the value between the second last and the last word. Used only when there are more than two words.
string.toSentence([
'route',
'middleware',
'controller'
], {
separator: '/ ',
lastSeparator: '/or '
}) // route/ middleware/or controller

prettyBytes

Convert bytes value to a human-readable string. For options, reference the bytes package.

import { string } from '@ioc:Adonis/Core/Helpers'
string.prettyBytes(1024) // 1KB
string.prettyBytes(1024, { unitSeparator: ' ' }) // 1 KB

toBytes

Convert human-readable string to bytes. This method is the opposite of the prettyBytes method.

import { string } from '@ioc:Adonis/Core/Helpers'
string.toBytes('1KB') // 1024

prettyMs

Convert time in milliseconds to a human-readable string

import { string } from '@ioc:Adonis/Core/Helpers'
string.prettyMs(60000) // 1min
string.prettyMs(60000, { long: true }) // 1 minute

toMs

Convert human-readable string to milliseconds. This method is the opposite of the prettyMs method.

import { string } from '@ioc:Adonis/Core/Helpers'
string.toMs('1min') // 60000

ordinalize

Ordinalize a string or a number value

import { string } from '@ioc:Adonis/Core/Helpers'
string.ordinalize(1) // 1st
string.ordinalize(99) // 99th

generateRandom

Generate a cryptographically strong random string

import { string } from '@ioc:Adonis/Core/Helpers'
string.generateRandom(32)

isEmpty

Find if a value is empty. Also checks for empty strings with all whitespace

import { string } from '@ioc:Adonis/Core/Helpers'
string.isEmpty('') // true
string.isEmpty(' ') // true

Type detection

Type detection in JavaScript is very weak and often leads to unexpected bugs. For example: typeof null is object and typeof [] is also an object.

You can use the types helper to have more accurate and consistent type checking in your application.

lookup

The lookup method returns the type for a given value.

import { types } from '@ioc:Adonis/Core/Helpers'
types.lookup({}) // object
types.lookup([]) // array
types.lookup(Object.create(null)) // object
types.lookup(null) // null
types.lookup(function () {}) // function
types.lookup(class Foo {}) // class
types.lookup(new Map()) // map

isNull

Find if the given value is null

import { types } from '@ioc:Adonis/Core/Helpers'
types.isNull(null) // true

isBoolean

Find if the given value is a boolean

import { types } from '@ioc:Adonis/Core/Helpers'
types.isBoolean(true) // true

isBuffer

Find if the given value is a buffer

import { types } from '@ioc:Adonis/Core/Helpers'
types.isBuffer(new Buffer()) // true

isNumber

Find if the given value is a number

import { types } from '@ioc:Adonis/Core/Helpers'
types.isNumber(100) // true

isString

Find if the given value is a string

import { types } from '@ioc:Adonis/Core/Helpers'
types.isString('hello') // true

isArguments

Find if the given value is an arguments object

import { types } from '@ioc:Adonis/Core/Helpers'
function foo() {
types.isArguments(arguments) // true
}

isObject

Find if the given value is a plain object

import { types } from '@ioc:Adonis/Core/Helpers'
types.isObject({}) // true

isDate

Find if the given value is a date object

import { types } from '@ioc:Adonis/Core/Helpers'
types.isDate(new Date()) // true

isArray

Find if the given value is an array

import { types } from '@ioc:Adonis/Core/Helpers'
types.isArray([1, 2, 3]) // true

isRegexp

Find if the given value is a regular expression

import { types } from '@ioc:Adonis/Core/Helpers'
types.isRegexp(/[a-z]+/) // true

isError

Find if the given value is an instance of the error object.

import { types } from '@ioc:Adonis/Core/Helpers'
import { Exception } from '@poppinss/utils'
types.isError(new Error('foo')) // true
types.isError(new Exception('foo')) // true

isFunction

Find if the given value is a function

import { types } from '@ioc:Adonis/Core/Helpers'
types.isFunction(function foo() {}) // true

isClass

Find if the given value is a class constructor. Uses regex to distinguish between a function and a class.

import { types } from '@ioc:Adonis/Core/Helpers'
class User {}
types.isClass(User) // true
types.isFunction(User) // false

isInteger

Find if the given value is an integer.

import { types } from '@ioc:Adonis/Core/Helpers'
types.isInteger(22.00) // true
types.isInteger(22) // true
types.isInteger(-1) // true
types.isInteger(-1.00) // true
types.isInteger(22.10) // false
types.isInteger(.3) // false
types.isInteger(-.3) // false

isFloat

Find if the given value is a float number.

import { types } from '@ioc:Adonis/Core/Helpers'
types.isFloat(22.10) // true
types.isFloat(-22.10) // true
types.isFloat(.3) // true
types.isFloat(-.3) // true
types.isFloat(22.00) // false
types.isFloat(-22.00) // false
types.isFloat(-22) // false

isDecimal

Find if the given value has a decimal. The value can be a string or a number. The number values are casted to a string by calling the toString() method on the value itself.

The string conversion is performed to test the value against a regex since there is no way to find a decimal value in JavaScript natively.

import { types } from '@ioc:Adonis/Core/Helpers'
types.isDecimal('22.10') // true
types.isDecimal(22.1) // true
types.isDecimal('-22.10') // true
types.isDecimal(-22.1) // true
types.isDecimal('.3') // true
types.isDecimal(0.3) // true
types.isDecimal('-.3') // true
types.isDecimal(-0.3) // true
types.isDecimal('22.00') // true
types.isDecimal(22.0) // false (gets converted to 22)
types.isDecimal('-22.00') // true
types.isDecimal(-22.0) // false (gets converted to -22)
types.isDecimal('22') // false
types.isDecimal(22) // false
types.isDecimal('0.0000000000001') // true
types.isDecimal(0.0000000000001) // false (gets converted to 1e-13)

safeEqual

Compares two values with each other by avoiding the timing attack . This method internally uses the crypto.timingSafeEqual method, but can also compare two strings.

import { safeEqual } from '@ioc:Adonis/Core/Helpers'
if (safeEqual('hello world', 'hello world')) {
}

requireAll

Helper to require all the .js, .ts and .json files from a directory. This method only works with commonjs modules and not with ES modules.

import { join } from 'path'
import { requireAll } from '@ioc:Adonis/Core/Helpers'
const configTree = requireAll(join(__dirname, 'config'))

The files are imported recursively by default. However, you can turn off recursive scanning by setting the second argument to false

requireAll(join(__dirname, 'config'), false)

An exception is raised when the root directory is missing. However, you can instruct the method to ignore the missing directory by setting the third argument as true.

requireAll(join(__dirname, 'config'), true, true)

fsReadAll

Recursively scan all and collect paths for all the .js, .ts, and .json files from a given directory.

import { join } from 'path'
import { fsReadAll } from '@ioc:Adonis/Core/Helpers'
fsReadAll(join(__dirname, 'config'))
// ['app.ts', 'bodyparser.ts', 'cors.ts']

Optionally you can define a custom filter function to ignore certain paths. Defining a custom filter removes the existing filter of selecting only .js, .ts and .json files.

fsReadAll(join(__dirname, 'config'), (filePath) => {
return filePath.endsWith('.md')
})

base64

Encode/decode Base64 values. Make use of the urlEncode and urlDecode methods if you want to pass the encoded value to a URL.

import { base64 } from '@ioc:Adonis/Core/Helpers'
base64.encode('hello world')
base64.decode(base64.encode('hello world'))
// URL safe encoding
base64.urlEncode('hello world')
base64.urlDecode(base64.urlEncode('hello world'))

You can also define custom encoding for the input value.

const encoded = base64.encode(bufferValue, 'binary')
base64.decode(encoded, 'binary')

interpolate

A lightweight helper method to interpolate curly braces inside a string. This method is not a replacement for any template engines.

import { interpolate } from '@ioc:Adonis/Core/Helpers'
interpolate('hello {{ username }}', { username: 'virk' })
// Nested values
interpolate('hello {{ user.username }}', {
user: { username: 'virk' }
})
// Array of objects
interpolate('hello {{ users.0.username }}', {
users: [{ username: 'virk' }]
})
// Array of literal values
interpolate('hello {{ scores.0 }}', {
scores: [67, 80]
})

compose

JavaScript doesn't have a concept of inheriting multiple classes together, and neither does TypeScript. However, the official documentation of TypeScript does talks about the concept of mixins.

As per the TypeScript docs, you can create and apply mixins as follows.

type Constructor = new (...args: any[]) => any
const UserWithEmail = <T extends Constructor>(superclass: T) => {
return class extends superclass {
public email: string
}
}
const UserWithPassword = <T extends Constructor>(superclass: T) => {
return class extends superclass {
public password: string
}
}
class BaseModel {}
class User extends UserWithPassword(UserWithEmail(BaseModel)) {}

Mixins are close to a perfect way of inheriting multiple classes. I recommend reading this article for the same.

However, the syntax of applying multiple mixins is ugly, as you have to apply mixins over mixins, creating a nested hierarchy as shown below.

class User extends UserWithAttributes(
UserWithAge(
UserWithPassword(
UserWithEmail(BaseModel)
)
)
) {}

The compose method is a small utility to improve the syntax a bit.

import { compose } from '@ioc:Adonis/Core/Helpers'
class User extends compose(
BaseModel,
UserWithPassword,
UserWithEmail,
UserWithAge,
UserWithAttributes
) {}

Mixins gotchas

TypeScript has an open issue related to the constructor arguments of the mixin class or the base class.

TypeScript expects all classes used in the mixin chain to have a constructor with only one argument of ...args: any[]. For example: The following code will work fine at runtime, but the TypeScript compiler complains about it.

class BaseModel {
constructor(name: string) {}
}
const UserWithEmail = <T extends typeof BaseModel>(superclass: T) => {
return class extends superclass {
// ERROR: A mixin class must have a constructor with a single rest parameter of type 'any[]'.ts(2545)
public email: string
}
}
class User extends compose(BaseModel, UserWithEmail) {}

You can work around this by overriding the base class's constructor using the NormalizeConstructor type.

import {
compose,
NormalizeConstructor
} from '@ioc:Adonis/Core/Helpers'
const UserWithEmail = <T extends NormalizeConstructor<typeof BaseModel>>(
superclass: T
) => {
return class extends superclass {
public email: string
}
}

cuid

Generate a collision-resistant ID .

import { cuid } from '@ioc:Adonis/Core/Helpers'
cuid()
// cjld2cjxh0000qzrmn831i7rn