Helpers
Understand how to use all the Athenna Helpers from @athenna/common and other packages.
Introduction
Athenna includes a variety of "helpers" classes inside
the @athenna/common
package that comes by default
installed in your application. Many of these classes are
used by the framework itself; however, you are free to use
them in your own applications if you find them convenient.
Available helpers
Clean
- Remove falsy values from different data structures.Color
- The UI Kit of Athenna command line applications.Enum
- Create enums with helper methods to retrieve keys, values and more.Exception
- Create errors with more details to handle them easily.Exec
- Simple helpers that executes some operation, like executing a command in a child process.FakeApi
- Create a fake REST API usingjson
files to map the routes and their returns (similiar to WireMock).File
- Create, copy, move, delete and get information about files.Folder
- Create, copy, move, delete and get information about folders.Globals
- Execute global helpers exposed by Athenna.HttpClient
- Make HTTP requests for other servers with a human-friendly and powerful API.HttpClientBuilder
- Build requests using builder pattern instead of creating options as objects.Is
- Validate if your data is equals to some type or pattern.Json
- Simple helpers to manipulate JSON.Module
- Simple helpers to manipulate Node.js modules.Number
- Simple helpers to manipulate numbers.ObjectBuilder
- Build objects removingundefined
andnull
values and without memory reference.Options
- Simple helpers to develop API's (functions and methods) with good options and configurations for developers.Parser
- Parse data from X to Y and Y to X.Path
- Get the full path to some file or folder starting from your application PWD.Route
- Simple helpers to manipulate route params, query params and more.String
- Simple helpers to manipulate strings.Uuid
- Create customized UUID v4 and validate if they are valid.
Clean
Clean::cleanArray()
Clean falsy and empty values from an array:
import { Clean } from '@athenna/common'
const array = [
'',
1,
null,
undefined,
{ joao: 'joao', lenon: null },
{}
]
const cleanedArray = Clean.cleanArray(array)
console.log(cleanedArray)
/**
* [
* 1,
* { joao: 'joao', lenon: null },
* {}
* ]
*/
The cleanArray()
method supports two options, removeEmpty
to remove empty
values like {}
and []
and recursive
to recursivelly remove data
from objects and arrays:
const cleanedArray = Clean.cleanArray(array, {
removeEmpty: true,
recursive: true
})
console.log(cleanedArray)
/**
* [
* 1, { joao: 'joao' }
* ]
*/
Clean::cleanObject()
const object = {
key: 'value',
emptyArray: [],
emptyObject: {},
object: { joao: 'joao' },
nullValue: null,
undefinedValue: undefined,
arrayWithSubs: [null, 1, { joao: 'joao', lenon: null }, {}],
}
const cleanedObject = Clean.cleanObject(object)
console.log(cleanedObject)
/**
* {
* key: 'value',
* emptyArray: [],
* emptyObject: {},
* object: { joao: 'joao' },
* arrayWithSubs: [null, 1, { joao: 'joao', lenon: null }, {}],
* }
*/
The cleanObject()
method supports two options, removeEmpty
to remove empty
values like {}
and []
and recursive
to recursivelly remove data
from objects and arrays:
const cleanedObject = Clean.cleanObject(object, {
removeEmpty: true,
recursive: true,
})
console.log(cleanedObject)
/**
* {
* key: 'value',
* object: { joao: 'joao' },
* arrayWithSubs: [1, { joao: 'joao' }],
* }
*/
Color
This helper uses chalk
library under the hood to create unique colors for Athenna
CLI applications. In this documentation we are going to focus
only in helpers that Color
provides different from chalk.
Color::apply()
Apply the color engine of Athenna to some string. The color
engine execute chalk
methods under the hood. In the example bellow we are executing
yellow()
and bold()
method of
chalk in the word
World
:
import { Color } from '@athenna/common'
const colorizedString = Color.apply('Hello ({yellow, bold} World)!')
The color of World
word will be yellow with bold format.
Color::remove()
Remove all the formats and colors of your string:
import { Color } from '@athenna/common'
const noColorString = Color.remove(Color.yellow('Davi Mezencio'))
Color::httpMethod()
Get an instance of chalk with a determined color for each type of http method:
import { Color } from '@athenna/common'
Color.httpMethod('GET').bold('Request Received')
Color.httpMethod('HEAD').bold('Request Received')
Color.httpMethod('POST').bold('Request Received')
Color.httpMethod('PUT').bold('Request Received')
Color.httpMethod('PATCH').bold('Request Received')
Color.httpMethod('DELETE').bold('Request Received')
Color.httpMethod('OPTIONS').bold('Request Received')
Enum
Enum::keys()
Return all the keys of your enum:
import { Enum } from '@athenna/common'
class Status extends Enum {
static PENDING = 'pending'
static APPROVED = 'approved'
}
const keys = Status.keys() // ['PENDING', 'APPROVED']
Enum::values()
Return all the values of your enum:
import { Enum } from '@athenna/common'
class Status extends Enum {
static PENDING = 0
static APPROVED = 1
}
const values = Status.values() // [0, 1]
Enum::entries()
Return all the entries of your enum:
import { Enum } from '@athenna/common'
class Status extends Enum {
static PENDING = 0
static APPROVED = 1
}
const entries = Status.entries() // [['PENDING', 0], ['APPROVED', 1]]
Exception
In this documentation section we are going to cover only the
helpers of the Exception
class. You can learn more about
exceptions when handling errors in your application. Those are
the available documentation for error handling by application:
Exception.toJSON()
Get the informations of the exception as JSON:
import { Exception } from '@athenna/common'
const exception = new Exception({
status: 500,
details: [],
otherInfos: {},
name: 'ErrorName',
code: 'ERROR_CODE',
message: 'Some exception has ocurred.',
help: 'Try restarting your computer, should work.'
})
const json = exception.toJSON() // { status: 500, name: 'ErrorName', ... }
Exception.prettify()
Transform the exception to a human redable format:
import { Exception } from '@athenna/common'
const exception = new Exception({
status: 500,
details: [],
otherInfos: {},
name: 'ErrorName',
code: 'ERROR_CODE',
message: 'Some exception has ocurred.',
help: 'Try restarting your computer, should work.'
})
Logger.error(await exception.prettify())
Exec
Exec::sleep()
Put the code to sleep for a determined amount of MS:
import { Exec } from '@athenna/common'
await Exec.sleep(3000) // 3 seconds
Exec::concurrently()
Execute some callback concurrently for each value of the array:
import { Exec } from '@athenna/common'
const array = [1, 2, 3]
const newArray = await Exec.concurrently(array, async (number) => {
// Some sync operation just to simulate
await Exec.sleep(1000)
return number++
})
Exec::link()
Link some library using any kind of package manager
(tested only with npm
):
import { Exec } from '@athenna/common'
await Exec.link('@athenna/mail')
await Exec.link(['@athenna/mail', '@athenna/database'], { registry: 'npm' })
await Exec.link(['@athenna/mail', '@athenna/database'], {
silent: true,
args: ['-D'],
reject: false,
registry: 'npm',
cwd: Path.pwd('other-pkg'),
})
Exec::install()
Install some library using any kind of package manager
(tested only with npm
):
import { Exec } from '@athenna/common'
await Exec.install('@athenna/mail')
await Exec.install(['@athenna/mail', '@athenna/database'], { registry: 'npm' })
await Exec.install(['@athenna/mail', '@athenna/database'], {
silent: true,
dev: false,
args: ['-D'],
cached: false,
reject: false,
registry: 'npm',
cwd: Path.pwd('other-pkg'),
})
Exec::command()
Execute some command of your OS in a child process:
import { Exec } from '@athenna/common'
const { stdout, stderr } = await Exec.command('ls -la')
You can add as second parameter an object with some options:
import { Exec, type CommandInput } from '@athenna/common'
const options: CommandInput = {}
const { stdout, stderr } = await Exec.command('ls -la', options)
Exec::shell()
Same as Exec::command()
method, but works only for shell consoles:
import { Exec } from '@athenna/common'
const { stdout, stderr } = await Exec.shell('ls -la')
Exec::node()
Execute some Node.js script in a child process:
#!/usr/bin/env node
import { Exec } from '@athenna/common'
Exec.artisan('./index.ts', {
nodeOptions: ['--import=@athenna/tsconfig']
})
Exec::artisan()
This method was created specially to execute artisan
scripts
in child processes. Under the hood it calls Exec::node()
method
but with predefined options:
#!/usr/bin/env node
import { Exec } from '@athenna/common'
Exec.artisan('./bootstrap/artisan.js', {
nodeOptions: ['--enable-source-maps', '--import=@athenna/tsconfig']
})
Exec::download()
Download some file from some URL and get the File
instance:
import { Exec } from '@athenna/common'
// File helper class instance
const file = await Exec.download(Path.storage('file.txt'), 'https://athenna.io/file.txt')
Exec::pagination()
Create a pagination object for your API:
import { Exec } from '@athenna/common'
const data = [{...}, {...}]
const paginateObject = await Exec.pagination(data, data.length, {
page: 0,
limit: 10,
resourceUrl: Config.get('app.url')
})
console.log(paginateObject)
/**
* {
* data: [{...}, {...}],
* meta: {
* itemCount: 2,
* totalItems: 2,
* totalPages: 1,
* currentPage: 0,
* itemsPerPage: 10,
* },
* links: {
* first: 'http://localhost:3000?limit=10',
* previous: 'http://localhost:3000?page=0&limit=10',
* next: 'http://localhost:3000?page=1&limit=10',
* last: 'http://localhost:3000?page=0&limit=10',
* }
* }
*/
FakeApi
FakeApi::start()
Start the fake server on port 8989
and loading the path
Path.resources('fake-api')
./resources/fake-api
import { FakeApi } from '@athenna/common'
await FakeApi.start()
You can change the server port and the path to read the
json
files:
import { FakeApi } from '@athenna/common'
await FakeApi.start(8989, Path.resources('path'))
FakeApi::stop()
Stop the fake api server and call FakeApi::recreate()
method to create a new server instance:
import { FakeApi } from '@athenna/common'
await FakeApi.stop()
FakeApi::isRunning()
Verify if the fake api server is running:
import { FakeApi } from '@athenna/common'
if (FakeApi.isRunning()) {
// do something...
}
FakeApi::listRoutes()
List all the routes registered in the fake api server:
import { FakeApi } from '@athenna/common'
const routes = FakeApi.listRoutes()
console.log(routes)
FakeApi::registerFile()
Register a route using a json
file in the fake api:
import { FakeApi } from '@athenna/common'
FakeApi.registerFile(Path.resources('fake-api/users.json'))
Content of
Path.fake-api('users.json')
./fake-api/users.json
{
"path": "/users",
"method": "GET",
"statusCode": 200, 👈 // The response status code
"body": [ 👈 // The response body
{
"id": 1,
"name": "João Lenon",
},
{
"id": 2,
"name": "Thais Gabriela",
}
],
"headers": { 👈 // The response header
"Content-Type": "application/json"
}
}
FakeApi::registerFolder()
Recursively register all the json
files of some folder:
import { FakeApi } from '@athenna/common'
await FakeApi.registerFolder(Path.resources('fake-api'))
FakeApi::build()
Use this method to programatically build the routes u sing the builder pattern:
import { FakeApi } from '@athenna/common'
import type { RouteOptions } from 'fastify'
const options: RouteOptions = {}
const fakeApiBuilder = FakeApi.build()
const users = [
{
id: 1,
name: 'João Lenon',
},
{
id: 2,
name: 'Thais Gabriela',
}
]
fakeApiBuilder
.path('/users')
.method('GET')
.statusCode(200)
.body(users)
.header({ 'Content-Type': 'application/json' })
.register(options)
File
File.load()
& File.loadSync()
Creates the file is does not exist and also load the file information:
import { File } from '@athenna/common'
const existent = new File(Path.storage('existent.txt'))
const nonExistent = new File('./nonExistent.txt', 'File content')
// Load the file info and content.
await existent.load({ withContent: true }) 👈
// Create and load the file info without the
// content (be careful when loading big files).
nonExistent.loadSync() 👈
After loading process, the file will contain new informations:
- createdAt - The date when the file was created.
- accessedAt - The date when the file was last accessed.
- modifiedAt - The date when the file was last modified.
- fileSize - The size of the file in MB.
- content - The content of the file as
Buffer
ifwithContent
wastrue
.
File.copy()
& File.copySync()
Create a copy of the file in other location or with other name:
import { File } from '@athenna/common'
const copiedFile = file.copySync('./copy-of-file.txt')
const copiedFile = await file.copy(Path.storage('copy-of-file.txt'))
To copy the file and load the content of the copy set the withContent
as true
:
import { File } from '@athenna/common'
const copiedFile = await file.copy(Path.storage('copy-of-file.txt'), {
withContent: true 👈
})
When copying the file you can set the mockedValues
to true
to create
a file with fake name:
import { File } from '@athenna/common'
const copiedFile = await file.copy(Path.storage('copy-of-file.txt'), {
mockedValues: true 👈
})
File.move()
& File.moveSync()
Move the file to other location:
import { File } from '@athenna/common'
const movedFile = file.moveSync('./move-of-file.txt') 👈
const movedFile = await file.move(Path.storage('move-of-file.txt')) 👈
To move the file and load the content of the move set the withContent
as true
:
import { File } from '@athenna/common'
const movedFile = await file.move(Path.storage('move-of-file.txt'), {
withContent: true 👈
})
When moving the file you can set the mockedValues
to true
to create
a file with fake name:
import { File } from '@athenna/common'
const movedFile = await file.move(Path.storage('file-path.txt'), {
mockedValues: true 👈
})
File.remove()
& File.removeSync()
Delete a file from the file system:
import { File } from '@athenna/common'
file.removeSync() 👈
await file.remove() 👈
File.setContent()
& File.setContentSync()
Set the content of a file overwriting the existing content:
import { File } from '@athenna/common'
const file = new File('./file.txt', 'Hello')
file.setContentSync('Hello World!') 👈
await file.setContent('Hello World!') 👈
File.getContent()
& File.getContentSync()
Get the content of a file as Buffer
:
import { File } from '@athenna/common'
const contentBuffer = file.getContentSync() 👈
const contentBuffer = await file.getContent() 👈
To save the content of the file in the instance set the saveContent
as true
:
import { File } from '@athenna/common'
const content = await file.getContent({ saveContent: true }) 👈
File.getContentAsString()
& File.getContentAsStringSync()
Same behavior of getContent()
/getContentSync()
, but return the content
as string
:
import { File } from '@athenna/common'
const contentString = file.getContentAsStringSync() 👈
const contentString = await file.getContentAsString() 👈
File.getContentAsJson()
& File.getContentAsJsonSync()
Same behavior of getContent()
/getContentSync()
, but return the content as
object
if the content is a valid JSON string:
import { File } from '@athenna/common'
const contentJSON = file.getContentAsJsonSync() 👈
const contentJSON = await file.getContentAsJson() 👈
File.getContentAsYaml()
& File.getContentAsYamlSync()
Same behavior of getContentAsJson()
/getContentAsJsonSync()
, but automatically
parse the the yaml to an object
if the content is a valid YAML string:
import { File } from '@athenna/common'
const contentYAML = file.getContentAsYamlSync() 👈
const contentYAML = await file.getContentAsYaml() 👈
File.getContentAsBuilder()
& File.getContentAsBuilderSync()
Same behavior of getContent()
/getContentSync()
, but return the content as
an ObjectBuilder
instance if the
content is a valid JSON string:
import { File } from '@athenna/common'
const contentObjectBuilder = file.getContentAsBuilderSync() 👈
const contentObjectBuilder = await file.getContentAsBuilder() 👈
File.append()
& File.appendSync()
Add content to the end of the file:
import { File } from '@athenna/common'
const file = new File('./file.txt', 'Hello')
file.appendSync(' World') 👈
await file.append('!\n') 👈
File.prepend()
& File.prependSync()
Add content to the top of the file:
import { File } from '@athenna/common'
const file = new File('./file.txt', 'World')
file.prependSync('ello ') 👈
await file.prepend('H') 👈
File.createReadStream()
Create a readable stream instance of the file:
const stream = file.createReadStream()
File.createWriteStream()
Create a writable stream instance of the file:
const stream = file.createWriteStream()
File.toJSON()
Get the informations of the file as JSON:
const infos = file.toJSON() 👈
File.import()
Import the file path if is a valid module:
import { File } from '@athenna/common'
const file = new File('./file.js', "console.log('hello')")
const module = await file.import() 👈
File.safeImport()
Same as import()
method, but if the file is not a valid module the exception
will be ignored:
import { File } from '@athenna/common'
const file = new File('./file.txt', "console.log('hello')")
const module = await file.safeImport() 👈
Importing files that got any errors like syntax errors will also not throw:
import { File } from '@athenna/common'
const file = new File('./file.js', "console.log('hello")
const module = await file.safeImport() 👈 // Nothing happens
File::safeRemove()
Call for a delete operation without worrying about exceptions because the file does not exist:
import { File } from '@athenna/common'
await File.safeRemove(Path.storage('file.txt')) 👈
await File.safeRemove(Path.storage('not-found.txt')) 👈 // Will not throw
File::exists()
& File::existsSync()
Verify if a file exists or not:
if (File.existsSync('package.json')) {
// do something
}
if (await File.exists('package.json')) {
// do something
}
File::isFile()
& File::isFileSync()
Verify if a file is a valid file or not:
if (File.isFileSync('package.json')) {
// do something
}
if (await File.isFile('package.json')) {
// do something
}
File::createFileOfSize()
Create a fake file with determined size for testing purposes:
const file = await File.createFileOfSize('fake.js', 1024 * 1024 * 100) 👈
Folder
Folder.load()
& Folder.loadSync()
Creates the folder if it does not exist and also load the folder information:
import { Folder } from '@athenna/common'
const existent = new Folder(Path.storage('existent'))
const nonExistent = new Folder('./nonExistent')
// Load the folder info with sub folders and with file contents.
await existent.load({ withSub: true, withContent: true }) 👈
// Create and load the folder info without the
// content (be careful when loading big files).
nonExistent.loadSync() 👈
After loading process, the folder will contain new informations:
- createdAt - The date when the folder was created.
- accessedAt - The date when the folder was last accessed.
- modifiedAt - The date when the folder was last modified.
- folderSize - The size of the folder in MB.
Folder.copy()
& Folder.copySync()
Create a copy of the folder in other location or with other name:
import { Folder } from '@athenna/common'
const copiedFolder = folder.copySync('./copy-of-folder')
const copiedFolder = await folder.copy(Path.storage('copy-of-folder'))
To copy the folder and load the sub folders and the content of the
copy set the withSub
and withContent
as true
:
import { Folder } from '@athenna/common'
const copiedFolder = await folder.copy(Path.storage('copy-of-folder'), {
withSub: true, 👈
withContent: true 👈
})
When copying the folder you can set the mockedValues
to true
to copy the
files with fake names:
import { Folder } from '@athenna/common'
const copiedFolder = await folder.copy(Path.storage('copy-of-file'), {
mockedValues: true 👈
})
Folder.move()
& Folder.moveSync()
Move the folder to other location:
import { Folder } from '@athenna/common'
const movedFolder = folder.moveSync('./move-of-folder') 👈
const movedFolder = await folder.move(Path.storage('move-of-folder')) 👈
To move the folder and load the sub folders and the content of the
move set the withSub
and withContent
as true
:
import { Folder } from '@athenna/common'
const movedFolder = await folder.move(Path.storage('move-of-folder'), {
withSub: true, 👈
withContent: true 👈
})
When moving the folder you can set the mockedValues
to true
to move the
files with fake names:
import { Folder } from '@athenna/common'
const movedFolder = await folder.move(Path.storage('file-path'), {
mockedValues: true 👈
})
Folder.remove()
& Folder.removeSync()
Delete a folder from the folder system:
import { Folder } from '@athenna/common'
folder.removeSync() 👈
await folder.remove() 👈
Folder.toJSON()
Get the informations of the folder as JSON:
const infos = folder.toJSON() 👈
Folder.getFilesByPattern()
Get all the files of a folder using a glob pattern:
const files = folder.getFilesByPattern('**/*.js') 👈
Folder.getFoldersByPattern()
Get all the folders of a folder using a glob pattern:
const folders = folder.getFoldersByPattern('**/*') 👈
Folder::safeRemove()
Call for a delete operation without worrying about exceptions because the folder does not exist:
import { Folder } from '@athenna/common'
await Folder.safeRemove(Path.storage('folder')) 👈
await Folder.safeRemove(Path.storage('not-found')) 👈 // Will not throw
Folder::exists()
& Folder::existsSync()
Verify if a folder exists or not:
if (Folder.existsSync('app')) {
// do something
}
if (await Folder.exists('app')) {
// do something
}
Folder::isFolder()
& Folder::isFolderSync()
Verify if a folder is a valid folder or not:
if (Folder.isFolderSync('app')) {
// do something
}
if (await Folder.isFolder('app')) {
// do something
}
Folder::size()
& Folder::sizeSync()
Get the size of the folder in MB:
if (Folder.sizeSync('app') === 100) {
// do something
}
if (await Folder.size('app') === 100) {
// do something
}
Globals
Array.athenna.random()
Return a random value from the array:
const numbers = [1, 2, 3, 4, 5]
const number = numbers.athenna.random() // 4
Array.athenna.toJSON()
For each value inside an array, execute the toJSON()
method if exists:
class User {
public constructor(private name: string) {}
public toJSON(criterias: any = {}) {
return { name: this.name }
}
}
const criterias = {}
const users = [new User('lenon'), new User('txsoura'), {}, 1, 0]
const jsons = users.athenna.toJSON(criterias)
// [{ name: 'lenon' }, { name:'txsoura' }]
Array.athenna.toResource()
For each value inside an array, execute the toResource()
method if exists:
class User {
public constructor(private name: string) {}
public toResource(criterias: any = {}) {
return { name: this.name }
}
}
const criterias = {}
const users = [new User('lenon'), new User('txsoura'), {}, 1, 0]
const resources = users.athenna.toResource(criterias)
// [{ name: 'lenon' }, { name:'txsoura' }]
Array.athenna.toCollection()
Parse an array to an Athenna collection:
class User {
public constructor(private name: string) {}
}
const users = [new User('lenon'), new User('txsoura')]
const collection = users.athenna.toCollection().all()
// [{ name: 'lenon' }, { name:'txsoura' }]
Error.toAthennaException()
Parse a vanilla error to an Athenna exception:
new Error().toAthennaException({ status: 404 })
HttpClient
This helper uses the got
library under the hood to build the requests.
HttpClient::get()
Make a GET
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const options: Request = {
timeout: 2000
}
const response: Response<any> = await HttpClient.get('https://athenna.io', options)
HttpClient::post()
Make a POST
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const url = 'https://athenna.io'
const options: Request = {
body: {
name: 'João Lenon',
age: 22,
},
timeout: 2000
}
const response: Response<any> = await HttpClient.post(url, options)
HttpClient::put()
Make a PUT
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const url = 'https://athenna.io'
const options: Request = {
body: {
name: 'João Lenon',
age: 22,
},
timeout: 2000
}
const response: Response<any> = await HttpClient.put(url, options)
HttpClient::patch()
Make a PATCH
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const url = 'https://athenna.io'
const options: Request = {
body: {
name: 'João Lenon',
age: 22,
},
timeout: 2000
}
const response: Response<any> = await HttpClient.patch(url, options)
HttpClient::delete()
Make a DELETE
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const url = 'https://athenna.io'
const options: Request = {
timeout: 2000,
}
const response: Response<any> = await HttpClient.delete(url, options)
HttpClient::head()
Make a HEAD
request to some URL:
import {
HttpClient,
type Request,
type Response
} from '@athenna/common'
const url = 'https://athenna.io'
const options: Request = {
timeout: 2000,
}
const response: Response<any> = await HttpClient.head(url, options)
HttpClient::builder()
& HttpClient::setBuilder()
Set the HttpClientBuilder
instance that HttpClient
class
will use to build your requests in the example above:
import {
HttpClient,
type RequestError,
type RetryObject
} from '@athenna/common'
const newBuilder = true
const builder = HttpClient.builder(newBuilder)
builder
.timeout(2000)
.prefixUrl('https://athenna.io')
.retryStrategy((error: RequestError, execCount: number, retryObject: RetryObject) => {
if (execCount === 3) {
return 0
}
return 2000
})
HttpClient.setBuilder(builder)
// Now all the requests done with HttpClient
// will use the same predefined builder options:
const response = await HttpClient.get('https://athenna.io')
HttpClientBuilder
This helper uses the got
library under the hood to build the requests.
HttpClientBuilder.setInitHook()
Called with the plain request options, right before
their normalization. The second argument represents
the current Options
instance.
- This hook must be synchronous.
- This is called every time options are merged.
- The
options
object may not have theurl
property. To modify it, use abeforeRequest
hook instead. - This hook is called when a new instance of
Options
is created. Do not confuse this with the creation ofRequest
orgot(…)
.
For example, this can be used to fix typos to migrate from older versions faster.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.setInitHook(init => {
if ('followRedirects' in init) {
init.followRedirect = init.followRedirects
delete init.followRedirects
}
})
HttpClientBuilder.setBeforeRequestHook()
Called right before making the request with
options.createNativeRequestOptions()
. This hook
is especially useful in conjunction with
HttpClient.setBuilder(customBuilder)
when you want
to sign your request.
- HttpClient will make no further changes to the request before it is sent.
- Changing
options.json
oroptions.form
has no effect on the request. You should changeoptions.body
instead. If needed, update theoptions.headers
accordingly.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.setBeforeRequestHook(options => {
options.body = JSON.stringify({ payload: 'new' })
options.headers['content-length'] = options.body.length.toString()
})
HttpClientBuilder.setBeforeRedirectHook()
The equivalent of setBeforeRequestHook
but when
redirecting.
- This is especially useful when you want to avoid dead sites.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.setBeforeRedirectHook((options, response) => {
if (options.hostname === 'deadSite') {
options.hostname = 'fallbackSite'
}
})
HttpClientBuilder.setBeforeErrorHook()
Called with a RequestError
instance. The error
is passed to the hook right before it's thrown.
This is especially useful when you want to have
more detailed errors.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.responseType('json')
.setBeforeErrorHook(error => {
const { response } = error
if (response && response.body) {
error.name = 'GitHubError'
error.message = `${response.body.message} (${response.statusCode})`
}
return error
})
HttpClientBuilder.setBeforeRetryHook()
The equivalent of setBeforeErrorHook
but when
retrying. Additionally, there is a second argument
retryCount
, the current retry number.
- When using the Stream API, this hook is ignored.
- When retrying, the
beforeRequest
hook is called afterwards. - If no retry occurs, the
beforeError
hook is called instead.
This hook is especially useful when you want to retrieve the cause of a retry.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.setBeforeRetryHook((error, retryCount) => {
console.log(`Retrying [${retryCount}]: ${error.code}`)
// Retrying [1]: ERR_NON_2XX_3XX_RESPONSE
})
HttpClientBuilder.setAfterResponseHook()
Each function should return the response. This is especially useful when you want to refresh an access token.
- When using the Stream API, this hook is ignored.
- Calling the
retryWithMergedOptions
function will triggerbeforeRetry
hooks. If the retry is successful, all remainingafterResponse
hooks will be called. In case of an error,beforeRetry
hooks will be called instead.
Meanwhile, the init
, beforeRequest
, beforeRedirect
as well as already executed afterResponse
hooks will be
skipped.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.mutableDefaults(true)
.setBeforeRetry(error => {
// This will be called on `retryWithMergedOptions(...)`
})
.setAfterResponseHook((response, retryWithMergedOptions) => {
// Unauthorized
if (response.statusCode === 401) {
// Refresh the access token
const updatedOptions = {
headers: {
token: getNewToken()
}
};
// Update the defaults
instance.defaults.options.merge(updatedOptions)
// Make a new retry
return retryWithMergedOptions(updatedOptions)
}
// No changes otherwise
return response
})
HttpClientBuilder.agent()
An object representing http
, https
and http2
keys
for http.Agent
,
https.Agent
and http2wrapper.Agent
instance. This is necessary because a request to one
protocol might redirect to another. In such a scenario,
HttpClient will switch over to the right protocol agent for
you. If a key is not present, it will default to a global
agent.
import { HttpClientBuilder } from '@athenna/common'
import HttpAgent, { HttpsAgent } from 'agentkeepalive'
const builder = new HttpClientBuilder()
builder.agent({ http: new HttpAgent(), https: new HttpsAgent() })
HttpClientBuilder.http2()
If set to true
, HttpClient will additionally accept
HTTP2 requests.
It will choose either HTTP/1.1 or HTTP/2 depending on the ALPN protocol:
- This option requires Node.js 15.10.0 or newer as HTTP/2 support on older Node.js versions is very buggy.
- Overriding
options.request
will disable HTTP2 support.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.http2(true)
HttpClientBuilder.h2session()
Set the http2 session instance to be used by the request:
import { connect } from 'node:http2'
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const session = connect('https://localhost:1234')
builder.h2session(session)
HttpClientBuilder.decompress()
Decompress the response automatically. This will set
the accept-encoding
header to gzip, deflate, br
unless you set it yourself. If this is disabled, a
compressed response is returned as a Buffer
. This
may be useful if you want to handle decompression
yourself or stream the raw compressed data.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.decompress(true)
HttpClientBuilder.timeout()
Milliseconds to wait for the server to end the response
before aborting the request with a timeout error
(a.k.a. request
property). By default, there's no timeout.
This also accepts an object
with the following fields to
constrain the duration of each phase of the request lifecycle:
lookup
starts when a socket is assigned and ends when the hostname has been resolved. Does not apply when using a Unix domain socket.connect
starts whenlookup
completes (or when the socket is assigned if lookup does not apply to the request) and ends when the socket is connected.secureConnect
starts whenconnect
completes and ends when the handshaking process completes (HTTPS only).socket
starts when the socket is connected. See request.setTimeout.response
starts when the request has been written to the socket and ends when the response headers are received.send
starts when the socket is connected and ends with the request has been written to the socket.request
starts when the request is initiated and ends when the response's end event fires.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.timeout(2000)
// or
builder.timeout({
lookup: 2000,
})
HttpClientBuilder.body()
Set the request body:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.body('hello', 'world!')
// or overwriting the entire body set before
builder.body({ hello: 'world!' })
HttpClientBuilder.input()
Alias for HttpClientBuilder.body()
method:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.input('hello', 'world!')
HttpClientBuilder.form()
Set the request body as form
:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.form({
hello: 'world!'
})
HttpClientBuilder.when()
Execute a closure only when first value is truthy:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.when(true, (builder, value) => {
builder.input('isAdmin', value)
})
HttpClientBuilder.header()
Set a header of the request:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.header('Content-Type', 'application/json')
// or overwriting the entire header set before
builder.header({ 'Content-Type': 'application/json' })
HttpClientBuilder.safeHeader()
Same as header()
,
but if the header already exists it will not be overwritten:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.header('Content-Type', 'application/json')
.safeHeader('Content-Type', 'will-not-change')
HttpClientBuilder.removeHeader()
Remove the header of the request:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.header('Content-Type', 'application/json')
.removeHeader('Content-Type')
HttpClientBuilder.prefixUrl()
When specified, prefixUrl
will be prepended to url
.
The prefix can be any valid URL, either relative or absolute.
A trailing slash /
is optional - one will be added
automatically:
prefixUrl
will be ignored if theurl
argument is a URL instance.- Leading slashes in
input
are disallowed when using this option to enforce consistency and avoid confusion. For example, when the prefix URL ishttps://example.com/foo
and the input is/bar
, there's ambiguity whether the resulting URL would becomehttps://example.com/foo/bar
orhttps://example.com/bar
. The latter is used by browsers. - You can change
prefixUrl
using hooks as long as the URL still includes theprefixUrl
. If the URL doesn't include it anymore, it will throw.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.prefixUrl('https://athenna.io')
HttpClientBuilder.method()
Set the request method:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.method('POST')
HttpClientBuilder.url()
Set the request url:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.url('https://athenna.io/api/v1')
HttpClientBuilder.cookieJar()
Set the cookie jar of the request:
- If you provide this option,
options.headers.cookie
will be overridden.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.cookieJar(new CookieJar())
HttpClientBuilder.signal()
Set the signal of the request:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const abortController = new AbortController()
build.signal(abortController.signal)
setTimeout(() => {
abortController.abort()
}, 100)
HttpClientBuilder.ignoreInvalidCookies()
Ignore invalid cookies instead of throwing an
error. Only useful when the cookieJar
option has
been set. Not recommended:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.ignoreInvalidCookies(true)
HttpClientBuilder.searchParams()
Query string that will be added to the request URL.
This will override the query string in url
. If you
need to pass in an array, you can do it using a
URLSearchParams
instance:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.searchParams('hello', 'world!')
// overwrite queries set before
.searchParams({ hello: 'world!' })
// overwrite queries set before
.searchParams(new URLSearchParams([['key', 'a'], ['key', 'b']]))
HttpClientBuilder.query()
Alias for searchParams()
method.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.query('hello', 'world!')
// overwrite queries set before
.query({ hello: 'world!' })
// overwrite queries set before
.query(new URLSearchParams([['key', 'a'], ['key', 'b']]))
HttpClientBuilder.safeQuery()
Same as query()
,
but if the query already exists it will not be overwritten:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.safeQuery('hello', 'world!')
HttpClientBuilder.dnsLookup()
Set the DNS lookup options of the request:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.dnsLookup({
family: 6
})
HttpClientBuilder.dnsCache()
An instance of CacheableLookup
used for making DNS lookups. Useful when making lots of
requests to different public hostnames.
CacheableLookup
uses dns.resolver4(..)
and
dns.resolver6(...)
under the hood and fall backs to
dns.lookup(...)
when the first two fail, which may lead
to additional delay:
- This should stay disabled when making requests to
internal hostnames such as
localhost
,database.local
etc.
import CacheableLookup from 'cacheable-lookup'
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.dnsCache(new CacheableLookup())
HttpClientBuilder.context()
User data. context
is shallow merged and enumerable.
If it contains non-enumerable properties they will NOT
be merged:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder
.setBeforeRequestHook(options => {
if (!options.context || !options.context.token) {
throw new Error('Token required')
}
options.headers.token = options.context.token
})
.context({ token: 'secret' })
HttpClientBuilder.hooks()
Hooks allow modifications during the request lifecycle. Hook functions may be async and are run serially:
import { HttpClientBuilder, type Hooks } from '@athenna/common'
const builder = new HttpClientBuilder()
const hooks: Hooks = {}
builder.hooks(hooks)
HttpClientBuilder.followRedirect()
Defines if redirect responses should be followed
automatically. Note that if a 303
is sent by
the server in response to any request type
(POST
, DELETE
, etc.), HttpClient will automatically
request the resource pointed to in the location
header via GET
. This is in accordance with
the spec.
You can optionally turn on this behavior also for
other redirect codes - see methodRewriting
:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.followRedirect(true)
HttpClientBuilder.followRedirects()
Alias for followRedirect()
.
HttpClientBuilder.maxRedirects()
If exceeded, the request will be aborted and a
MaxRedirectsError
will be thrown:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.maxRedirects(5)
HttpClientBuilder.cacheOptions()
Set the cache options of the request
from http-cache-semantics
:
import { HttpClientBuilder, type CacheOptions } from '@athenna/common'
const builder = new HttpClientBuilder()
const cacheOptions: CacheOptions = {
shared: true,
immutableMinTimeToLive: 1000,
}
builder.cacheOptions(cacheOptions)
HttpClientBuilder.cache()
A cache adapter instance for storing cached response data:
import { HttpClientBuilder } from '@athenna/common'
const cache = new Map()
const builder = new HttpClientBuilder()
builder.cache(cache)
HttpClientBuilder.throwHttpErrors()
Determines if an error is thrown for unsuccessful responses.
If this is disabled, requests that encounter an error
status code will be resolved with the response
instead
of throwing. This may be useful if you are checking for
resource availability and are expecting error responses:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.throwHttpErrors(false)
HttpClientBuilder.username()
Username for Basic Authentication:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.username('jlenon7')
HttpClientBuilder.password()
Password for Basic Authentication:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.username('12345')
HttpClientBuilder.allowGetBody()
Set this to true
to allow sending body for
the GET
method. However, the
HTTP/2 specification
says that An HTTP GET request includes request header fields and no payload body
,
therefore when using the HTTP/2 protocol this option will
have no effect. This option is only meant to interact with
non-compliant servers when you have no other choice.
- The RFC 7231 doesn't specify any particular behavior for the GET method having a payload, therefore it's considered an anti-pattern.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.allowGetBody(true)
HttpClientBuilder.methodRewriting()
Specifies if the HTTP request method should be
rewritten as GET
on redirects.
As the specification
prefers to rewrite the HTTP method only on 303
responses,
this is HttpClient's default behavior.
Setting methodRewriting
to true
will also rewrite 301
and 302
responses, as allowed by the spec. This is the
behavior followed by curl
and browsers.
- HttpClient never performs method rewriting on
307
and308
responses, as this is [explicitly prohibited by the specification.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.methodRewriting(true)
HttpClientBuilder.dnsLookupIpVersion()
Indicates which DNS record family to use.
Values:
undefined
: IPv4 (if present) or IPv64
: Only IPv46
: Only IPv6
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.dnsLookupIpVersion(6)
HttpClientBuilder.parseJson()
Define a function to parse JSON responses:
import Bourne from '@hapi/bourne'
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.parseJson(text => Bourne.parse(text))
HttpClientBuilder.stringifyJson()
Define a function to stringify JSON requests:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.stringifyJson(object => JSON.stringify(object))
HttpClientBuilder.retry()
An object representing limit
, calculateDelay
,
methods
, statusCodes
, maxRetryAfter
and
errorCodes
fields for maximum retry count, retry
handler, allowed methods, allowed status codes,
maximum Retry-After
time and allowed error codes.
Delays between retries counts with function
1000 * Math.pow(2, retry) + Math.random() * 100
,
where retry
is attempt number (starts from 1).
The calculateDelay
property is a function
that
receives an object with attemptCount
, retryOptions
,
error
and computedValue
properties for current retry
count, the retry options, error and default computed value.
The function must return a delay in milliseconds
(or a Promise resolving with it) (0
return value cancels
retry).
By default, it retries only on the specified methods, status codes, and on these network errors:
ETIMEDOUT
: One of the timeout limits were reached.ECONNRESET
: Connection was forcibly closed by a peer.EADDRINUSE
: Could not bind to any free port.ECONNREFUSED
: Connection was refused by the server.EPIPE
: The remote side of the stream being written has been closed.ENOTFOUND
: Couldn't resolve the hostname to an IP address.ENETUNREACH
: No internet connection.EAI_AGAIN
: DNS lookup timed out.
- If
maxRetryAfter
is set toundefined
, it will useoptions.timeout
. - If
Retry-After
header is greater thanmaxRetryAfter
, it will cancel the request.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.retry({
limit: 5,
calculateDelay: ({ attemptCount }) => {
return attemptCount * 1000
},
methods: ['GET'],
statusCodes: [[100, 199], 429, 404],
maxRetryAfter: 5000,
errorCodes: ['ETIMEDOUT'],
})
HttpClientBuilder.retryStrategy()
This method is just an alias to configure your own retry strategy.
The strategy function needs to return the delay between the execution count of each request, if the strategy function returns 0, the retry is canceled:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.retryStrategy((error, execCount, retryObject) => {
if (execCount === 3) {
return 0
}
return 2000
})
HttpClientBuilder.localAddress()
From http.RequestOptions
. The IP address used to send
the request from.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.localAddress('192.168.0.1')
HttpClientBuilder.createConnection()
Define a function used to retrieve a net.Socket
instance
when the agent
option is not used.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.createConnection(options => {
return new net.Socket(options)
})
HttpClientBuilder.https()
Define options to make https
requests:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.https({
rejectUnauthorized: false
})
HttpClientBuilder.encoding()
Encoding
to be used on setEncoding
of the response data.
To get a Buffer
, you
need to set responseType
to buffer
instead. Don't set
this option to null
.
- This doesn't affect streams! Instead, you need to do
HttpClientBuilder.stream(...).setEncoding(encoding)
.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.encoding('utf8')
HttpClientBuilder.resolveBodyOnly()
When set to true
the promise will return the response
body instead of the response object:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.resolveBodyOnly(true)
HttpClientBuilder.responseType()
The parsing method. The promise also has .text()
,
.json()
and .buffer()
methods which return another
HttClient promise for the parsed body.
It's like setting the options to {responseType: 'json', resolveBodyOnly: true}
but without affecting the main HttpClient promise.
- When using streams, this option is ignored.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.responseType('json')
HttpClientBuilder.pagination()
Set pagination options to your request:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.pagination({ countLimit: 100 })
HttpClientBuilder.setHost()
Set the host option:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.setHost(true)
HttpClientBuilder.maxHeaderSize()
Set the maxHeaderSize option.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.maxHeaderSize(1000)
HttpClientBuilder.enableUnixSockets()
Set the enableUnixSockets option.
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
builder.enableUnixSockets(true)
HttpClientBuilder.stream()
Execute the request and return as stream:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const stream = await builder.stream()
HttpClientBuilder.paginate()
Execute the request and return as paginated response:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const paginatedResponse = await builder.paginate()
HttpClientBuilder.request()
Execute the request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder.request()
HttpClientBuilder.get()
Execute a GET
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder.get()
HttpClientBuilder.post()
Execute a POST
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder
.url('https://localhost:3000/users')
.body({ name: 'lenon' })
.post()
// or
const response = await builder.post('https://localhost:3000/users', {
body: { name: 'lenon' }
})
HttpClientBuilder.put()
Execute a PUT
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder
.url('https://localhost:3000/users')
.body({ name: 'lenon' })
.put()
// or
const response = await builder.put('https://localhost:3000/users', {
body: { name: 'lenon' }
})
HttpClientBuilder.patch()
Execute a PATCH
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder
.url('https://localhost:3000/users')
.body({ name: 'lenon' })
.patch()
// or
const response = await builder.patch('https://localhost:3000/users', {
body: { name: 'lenon' }
})
HttpClientBuilder.delete()
Execute a DELETE
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder.delete()
HttpClientBuilder.head()
Execute a HEAD
request using all the options defined:
import { HttpClientBuilder } from '@athenna/common'
const builder = new HttpClientBuilder()
const response = await builder.head()
Is
Is::kindOf()
Return the kind of the argument:
import { Is } from '@athenna/common'
Is.kindOf('hello') // string
Is::Mac()
Validate if the current OS is a MacOS:
import { Is } from '@athenna/common'
Is.Mac() // true
Is::Linux()
Validate if the current OS is a distribution of Linux:
import { Is } from '@athenna/common'
Is.Linux() // true
Is::Windows()
Validate if the current OS is a Windows:
import { Is } from '@athenna/common'
Is.Windows() // true
Is::Module()
Validate if a path or File
instance is a module:
import { Is, File } from '@athenna/common'
Is.Module('./hello.js') // true
Is.Module('./hello.ts') // true
Is.Module('./hello.json') // false
Is.Module(new File('./hello.js')) // true
Is.Module(new File('./hello.ts')) // true
Is.Module(new File('./hello.json')) // false
Is::Uuid()
Validate if the value is a valid UUID v4:
import { Is } from '@athenna/common'
Is.Uuid('adm::a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') // true
Is.Uuid('adm::a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', { prefix: 'adm' }) // true
Is::Defined()
Validate if the value is defined:
import { Is } from '@athenna/common'
Is.Defined('Hello World') // true
Is::Json()
Validate if the value is a valid JSON string:
import { Is } from '@athenna/common'
Is.Json('{ "name": "João Lenon" }') // true
Is::Ip()
Validate if the value is a valid IP address:
import { Is } from '@athenna/common'
Is.Ip('192.168.0.1') // true
Is::Empty()
Validate if the value is empty:
import { Is } from '@athenna/common'
Is.Empty(undefined) // true
Is.Empty('') // true
Is.Empty([]) // true
Is.Empty({}) // true
Is::Cep()
Validate if the value is a valid Brazil CEP:
import { Is } from '@athenna/common'
Is.Cep('85869-676') // true
Is::Cpf()
Validate if the value is a valid Brazil Cpf:
import { Is } from '@athenna/common'
Is.Cpf('208.464.590-51') // true
Is::Cnpj()
Validate if the value is a valid Brazil Cnpj:
import { Is } from '@athenna/common'
Is.Cnpj('71.151.950/0001-65') // true
Is::Async()
Verify if a function is async:
import { Is } from '@athenna/common'
Is.Async(async () => {}) // true
Is.Async(async function () {}) // true
Is::Undefined()
Validate if the value is undefined:
import { Is } from '@athenna/common'
Is.Undefined(undefined) // true
Is::Null()
Validate if the value is null:
import { Is } from '@athenna/common'
Is.Null(null) // true
Is::Boolean()
Validate if the value is boolean:
import { Is } from '@athenna/common'
Is.Boolean(true) // true
Is::Buffer()
Validate if the value is Buffer:
import { Is } from '@athenna/common'
Is.Buffer(new Buffer()) // true
Is::Number()
Validate if the value is number:
import { Is } from '@athenna/common'
Is.Number(10) // true
Is::String()
Validate if the value is string:
import { Is } from '@athenna/common'
Is.String('Athenna is the best!') // true
Is::Object()
Validate if the value is Object:
import { Is } from '@athenna/common'
Is.Object({ hello: 'world!' }) // true
Is::Date()
Validate if the value is Date:
import { Is } from '@athenna/common'
Is.Date(new Date()) // true
Is::Array()
Validate if the value is array:
import { Is } from '@athenna/common'
Is.Array([1, 2, 3, 4, 5]) // true
Is::Regexp()
Validate if the value is Regexp:
import { Is } from '@athenna/common'
Is.Regexp(new RegExp()) // true
Is::Error()
Validate if the value is error:
import { Is } from '@athenna/common'
Is.Error(new Error()) // true
Is::Exception()
Validate if the value is Exception
or any variation that extends from it:
import { Is, Exception } from '@athenna/common'
Is.Exception(new Error()) // false
Is.Exception(new Exception()) // true
Is::Class()
Validate if the value is class:
import { Is } from '@athenna/common'
Is.Class(Is) // true
Is::Float()
Validate if the value is float:
import { Is } from '@athenna/common'
Is.Float(10.5) // true
Is::ArrayOfObjects()
Validate if the value is an array of objects:
import { Is } from '@athenna/common'
Is.ArrayOfObjects([{}, {}, {}]) // true
Json
Json::builder()
Create a new instance of ObjectBuilder:
import { Json } from '@athenna/common'
const builder = Json.builder({ referencedValues: false })
Json::copy()
Deep copy any object without memory reference:
import { Json } from '@athenna/common'
const object = { name: 'João Lenon' }
const copiedObject = Json.copy(object)
object.name = 'Victor Tesoura'
console.log(object.name === copiedObject.name) // false
Json::getJson()
Find all JSON values inside a string and return it:
import { Json } from '@athenna/common'
const string = 'Hello { "name": "João Lenon" }'
const arrayOfFoundJson = Json.getJson(string)
// ['{"name":"João Lenon"}']
Json::parse()
Safe parse a JSON string. If the JSON is not valid,
returns null
:
import { Json } from '@athenna/common'
const string = 'Hello { "name": "Lenon" }'
const json = Json.parse(string) // null
console.log('{ "name": "Lenon" }') // { name: 'Lenon' }
Json::observeChanges()
Observe any change done inside an object and execute some closure:
import { Json } from '@athenna/common'
const object = { name: 'Lenon' }
Json.observeChanges(object, (value) => {
console.log('Object changed!', value)
})
object.name = 'João Lenon'
Json::fillable()
Remove all keys from data that is not inside the array:
import { Json } from '@athenna/common'
const data = {
name: 'João Lenon',
age: 22,
role: 'admin',
deletedAt: new Date()
}
const parsedData = Json.fillable(data, ['name', 'age'])
// { name: 'João Lenon', age: 22 }
Json::get()
Get the object properties using dot notation:
import { Json } from '@athenna/common'
const object = {
name: 'João Lenon',
age: 22,
role: 'admin',
deletedAt: new Date(),
configs: {
theme: 'dark',
language: 'en'
}
}
Json.get(object, 'name') // João Lenon
Json.get(object, 'configs.theme') // dark
Json::omit()
Get all keys from an object omitting selected ones:
const omitted = Json.omit({ name: 'Lenon', age: 23 }, ['name'])
console.log(omitted) // { age: 23 }
Json::pick()
Pick only selected keys from an object:
const picked = Json.pick({ name: 'Lenon', age: 23 }, ['name'])
console.log(picked) // { name: 'Lenon' }
Module
Module::get()
Get the default module or the first exported module of a file:
export default {
name: 'João Lenon'
}
export const person = {
name: 'João Lenon'
}
import { Module } from '@athenna/common'
await Module.get(import('./default.js')) // { name: 'João Lenon' }
await Module.get(import('./object.js')) // { name: 'João Lenon' }
Module::getAll()
Get all exported modules of a path and return as array:
import { Module } from '@athenna/common'
const modules = [import('./object.js'), import('./default.js')]
await Module.getAll(modules)
// [{ name: 'João Lenon' }, { name: 'João Lenon' }]
Module::getFrom()
Same as Module::get()
method, but import from a path:
import { Module } from '@athenna/common'
await Module.getFrom('./default.js') // { name: 'João Lenon' }
await Module.getFrom('./object.js') // { name: 'João Lenon' }
Module::getAllFrom()
Same as Module::getAll()
method, but import from a path:
import { Module } from '@athenna/common'
const paths = ['./object.js', './default.js']
await Module.getAllFrom(paths)
// [{ name: 'João Lenon' }, { name: 'João Lenon' }]
Module::getWithAlias()
Get the default module or the first exported module of a file with alias:
export default class MyService {}
import { Module } from '@athenna/common'
const modules = [import('./MyService.js')]
const subAlias = 'App/Services'
await Module.getWithAlias(modules, subAlias)
// { alias: 'App/Services/MyService', module: [class MyService] }
Module::getAllWithAlias()
Get all exported modules of a path and return as array with alias:
import { Module } from '@athenna/common'
const modules = [import('./MyService.js')]
const subAlias = 'App/Services'
await Module.getAllWithAlias(modules, subAlias)
// [{ alias: 'App/Services/MyService', module: { name: 'João Lenon' } }]
Module::getFromWithAlias()
Same as Module::getWithAlias()
method, but import from a path:
import { Module } from '@athenna/common'
const paths = ['./MyService.js']
const subAlias = 'App/Services'
await Module.getAllWithAlias(paths, subAlias)
// [{ alias: 'App/Services/MyService', module: { name: 'João Lenon' } }]
Module::getAllFromWithAlias()
Same as Module::getAllWithAlias()
method, but import from a path:
import { Module } from '@athenna/common'
const paths = ['./MyService.js']
const subAlias = 'App/Services'
await Module.getAllWithAlias(paths, subAlias)
// [{ alias: 'App/Services/MyService', module: { name: 'João Lenon' } }]
Module::getAllJSFilesFrom()
Get all the Path.ext()
files from a path:
import { File, Path, Module } from '@athenna/common'
const files: File[] = await Module.getAllJSFilesFrom(Path.app())
Module::import()
Import a full path using the path href to ensure compatibility between OS's:
import { Module } from '@athenna/common'
const module = await Module.import('./MyService.js')
Module::safeImport()
Same as Module::import()
method but return null if the module does not exist:
import { Module } from '@athenna/common'
const module = await Module.safeImport('./notFound.js')
Module::resolve()
Resolve the module path by meta url and import it:
import { Module } from '@athenna/common'
const module = await Module.resolve('./MyService.js', import.meta.url)
You can add the following options to it as third argument:
const module = './MyService.js'
const parentURL = import.meta.url
const module = await Module.resolve(module, parentURL, {
// Automatically import the module instead of returning
// the module path resolved.
import: true,
// Automatically get the imported module using `Module.get()`
// method.
getModule: true
})
Module::createDirname()
Crete the old __dirname
property:
import { Module } from '@athenna/common'
const __dirname = Module.createDirname(import.meta.url)
Module::createFilename()
Crete the old __filename
property:
import { Module } from '@athenna/common'
const __filename = Module.createFilename(import.meta.url)
Module::createRequire()
Crete the old require()
function:
import { Module } from '@athenna/common'
const require = Module.createRequire(import.meta.url)
Number
Number::getHigher()
Get the higher number of an array of numbers:
import { Number } from '@athenna/common'
const higher = Number.getHigher([1, 2, 3, 4, 5]) // 5
Number::getKmRadius()
Get km radius between two coordinates:
import { Number } from '@athenna/common'
const centerCord = {
latitude: -25503207,
longitude: -545390592
}
const pointCord = {
latitude: -254957901,
longitude: -545671577
}
const radius = Number.getKmRadius(centerCord, pointCord)
// 5338.683217695541
Number::getLower()
Get the lower number of an array of numbers:
import { Number } from '@athenna/common'
const lower = Number.getLower([1, 2, 3, 4, 5]) // 1
Number::extractNumber()
Extract all numbers inside a string and return as a unique number:
import { Number } from '@athenna/common'
const string = 'Hello 123 World 456'
const number = Number.extractNumber(string) // 123456
Number::extractNumbers()
Extract all numbers inside a string:
import { Number } from '@athenna/common'
const string = 'Hello 123 World 456'
const numbers = Number.extractNumbers(string) // ['123', '456']
Number::argsAverage()
Get the average of all numbers in function arguments:
import { Number } from '@athenna/common'
const average = Number.argsAverage(1, 2, 3, 4, 5) // 3
Number::arrayAverage()
Get the average of all numbers in an array:
import { Number } from '@athenna/common'
const average = Number.argsAverage([1, 2, 3, 4, 5]) // 3
Number::randomIntFromInterval()
Generate a random integer number between two numbers:
import { Number } from '@athenna/common'
const random = Number.randomIntFromInterval(1, 10) // 5
ObjectBuilder
new ObjectBuilder()
Create a new instance of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder({
// Dont create memory referenced values.
referencedValues: false,
// Dont ignore null values when calling `set()` method.
ignoreNull: false,
// Ignore undefined values when calling `set()` method.
ignoreUndefined: true,
// Default value to set if value is undefined on calling `set()` method.
defaultValue: 'Hello World'
})
const key = 'name'
const value = 'João Lenon'
const defaultValue = 'User Name'
builder.set(key, value, defaultValue)
console.log(builder.get(key)) // João Lenon
builder.set(key, undefined)
console.log(builder.get(key)) // Hello World
ObjectBuilder.set()
Set a value to the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'João Lenon', age: 22, options: { role: 'admin' } })
console.log(builder.get())
// { name: 'João Lenon', age: 22, options: { role: 'admin' } }
Set a value to the object builder using dot notation:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
const defaultValue = null
builder.set('name', 'João Lenon', defaultValue)
builder.set('age', 22, defaultValue)
builder.set('deletedAt', undefined, defaultValue)
builder.set('options.role', 'admin', defaultValue)
console.log(builder.get())
// { name: 'João Lenon', age: 22, deletedAt: null, options: { role: 'admin' } }
ObjectBuilder.delete()
Delete a value from the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
builder.delete('name')
ObjectBuilder.get()
Get the value of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon', options: { role: 'admin' } })
console.log(builder.get('name')) // Lenon
console.log(builder.get('options.role')) // admin
ObjectBuilder.keys()
Get all the keys of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
console.log(builder.keys()) // ['name']
ObjectBuilder.values()
Get all the values of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
console.log(builder.values()) // ['Lenon']
ObjectBuilder.entries()
Get all the entries of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
console.log(builder.entries()) // [['name', 'Lenon']]
ObjectBuilder.forEachKey()
Iterate over all the keys of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
builder.forEachKey((key) => {
console.log(key) // name
})
ObjectBuilder.forEachValue()
Iterate over all the values of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
builder.forEachValue((value) => {
console.log(value) // Lenon
})
ObjectBuilder.forEachEntry()
Iterate over all the entries of the object builder:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
builder.forEachEntry(([key, value]) => {
console.log(key, value) // name Lenon
})
ObjectBuilder.is()
& ObjectBuilder.isNot()
Verify if the object key path is or not the same value:
import { ObjectBuilder } from '@athenna/common'
const builder = new ObjectBuilder()
builder.set({ name: 'Lenon' })
builder.is('name', 'Lenon') // true
builder.isNot('name', 'Lenon') // false