# Table of Contents - [Introduction (Preface) | AdonisJS Documentation](#introduction-preface-adonisjs-documentation) - [Introduction (Views & Templates) | AdonisJS Documentation](#introduction-views-templates-adonisjs-documentation) - [Encryption (Security) | AdonisJS Documentation](#encryption-security-adonisjs-documentation) - [Basic auth guard (Authentication) | AdonisJS Documentation](#basic-auth-guard-authentication-adonisjs-documentation) - [CORS (Security) | AdonisJS Documentation](#cors-security-adonisjs-documentation) - [EdgeJS (Views & Templates) | AdonisJS Documentation](#edgejs-views-templates-adonisjs-documentation) - [Database (Testing) | AdonisJS Documentation](#database-testing-adonisjs-documentation) - [Mocks & Fakes (Testing) | AdonisJS Documentation](#mocks-fakes-testing-adonisjs-documentation) - [Repl (Digging deeper) | AdonisJS Documentation](#repl-digging-deeper-adonisjs-documentation) - [Hashing (Security) | AdonisJS Documentation](#hashing-security-adonisjs-documentation) - [Command arguments (Ace commands) | AdonisJS Documentation](#command-arguments-ace-commands-adonisjs-documentation) - [Introduction (Testing) | AdonisJS Documentation](#introduction-testing-adonisjs-documentation) - [Console tests (Testing) | AdonisJS Documentation](#console-tests-testing-adonisjs-documentation) - [Custom auth guard (Authentication) | AdonisJS Documentation](#custom-auth-guard-authentication-adonisjs-documentation) - [Prompts (Ace commands) | AdonisJS Documentation](#prompts-ace-commands-adonisjs-documentation) - [Introduction (Ace commands) | AdonisJS Documentation](#introduction-ace-commands-adonisjs-documentation) - [Command flags (Ace commands) | AdonisJS Documentation](#command-flags-ace-commands-adonisjs-documentation) - [Browser tests (Testing) | AdonisJS Documentation](#browser-tests-testing-adonisjs-documentation) - [Securing SSR apps (Security) | AdonisJS Documentation](#securing-ssr-apps-security-adonisjs-documentation) - [HTTP tests (Testing) | AdonisJS Documentation](#http-tests-testing-adonisjs-documentation) - [Social authentication (Authentication) | AdonisJS Documentation](#social-authentication-authentication-adonisjs-documentation) - [Transmit (Digging deeper) | AdonisJS Documentation](#transmit-digging-deeper-adonisjs-documentation) - [Terminal UI (Ace commands) | AdonisJS Documentation](#terminal-ui-ace-commands-adonisjs-documentation) - [Creating commands (Ace commands) | AdonisJS Documentation](#creating-commands-ace-commands-adonisjs-documentation) - [Drive (Digging deeper) | AdonisJS Documentation](#drive-digging-deeper-adonisjs-documentation) - [Locks (Digging deeper) | AdonisJS Documentation](#locks-digging-deeper-adonisjs-documentation) - [Cache (Digging deeper) | AdonisJS Documentation](#cache-digging-deeper-adonisjs-documentation) - [Events (References) | AdonisJS Documentation](#events-references-adonisjs-documentation) - [Exceptions (References) | AdonisJS Documentation](#exceptions-references-adonisjs-documentation) - [Edge helpers and tags (References) | AdonisJS Documentation](#edge-helpers-and-tags-references-adonisjs-documentation) - [Commands (References) | AdonisJS Documentation](#commands-references-adonisjs-documentation) - [Logger (Digging deeper) | AdonisJS Documentation](#logger-digging-deeper-adonisjs-documentation) - [Emitter (Digging deeper) | AdonisJS Documentation](#emitter-digging-deeper-adonisjs-documentation) - [Health checks (Digging deeper) | AdonisJS Documentation](#health-checks-digging-deeper-adonisjs-documentation) - [Rate limiting (Security) | AdonisJS Documentation](#rate-limiting-security-adonisjs-documentation) - [Helpers (References) | AdonisJS Documentation](#helpers-references-adonisjs-documentation) - [I18n (Digging deeper) | AdonisJS Documentation](#i18n-digging-deeper-adonisjs-documentation) - [Mail (Digging deeper) | AdonisJS Documentation](#mail-digging-deeper-adonisjs-documentation) - [Inertia (Views & Templates) | AdonisJS Documentation](#inertia-views-templates-adonisjs-documentation) --- # Introduction (Preface) | AdonisJS Documentation Toggle docs menu Introduction [](https://docs.adonisjs.com/guides/preface/introduction#introduction) Introduction =================================================================================== ### Getting started * [Screencasts](https://adocasts.com/?utm_source=docs.adonisjs.com) * [AdonisJS VSCode extension](https://marketplace.visualstudio.com/items?itemName=jripouteau.adonis-vscode-extension) * [Edge VSCode extension](https://marketplace.visualstudio.com/items?itemName=AdonisJS.vscode-edge) * [Japa VSCode extension](https://marketplace.visualstudio.com/items?itemName=jripouteau.japa-vscode) ### Community and Help * [Discord server](https://discord.gg/vDcEjq6) * [X (Formerly Twitter)](https://twitter.com/adonisframework) * [GitHub discussions](https://github.com/orgs/adonisjs/discussions) * [Packages ecosystem](https://packages.adonisjs.com/) * [Priority Support](https://adonisjs.com/contact) ### Translations The following translations are not official and managed by the community * [Japanese](https://adonisjs-docs-ja.vercel.app/) [](https://docs.adonisjs.com/guides/preface/introduction#what-is-adonisjs) What is AdonisJS? -------------------------------------------------------------------------------------------- AdonisJS is a TypeScript-first web framework for Node.js. You can use it to create a full-stack web application or a JSON API server. At the fundamental level, AdonisJS [provides structure to your applications](https://docs.adonisjs.com/guides/getting-started/folder-structure) , configures a [seamless TypeScript development environment](https://docs.adonisjs.com/guides/concepts/typescript-build-process) , configures [HMR](https://docs.adonisjs.com/guides/concepts/hot-module-replacement) for your backend code, and offers a vast collection of well-maintained and extensively documented packages. We envision teams using AdonisJS **spending less time** on trivial decisions like cherry-picking npm packages for every minor feature, writing glue code, debating for the perfect folder structure, and **spending more time** delivering real-world features critical for the business needs. ### [](https://docs.adonisjs.com/guides/preface/introduction#frontend-agnostic) Frontend agnostic AdonisJS focuses on the backend and lets you choose the frontend stack of your choice. If you like to keep things simple, pair AdonisJS with a [traditional template engine](https://docs.adonisjs.com/guides/views-and-templates/introduction) to generate static HTML on the server, create a JSON API for your frontend Vue/React application or use [Inertia](https://docs.adonisjs.com/guides/views-and-templates/inertia) to make your favorite frontend framework work together in perfect harmony. AdonisJS aims to provide you with batteries to create a robust backend application from scratch. Be it sending emails, validating user input, performing CRUD operations, or authenticating users. We take care of it all. ### [](https://docs.adonisjs.com/guides/preface/introduction#modern-and-type-safe) Modern and Type-safe AdonisJS is built on top of modern JavaScript primitives. We use ES modules, Node.js sub-path import aliases, SWC for executing TypeScript source, and Vite for assets bundling. Also, TypeScript plays a considerable role when designing the framework's APIs. For example, AdonisJS has: * [Type-safe event emitter](https://docs.adonisjs.com/guides/digging-deeper/emitter#making-events-type-safe) * [Type-safe environment variables](https://docs.adonisjs.com/guides/getting-started/environment-variables) * [Type-safe validation library](https://docs.adonisjs.com/guides/basics/validation) ### [](https://docs.adonisjs.com/guides/preface/introduction#embracing-mvc) Embracing MVC AdonisJS embraces the classic MVC design pattern. You start by defining the routes using the functional JavaScript API, bind controllers to them and write logic to handle the HTTP requests within the controllers. start/routes.ts Copy code to clipboard import router from '@adonisjs/core/services/router' const PostsController = () => import('#controllers/posts_controller') router.get('posts', [PostsController, 'index']) Controllers can use models to fetch data from the database and render a view (aka template) as a response. app/controllers/posts\_controller.ts Copy code to clipboard import Post from '#models/post' import type { HttpContext } from '@adonisjs/core/http' export default class PostsController { async index({ view }: HttpContext) { const posts = await Post.all() return view.render('pages/posts/list', { posts }) } } If you are building an API server, you can replace the view layer with a JSON response. But, the flow of handling and responding to the HTTP requests remains the same. app/controllers/posts\_controller.ts Copy code to clipboard import Post from '#models/post' import type { HttpContext } from '@adonisjs/core/http' export default class PostsController { async index({ view }: HttpContext) { const posts = await Post.all() return view.render('pages/posts/list', { posts }) /** * Posts array will be serialized to JSON * automatically. */ return posts } } [](https://docs.adonisjs.com/guides/preface/introduction#guides-assumptions) Guides assumptions ----------------------------------------------------------------------------------------------- The AdonisJS documentation is written as a reference guide, covering the usage and the API of several packages and modules maintained by the core team. **The guide does not teach you how to build an application from scratch**. If you are looking for a tutorial, we recommend starting your journey with [Adocasts](https://adocasts.com/) . Tom (the creator of Adocasts) has created some high quality screencasts, helping you to take the first steps with AdonisJS. With that said, the documentation extensively covers the usage of available modules and the inner workings of the framework. [](https://docs.adonisjs.com/guides/preface/introduction#recent-releases) Recent releases ----------------------------------------------------------------------------------------- Following is the list of recent releases. [Click here](https://docs.adonisjs.com/guides/preface/releases) to view all the releases. | Package | Change | Released on | | --- | --- | --- | | [`@adonisjs/repl`](https://github.com/adonisjs/repl/releases/tag/v4.1.1) | Update dependencies. | Aug 15, 2025 | | [`@adonisjs/eslint-config`](https://github.com/adonisjs/eslint-config/releases/tag/v2.1.2) | Remove @stylistic/eslint-plugin-ts in favor of @stylistic/eslint-plugin. | Aug 8, 2025 | | [`@adonisjs/lucid`](https://github.com/adonisjs/lucid/releases/tag/v21.8.0) | Use natural sort when executing seeders and display migration filename during error in compact mode. | Jul 22, 2025 | | [`@adonisjs/cache`](https://github.com/adonisjs/cache/releases/tag/v1.3.0) | \`cache:prune\` and database migration. | Jul 20, 2025 | | [`@adonisjs/i18n`](https://github.com/adonisjs/i18n/releases/tag/v2.2.2) | Resolve otherField from translations. | Jul 16, 2025 | [](https://docs.adonisjs.com/guides/preface/introduction#sponsors) Sponsors --------------------------------------------------------------------------- List of individuals and companies publicly sponsoring [Harminder Virk on GitHub](https://github.com/sponsors/thetutlage) . If you are sponsoring and cannot see your name, ensure your [sponsorship is not private](https://docs.github.com/en/sponsors/sponsoring-open-source-contributors/managing-your-sponsorship#managing-the-privacy-setting-for-your-sponsorship) . ### Featured sponsors [](https://route4me.com/?utm_source=adonisjs.com) [](https://ezycourse.com/?utm_source=adonisjs.com) [](https://meteor.software/g6h?utm_source=adonisjs.com) [](https://www.lambdatest.com/?utm_source=adonisjs.com) [](https://relancer.com/?utm_source=adonisjs.com) [Your logo](https://github.com/sponsors/thetutlage/sponsorships?tier_id=379745&preview=false&pay_prorated=false) ### Gold sponsors [](https://github.com/jwahdatehagh "Jalil Wahdatehagh") [](https://github.com/route4me "Route4Me Route Planner") [](https://github.com/zakodium "Zakodium") [](https://github.com/hkmsadek "Sadek Hossain") [](https://github.com/LambdaTest-Inc "LambdaTest") [](https://github.com/CloudByGalaxy "Galaxy Cloud") ### Silver sponsors [](https://github.com/carlmathisen "Carl Mathisen") [](https://github.com/connorford2 "Connor Ford") [](https://github.com/tomgobich "Tom Gobich") [](https://github.com/kerwanp "Martin Paucot") [](https://github.com/madebyoutloud "Outloud") ### Sponsors [](https://github.com/marcoleong "Marco Leong") [](https://github.com/tgriesser "Tim Griesser") [](https://github.com/p3drosola "Pedro Solá") [](https://github.com/eduwass "Edu Wass") [](https://github.com/ericpugh "Eric Pugh") [](https://github.com/batosai "Jeremy Chaufourier") [](https://github.com/filipebraida "Filipe Braida") [](https://github.com/jarle "Jarle Mathiesen") [](https://github.com/bnoufel "Benjamin Noufel") [](https://github.com/McSneaky "McSneaky") [](https://github.com/andrew-connell "Andrew Connell") [](https://github.com/gunhanselas "gunhanselas") [](https://github.com/CoSNaYe "Ian Chen") [](https://github.com/nativadesenvolvimento "nativadesenvolvimento") [](https://github.com/DavideCarvalho "Davi de Carvalho") [](https://github.com/neighbourhoodie "Neighbourhoodie Software") [](https://github.com/philippe-desplats "Philippe DESPLATS") [](https://github.com/capoia "capoia") [](https://github.com/itsemirhanengin "Emirhan Engin") [](https://github.com/luffynando "Fernando Isidro Luna") [](https://github.com/jonasthiesen "Jonas Thiesen") [](https://github.com/QuentinRoggy "Quentin Roggy") [](https://github.com/manomintis "Boris Abramov") [](https://github.com/smithbm2316 "Ben Smith") [](https://github.com/muco-rolle "Trésor Muco") [](https://github.com/diego-sepulveda-lavin "Diego Sepulveda") [](https://github.com/JNantet "Julien Nantet") [](https://github.com/clement-mistral "Clément Mistral") [](https://github.com/mhmdmrabet "Mohamed M'rabet") [](https://github.com/ThibaultPointurier "Thibault Pointurier") [](https://github.com/coddess-development "coddess-development") [](https://github.com/roseratugo "Ugo ROSERAT") [](https://github.com/BretRen "BretRen") [](https://github.com/letsdial "Let's Dial") ### Backers [](https://github.com/Zizaco "Luiz Alves") [](https://github.com/jsilva74 "José Silva Jr.") [](https://github.com/dunhamjared "Jared Dunham") [](https://github.com/moistpope "moistpope") [](https://github.com/macieklad "Maciej Ładoś") [](https://github.com/juael "Julian Dawson") [](https://github.com/igortrinidad "Igor Trindade") [](https://github.com/tavaresgerson "Tavares") [](https://github.com/vartiainen "vartiainen") [](https://github.com/FinotiLucas "Lucas Finoti") [](https://github.com/ChristopherFritz "ChristopherFritz") [](https://github.com/syntaxfm "Syntax") ### Past sponsors [](https://github.com/furatamasensei "Yura") [](https://github.com/Professor-MAD "Professor MAD") [](https://github.com/bitkidd "bitkidd") [](https://github.com/DominusKelvin "DominusKelvin") [](https://github.com/adegbengaagoro "adegbengaagoro") [](https://github.com/DanielCoulbourne "DanielCoulbourne") [](https://github.com/microeinhundert "microeinhundert") [](https://github.com/hecht-a "hecht-a") [](https://github.com/frangambin1 "frangambin1") [](https://github.com/baekilda "baekilda") [](https://github.com/roonie007 "roonie007") [](https://github.com/GabrielNBDS "GabrielNBDS") [](https://github.com/MatusMockor "MatusMockor") [](https://github.com/AlexandreGerault "AlexandreGerault") [](https://github.com/wavedeck "wavedeck") [](https://github.com/ungerpeter "ungerpeter") [](https://github.com/ammezie "ammezie") [](https://github.com/ArthurFranckPat "ArthurFranckPat") [](https://github.com/hsoumi-technology "hsoumi-technology") [](https://github.com/xqsit94 "xqsit94") [](https://github.com/natapolc "natapolc") [](https://github.com/brunoziie "brunoziie") [](https://github.com/devKnaryo "devKnaryo") [](https://github.com/Dolu89 "Dolu89") [](https://github.com/augustosamame "augustosamame") [](https://github.com/PandaGuerrier "PandaGuerrier") [](https://github.com/flozdra "flozdra") [](https://github.com/webNeat "webNeat") [](https://github.com/watzon "watzon") [](https://github.com/xinha-sh "xinha-sh") [](https://github.com/cyanicr "cyanicr") [](https://github.com/Forsties08 "Forsties08") [](https://github.com/urloki "urloki") [](https://github.com/itsabdessalam "itsabdessalam") [](https://github.com/alexandrecoin "alexandrecoin") [](https://github.com/hariesnurikhwan "hariesnurikhwan") [](https://github.com/Morganjackson "Morganjackson") [](https://github.com/gabrielmaialva33 "gabrielmaialva33") [](https://github.com/karakunai "karakunai") [](https://github.com/anthonykoch "anthonykoch") [](https://github.com/tom-brulin "tom-brulin") [](https://github.com/flowdev777 "flowdev777") [](https://github.com/franciskouaho "franciskouaho") [](https://github.com/vnay92 "vnay92") [](https://github.com/aniftyco "aniftyco") [](https://github.com/BSN4 "BSN4") [](https://github.com/gabrielbuzziv "gabrielbuzziv") [](https://github.com/Secular12 "Secular12") [](https://github.com/petchpool "petchpool") [](https://github.com/lmd-dev "lmd-dev") [](https://github.com/KeqinHQ "KeqinHQ") [](https://github.com/prisca-c "prisca-c") [](https://github.com/Sonny93 "Sonny93") [](https://github.com/maamri95 "maamri95") [](https://github.com/ninjaparade "ninjaparade") [](https://github.com/sefidary "sefidary") [](https://github.com/bartonhammond "bartonhammond") [](https://github.com/CloudGakkai "CloudGakkai") [](https://github.com/akatora28 "akatora28") [](https://github.com/good-tape "good-tape") [](https://github.com/PietroDSK "PietroDSK") [](https://github.com/FernandoEncina "FernandoEncina") [](https://github.com/iDroid-dev "iDroid-dev") [](https://github.com/whayes75 "whayes75") [](https://github.com/itsjustanks "itsjustanks") [](https://github.com/Untel "Untel") [](https://github.com/fadeaway "fadeaway") [](https://github.com/FragRappy "FragRappy") [](https://github.com/ClementLaval "ClementLaval") [](https://github.com/Raesta "Raesta") [](https://github.com/yungsilva "yungsilva") [](https://github.com/maximedeoliveira "maximedeoliveira") [](https://github.com/Segfaultd "Segfaultd") [](https://github.com/leereichardt "leereichardt") [](https://github.com/devanandb "devanandb") [](https://github.com/meyer0x "meyer0x") [](https://github.com/Yofou "Yofou") [](https://github.com/ademoverflow "ademoverflow") [](https://github.com/olivier-lucas "olivier-lucas") [](https://github.com/romainrhd "romainrhd") [](https://github.com/tomgscs "tomgscs") [](https://github.com/adokce "adokce") [](https://github.com/JasonVerk "JasonVerk") [](https://github.com/denisdenes "denisdenes") [](https://github.com/Geek2tech "Geek2tech") [](https://github.com/luzzbe "luzzbe") [](https://github.com/eliemoriceau "eliemoriceau") [](https://github.com/flupine "flupine") [](https://github.com/chaptiste "chaptiste") [](https://github.com/s1nyx "s1nyx") [](https://github.com/mkevison "mkevison") [](https://github.com/qkab78 "qkab78") [](https://github.com/lncitador "lncitador") [](https://github.com/dmdboi "dmdboi") [](https://github.com/arkerone "arkerone") [](https://github.com/MeLoBeats "MeLoBeats") [](https://github.com/brngrs "brngrs") [](https://github.com/GermainMichaud "GermainMichaud") [](https://github.com/FrenchMajesty "FrenchMajesty") [](https://github.com/Flamystal "Flamystal") [](https://github.com/Renaud-HUSSON "Renaud-HUSSON") [](https://github.com/muniter "muniter") [](https://github.com/kubilaysalih "kubilaysalih") [](https://github.com/ivan-prats "ivan-prats") [](https://github.com/WebShapedBiz "WebShapedBiz") [](https://github.com/DennisKraaijeveld "DennisKraaijeveld") [](https://github.com/Obapelumi "Obapelumi") [](https://github.com/usagar80 "usagar80") [](https://github.com/enkot "enkot") [](https://github.com/juni0r "juni0r") [](https://github.com/roggerJt "roggerJt") [](https://github.com/lucas-jacques "lucas-jacques") [](https://github.com/dimas-cyriaco "dimas-cyriaco") [](https://github.com/keef3ar "keef3ar") [](https://github.com/mr-feek "mr-feek") [](https://github.com/pranab-martiniapp "pranab-martiniapp") [](https://github.com/forgiv "forgiv") [](https://github.com/BrodyMacfarlane "BrodyMacfarlane") [](https://github.com/bakaburg24 "bakaburg24") [](https://github.com/jikkai "jikkai") [](https://github.com/OmnizTwink "OmnizTwink") [](https://github.com/jamesbuch "jamesbuch") [](https://github.com/mrfelipemartins "mrfelipemartins") [](https://github.com/AlexGalhardo "AlexGalhardo") [](https://github.com/mayavadeesoft "mayavadeesoft") [](https://github.com/8mist "8mist") [](https://github.com/HamzaDams "HamzaDams") [](https://github.com/JusCezari "JusCezari") [](https://github.com/branrgx "branrgx") [](https://github.com/brandon-beacher "brandon-beacher") [](https://github.com/MrGlox "MrGlox") [](https://github.com/c4n4r "c4n4r") [](https://github.com/MateoSRQ "MateoSRQ") [](https://github.com/capibawa "capibawa") [](https://github.com/wmmariano "wmmariano") [](https://github.com/autumn-witch "autumn-witch") [](https://github.com/jtanudjaja "jtanudjaja") [](https://github.com/studio-intellisoft "studio-intellisoft") [](https://github.com/cloleb "cloleb") [](https://github.com/davidharting "davidharting") [](https://github.com/danygiguere "danygiguere") [](https://github.com/adocasts "adocasts") [](https://github.com/redeemefy "redeemefy") [](https://github.com/WilliamTraoreee "WilliamTraoreee") [](https://github.com/tecrovalsdevelop "tecrovalsdevelop") [](https://github.com/jstoone "jstoone") [](https://github.com/ymir95 "ymir95") [](https://github.com/shiny "shiny") [](https://github.com/SX-3 "SX-3") [](https://github.com/AlexTorrenti "AlexTorrenti") [](https://github.com/ScribeSavant "ScribeSavant") [](https://github.com/Fabio-web "Fabio-web") [](https://github.com/jerome-dm "jerome-dm") [](https://github.com/tleneveu "tleneveu") [](https://github.com/TranspaClean "TranspaClean") [](https://github.com/marcelodelta "marcelodelta") [](https://github.com/damienguezou "damienguezou") [](https://github.com/CodingDive "CodingDive") [](https://github.com/LeadcodeDev "LeadcodeDev") [](https://github.com/aarhusgregersen "aarhusgregersen") [](https://github.com/langovoi "langovoi") [](https://github.com/nfadili "nfadili") [](https://github.com/Arania "Arania") [](https://github.com/CoreyVincent "CoreyVincent") [](https://github.com/rchrdkvcs "rchrdkvcs") [](https://github.com/mytraiteur "mytraiteur") [](https://github.com/crbast "crbast") [](https://github.com/mathieuh "mathieuh") [](https://github.com/ivanlobster "ivanlobster") [](https://github.com/nickgravel "nickgravel") [](https://github.com/fareeda0 "fareeda0") [](https://github.com/ndianabasi "ndianabasi") [](https://github.com/lolabyleaf "lolabyleaf") [](https://github.com/gatarelib "gatarelib") [](https://github.com/laxadev "laxadev") [](https://github.com/rbartholomay "rbartholomay") [](https://github.com/armgitaar "armgitaar") [](https://github.com/pb03 "pb03") [](https://github.com/victormolla14 "victormolla14") [](https://github.com/richardk80 "richardk80") [](https://github.com/hymns "hymns") [](https://github.com/satrio-pamungkas "satrio-pamungkas") [](https://github.com/andipyk "andipyk") [](https://github.com/agusm "agusm") [](https://github.com/Porrapat "Porrapat") [](https://github.com/LeCoupa "LeCoupa") [](https://github.com/stanosmith "stanosmith") [](https://github.com/saiganov "saiganov") [](https://github.com/hdouanla "hdouanla") [](https://github.com/Joan1590 "Joan1590") [](https://github.com/EdwinRomelta "EdwinRomelta") [](https://github.com/ramsesmoreno "ramsesmoreno") [](https://github.com/fredrickreuben "fredrickreuben") [](https://github.com/tonimoreiraa "tonimoreiraa") [](https://github.com/phongpanotdang2000 "phongpanotdang2000") [](https://github.com/armandojes "armandojes") [](https://github.com/timganter "timganter") [](https://github.com/dogpaw-io "dogpaw-io") [](https://github.com/peguimasid "peguimasid") [](https://github.com/AugustinLegrand "AugustinLegrand") [](https://github.com/jpamittan "jpamittan") [](https://github.com/wukong0111 "wukong0111") [](https://github.com/thehypergraph "thehypergraph") [](https://github.com/FreelancePayrollSystems "FreelancePayrollSystems") [](https://github.com/maukoese "maukoese") [](https://github.com/antoineLZCH "antoineLZCH") [](https://github.com/Christopher2K "Christopher2K") [](https://github.com/josejames "josejames") [](https://github.com/Kokoskun "Kokoskun") [](https://github.com/krthr "krthr") [](https://github.com/glprog "glprog") [](https://github.com/darshithedpara "darshithedpara") [](https://github.com/HappyLDE "HappyLDE") [](https://github.com/nahumrosillo "nahumrosillo") [](https://github.com/Xstoudi "Xstoudi") [](https://github.com/SpatzlHD "SpatzlHD") [](https://github.com/theoludwig "theoludwig") [](https://github.com/aditya-wiguna "aditya-wiguna") [](https://github.com/mbagalwa "mbagalwa") [](https://github.com/avenikolay "avenikolay") [](https://github.com/sooluh "sooluh") [](https://github.com/tokalink "tokalink") [](https://github.com/Luis-Sejer "Luis-Sejer") [](https://github.com/radiumrasheed "radiumrasheed") [](https://github.com/kennymark "kennymark") [](https://github.com/herquiloidehele "herquiloidehele") [](https://github.com/arnaudfribault "arnaudfribault") [](https://github.com/phatmann "phatmann") [](https://github.com/rafael-neri "rafael-neri") [](https://github.com/ldivrala "ldivrala") [](https://github.com/johngerome "johngerome") [](https://github.com/monojson "monojson") [](https://github.com/CheatCodeSam "CheatCodeSam") [](https://github.com/senthilpk "senthilpk") [](https://github.com/afrijaldz "afrijaldz") [](https://github.com/sinanusluer "sinanusluer") [](https://github.com/PangFive "PangFive") [](https://github.com/darkmoon-dev "darkmoon-dev") [](https://github.com/sorenzo "sorenzo") [](https://github.com/diogops "diogops") [](https://github.com/Ziut3k-dev "Ziut3k-dev") [](https://github.com/sizovs "sizovs") [](https://github.com/mcordoba "mcordoba") [](https://github.com/LucasCavalheri "LucasCavalheri") [](https://github.com/ahmerds "ahmerds") [](https://github.com/adufr "adufr") [](https://github.com/leofmarciano "leofmarciano") [](https://github.com/etmartinkazoo "etmartinkazoo") [](https://github.com/RomainLanz "RomainLanz") [](https://github.com/Intraweb-Org "Intraweb-Org") [](https://github.com/glomepe "glomepe") [](https://github.com/ApixPlay "ApixPlay") [](https://github.com/KevinRouchut "KevinRouchut") [](https://github.com/stephenhuh "stephenhuh") [](https://github.com/cfu288 "cfu288") [](https://github.com/0xtlt "0xtlt") [](https://github.com/tky0065 "tky0065") [](https://github.com/loichauseux "loichauseux") [](https://github.com/codec4 "codec4") * * * [Preface\ \ FAQs](https://docs.adonisjs.com/guides/preface/faqs) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/preface/introduction.md) --- # Introduction (Views & Templates) | AdonisJS Documentation Toggle docs menu Introduction [](https://docs.adonisjs.com/guides/views-and-templates/introduction#views-and-templates) Views and Templates ============================================================================================================= AdonisJS is an excellent fit for creating traditional server-rendered applications in Node.js. If you enjoy the simplicity of using a backend template engine that outputs HTML without any overhead of Virtual DOM and build tools, then this guide is for you. The typical workflow of a server-rendered application in AdonisJS looks as follows. * Choose a template engine to render HTML dynamically. * Use [Vite](https://docs.adonisjs.com/guides/basics/vite) for bundling CSS and frontend JavaScript. * Optionally, you can opt for libraries like [HTMX](https://htmx.org/) or [Unpoly](https://unpoly.com/) to progressively enhance your application and navigate like a SPA. The AdonisJS core team has created a framework-agnostic template engine called [Edge.js](https://edgejs.dev/) but does not force you to use it. You can use any other template engine you would like inside an AdonisJS application. [](https://docs.adonisjs.com/guides/views-and-templates/introduction#popular-options) Popular options ----------------------------------------------------------------------------------------------------- Following is the list of popular template engines you can use inside an AdonisJS application (just like any other Node.js application). * [**EdgeJS**](https://edgejs.dev/) is a simple, modern, and batteries included template engine created and maintained by the AdonisJS core team for Node.js. * [**Pug**](https://pugjs.org/) is a template engine heavily influenced by Haml. * [**Nunjucks**](https://mozilla.github.io/nunjucks) is a rich feature template engine inspired by Jinja2. [](https://docs.adonisjs.com/guides/views-and-templates/introduction#hybrid-applications) Hybrid applications ------------------------------------------------------------------------------------------------------------- AdonisJS is also a great fit for creating hybrid applications that render HTML on the server and then hydrate your JavaScript on the client. This approach is popular among developers who want to use `Vue`, `React`, `Svelte`, `Solid`, or others for building interactive user interfaces but still want a full backend stack to handle server-side concerns. In this case, AdonisJS provide a first-class support for using [InertiaJS](https://docs.adonisjs.com/guides/views-and-templates/inertia) to bridge the gap between your frontend and backend. * * * [Security\ \ Rate limiting](https://docs.adonisjs.com/guides/security/rate-limiting) [Views & Templates\ \ EdgeJS](https://docs.adonisjs.com/guides/views-and-templates/edgejs) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/views-and-templates/introduction.md) --- # Encryption (Security) | AdonisJS Documentation Toggle docs menu Encryption [](https://docs.adonisjs.com/guides/security/encryption#encryption) Encryption ============================================================================== Using the encryption service, you may encrypt and decrypt values in your application. The encryption is based on the [aes-256-cbc algorithm](https://www.n-able.com/blog/aes-256-encryption-algorithm) , and we append an integrity hash (HMAC) to the final output to prevent value tampering. The `encryption` service uses the `appKey` stored inside the `config/app.ts` file as the secret to encrypt the values. * It is recommended to keep the `appKey` secure and inject it into your application using [environment variables](https://docs.adonisjs.com/guides/getting-started/environment-variables) . Anyone with access to this key can decrypt values. * The key should be at least 16 characters long and have a cryptographically secure random value. You may generate the key using the `node ace generate:key` command. * If you decide to change the key later, you will not be able to decrypt existing values. This will result in invalidating existing cookies and user sessions. [](https://docs.adonisjs.com/guides/security/encryption#encrypting-values) Encrypting values -------------------------------------------------------------------------------------------- You may encrypt values using the `encryption.encrypt` method. The method accepts the value to encrypt and an optional time duration after which to consider the value expired. Copy code to clipboard import encryption from '@adonisjs/core/services/encryption' const encrypted = encryption.encrypt('hello world') Define a time duration after which the value will be considered expired and cannot be decrypted. Copy code to clipboard const encrypted = encryption.encrypt('hello world', '2 hours') [](https://docs.adonisjs.com/guides/security/encryption#decrypting-values) Decrypting values -------------------------------------------------------------------------------------------- Encrypted values can be decrypted using the `encryption.decrypt` method. The method accepts the encrypted value as the first argument. Copy code to clipboard import encryption from '@adonisjs/core/services/encryption' encryption.decrypt(encryptedValue) [](https://docs.adonisjs.com/guides/security/encryption#supported-data-types) Supported data types -------------------------------------------------------------------------------------------------- The value given to the `encrypt` method is serialized to a string using `JSON.stringify`. Therefore, you can use the following JavaScript data types. * string * number * bigInt * boolean * null * object * array Copy code to clipboard import encryption from '@adonisjs/core/services/encryption' // Object encryption.encrypt({ id: 1, fullName: 'virk', }) // Array encryption.encrypt([1, 2, 3, 4]) // Boolean encryption.encrypt(true) // Number encryption.encrypt(10) // BigInt encryption.encrypt(BigInt(10)) // Data objects are converted to ISO string encryption.encrypt(new Date()) [](https://docs.adonisjs.com/guides/security/encryption#using-custom-secret-keys) Using custom secret keys ---------------------------------------------------------------------------------------------------------- You can create an [instance of the Encryption class](https://github.com/adonisjs/encryption/blob/main/src/encryption.ts) directly to use custom secret keys. Copy code to clipboard import { Encryption } from '@adonisjs/core/encryption' const encryption = new Encryption({ secret: 'alongrandomsecretkey', }) * * * [Security\ \ Authorization](https://docs.adonisjs.com/guides/security/authorization) [Security\ \ Hashing](https://docs.adonisjs.com/guides/security/hashing) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/security/encryption.md) --- # Basic auth guard (Authentication) | AdonisJS Documentation Toggle docs menu Basic auth guard [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#basic-authentication-guard) Basic authentication guard ========================================================================================================================== The basic auth guard is an implementation of the [HTTP authentication framework](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) , in which the client must pass the user credentials as a base64 encoded string via the `Authorization` header. The server allows the request if the credentials are valid. Otherwise, a web-native prompt is displayed to re-enter the credentials. [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#configuring-the-guard) Configuring the guard ---------------------------------------------------------------------------------------------------------------- The authentication guards are defined inside the `config/auth.ts` file. You can configure multiple guards inside this file under the `guards` object. Copy code to clipboard import { defineConfig } from '@adonisjs/auth' import { basicAuthGuard, basicAuthUserProvider } from '@adonisjs/auth/basic_auth' const authConfig = defineConfig({ default: 'basicAuth', guards: { basicAuth: basicAuthGuard({ provider: basicAuthUserProvider({ model: () => import('#models/user'), }), }) }, }) export default authConfig The `basicAuthGuard` method creates an instance of the [BasicAuthGuard](https://github.com/adonisjs/auth/blob/main/modules/basic_auth_guard/guard.ts) class. It accepts a user provider that can be used to find users during authentication. The `basicAuthUserProvider` method creates an instance of the [BasicAuthLucidUserProvider](https://github.com/adonisjs/auth/blob/main/modules/basic_auth_guard/user_providers/lucid.ts) class. It accepts a reference to the model to use for verifying user credentials. [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#preparing-the-user-model) Preparing the User model ---------------------------------------------------------------------------------------------------------------------- The model (`User` model in this example) configured with the `basicAuthUserProvider` must use the [AuthFinder](https://docs.adonisjs.com/guides/authentication/verifying-user-credentials#using-the-auth-finder-mixin) mixin to verify the user credentials during authentication. Copy code to clipboard import { DateTime } from 'luxon' import { compose } from '@adonisjs/core/helpers' import { BaseModel, column } from '@adonisjs/lucid/orm' import hash from '@adonisjs/core/services/hash' import { withAuthFinder } from '@adonisjs/auth/mixins/lucid' const AuthFinder = withAuthFinder(() => hash.use('scrypt'), { uids: ['email'], passwordColumnName: 'password', }) export default class User extends compose(BaseModel, AuthFinder) { @column({ isPrimary: true }) declare id: number @column() declare fullName: string | null @column() declare email: string @column() declare password: string @column.dateTime({ autoCreate: true }) declare createdAt: DateTime @column.dateTime({ autoCreate: true, autoUpdate: true }) declare updatedAt: DateTime } [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#protecting-routes) Protecting routes -------------------------------------------------------------------------------------------------------- Once you have configured the guard, you can use the `auth` middleware to protect routes from unauthenticated requests. The middleware is registered inside the `start/kernel.ts` file under the named middleware collection. Copy code to clipboard import router from '@adonisjs/core/services/router' export const middleware = router.named({ auth: () => import('#middleware/auth_middleware') }) Copy code to clipboard import { middleware } from '#start/kernel' import router from '@adonisjs/core/services/router' router .get('dashboard', ({ auth }) => { return auth.user }) .use(middleware.auth({ guards: ['basicAuth'] })) ### [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#handling-authentication-exception) Handling authentication exception The auth middleware throws the [E\_UNAUTHORIZED\_ACCESS](https://github.com/adonisjs/auth/blob/main/src/errors.ts#L21) if the user is not authenticated. The exception is automatically converted to an HTTP response with the [WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) header in the response. The `WWW-Authenticate` challenges the authentication and triggers a web-native prompt to re-enter the credentials. [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#getting-access-to-the-authenticated-user) Getting access to the authenticated user ------------------------------------------------------------------------------------------------------------------------------------------------------ You may access the logged-in user instance using the `auth.user` property. Since, you are using the `auth` middleware, the `auth.user` property will always be available. Copy code to clipboard import { middleware } from '#start/kernel' import router from '@adonisjs/core/services/router' router .get('dashboard', ({ auth }) => { return `You are authenticated as ${auth.user!.email}` }) .use(middleware.auth({ guards: ['basicAuth'] })) ### [](https://docs.adonisjs.com/guides/authentication/basic-auth-guard#get-authenticated-user-or-fail) Get authenticated user or fail If you do not like using the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-) on the `auth.user` property, you may use the `auth.getUserOrFail` method. This method will return the user object or throw [E\_UNAUTHORIZED\_ACCESS](https://docs.adonisjs.com/guides/references/exceptions#e_unauthorized_access) exception. Copy code to clipboard import { middleware } from '#start/kernel' import router from '@adonisjs/core/services/router' router .get('dashboard', ({ auth }) => { const user = auth.getUserOrFail() return `You are authenticated as ${user.email}` }) .use(middleware.auth({ guards: ['basicAuth'] })) * * * [Authentication\ \ Access tokens guard](https://docs.adonisjs.com/guides/authentication/access-tokens-guard) [Authentication\ \ Custom auth guard](https://docs.adonisjs.com/guides/authentication/custom-auth-guard) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/authentication/basic_auth_guard.md) --- # CORS (Security) | AdonisJS Documentation Toggle docs menu CORS [](https://docs.adonisjs.com/guides/security/cors#cors) CORS ============================================================ [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) helps you protect your application from malicious requests triggered using scripts in a browser environment. For example, if an AJAX or a fetch request is sent to your server from a different domain, the browser will block that request with a CORS error and expect you to implement a CORS policy if you think the request should be allowed. In AdonisJS, you can implement the CORS policy using the `@adonisjs/cors` package. The package ships with an HTTP middleware that intercepts incoming requests and responds with correct CORS headers. [](https://docs.adonisjs.com/guides/security/cors#installation) Installation ---------------------------------------------------------------------------- Install and configure the package using the following command : Copy code to clipboard node ace add @adonisjs/cors See steps performed by the add command 1. Installs the `@adonisjs/cors` package using the detected package manager. 2. Registers the following service provider inside the `adonisrc.ts` file. Copy code to clipboard { providers: [\ // ...other providers\ () => import('@adonisjs/cors/cors_provider')\ ] } 3. Creates the `config/cors.ts` file. This file contains the configuration settings for CORS. 4. Registers the following middleware inside the `start/kernel.ts` file. Copy code to clipboard server.use([\ () => import('@adonisjs/cors/cors_middleware')\ ]) [](https://docs.adonisjs.com/guides/security/cors#configuration) Configuration ------------------------------------------------------------------------------ The configuration for the CORS middleware is stored inside the `config/cors.ts` file. Copy code to clipboard import { defineConfig } from '@adonisjs/cors' const corsConfig = defineConfig({ enabled: true, origin: true, methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'], headers: true, exposeHeaders: [], credentials: true, maxAge: 90, }) export default corsConfig enabled Turn the middleware on or off temporarily without removing it from the middleware stack. origin The `origin` property controls the value for the [Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) header. You can allow the request's current origin by setting the value to `true` or disallow the request's current origin by setting it to `false`. Copy code to clipboard { origin: true } You may specify a list of hardcoded origins to allow an array of domain names. Copy code to clipboard { origin: ['adonisjs.com'] } Use the wildcard expression `*` to allow all the origins. Read the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#directives) to understand how the wildcard expression works. When the `credentials` property is set to `true`, we will automatically make the wildcard expression behave like a `boolean (true)`. Copy code to clipboard { origin: '*' } You can compute the `origin` value during the HTTP request using a function. For example: Copy code to clipboard { origin: (requestOrigin, ctx) => { return true } } methods The `methods` property controls the method to allow during the preflight request. The [Access-Control-Request-Method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Method) header value is checked against the allowed methods. Copy code to clipboard { methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] } headers The `headers` property controls the request headers to allow during the preflight request. The [Access-Control-Request-Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers) header value is checked against the headers property. Setting the value to `true` will allow all the headers. Whereas setting the value to `false` will disallow all the headers. Copy code to clipboard { headers: true } You can specify a list of headers to allow by defining them as an array of strings. Copy code to clipboard { headers: [\ 'Content-Type',\ 'Accept',\ 'Cookie'\ ] } You can compute the `headers` config value using a function during the HTTP request. For example: Copy code to clipboard { headers: (requestHeaders, ctx) => { return true } } exposeHeaders The `exposeHeaders` property controls the headers to expose via [Access-Control-Expose-Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) header during the preflight request. Copy code to clipboard { exposeHeaders: [\ 'cache-control',\ 'content-language',\ 'content-type',\ 'expires',\ 'last-modified',\ 'pragma',\ ] } credentials The `credentials` property controls whether to set the [Access-Control-Allow-Credentials](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials) header during the preflight request. Copy code to clipboard { credentials: true } maxAge The `maxAge` property controls the [Access-Control-Max-Age](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age) response header. The value is in seconds. * Setting the value to `null` will not set the header. * Whereas setting it to `-1` does set the header but disables the cache. Copy code to clipboard { maxAge: 90 } [](https://docs.adonisjs.com/guides/security/cors#debugging-cors-errors) Debugging CORS errors ---------------------------------------------------------------------------------------------- Debugging CORS issues is a challenging experience. However, there are no shortcuts other than understanding the rules of CORS and debugging the response headers to ensure everything is in place. Following are some links to the articles you may read to understand better how CORS works. * [How to Debug Any CORS Error](https://httptoolkit.com/blog/how-to-debug-cors-errors/) * [Will it CORS?](https://httptoolkit.com/will-it-cors/) * [MDN in-depth explanation of CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) * * * [Security\ \ Hashing](https://docs.adonisjs.com/guides/security/hashing) [Security\ \ Securing SSR apps](https://docs.adonisjs.com/guides/security/securing-ssr-applications) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/security/cors.md) --- # EdgeJS (Views & Templates) | AdonisJS Documentation Toggle docs menu EdgeJS [](https://docs.adonisjs.com/guides/views-and-templates/edgejs#edgejs) EdgeJS ============================================================================= Edge is a **simple**, **Modern**, and **batteries included** template engine created and maintained by the AdonisJS core team for Node.js. Edge is similar to writing JavaScript. If you know JavaScript, you know Edge. The documentation for Edge is available on [https://edgejs.dev](https://edgejs.dev/) [](https://docs.adonisjs.com/guides/views-and-templates/edgejs#installation) Installation ----------------------------------------------------------------------------------------- Install and configure Edge using the following command. Copy code to clipboard node ace add edge See steps performed by the add command 1. Installs the `edge.js` package using the detected package manager. 2. Registers the following service provider inside the `adonisrc.ts` file. Copy code to clipboard { providers: [\ // ...other providers\ () => import('@adonisjs/core/providers/edge_provider')\ ] } [](https://docs.adonisjs.com/guides/views-and-templates/edgejs#rendering-your-first-template) Rendering your first template --------------------------------------------------------------------------------------------------------------------------- Once the configuration is completed, you can use Edge to render templates. Let's create a `welcome.edge` file inside the `resources/views` directory. Copy code to clipboard node ace make:view welcome Open the newly created file and write the following markup inside it. Copy code to clipboard
{{ $message }}
@end You can also self-handle the `E_BAD_CSRF_TOKEN` exception inside the [global exception handler](https://docs.adonisjs.com/guides/basics/exception-handling#handling-exceptions) as follows. Copy code to clipboard import app from '@adonisjs/core/services/app' import { errors } from '@adonisjs/shield' import { HttpContext, ExceptionHandler } from '@adonisjs/core/http' export default class HttpExceptionHandler extends ExceptionHandler { async handle(error: unknown, ctx: HttpContext) { if (error instanceof errors.E_BAD_CSRF_TOKEN) { return ctx.response .status(error.status) .send('Page has expired') } return super.handle(error, ctx) } } ### [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#config-reference) Config reference The configuration for the CSRF guard is stored inside the `config/shield.ts` file. Copy code to clipboard import { defineConfig } from '@adonisjs/shield' const shieldConfig = defineConfig({ csrf: { enabled: true, exceptRoutes: [], enableXsrfCookie: true, methods: ['POST', 'PUT', 'PATCH', 'DELETE'], }, }) export default shieldConfig enabled Turn the CSRF guard on or off. exceptRoutes An array of route patterns to exempt from the CSRF protection. If your application has routes that accept form submissions via an API, you might want to exempt them. For more advanced use cases, you may register a function to exempt specific routes dynamically. Copy code to clipboard { exceptRoutes: (ctx) => { // exempt all routes starting with /api/ return ctx.request.url().includes('/api/') } } enableXsrfCookie When enabled, Shield will store the CSRF token inside an encrypted cookie named `XSRF-TOKEN`, which can be read by the frontend JavaScript code. This allows frontend request libraries like Axios to read the `XSRF-TOKEN` automatically and set it as a `X-XSRF-TOKEN` header when making Ajax requests without server-rendered forms. You must keep the `enableXsrfCookie` disabled if you are not triggering Ajax requests programmatically. methods An array of HTTP methods to protect. All incoming requests for the mentioned methods must present a valid CSRF token. cookieOptions Configuration for the `XSRF-TOKEN` cookie. [See cookies configuration](https://docs.adonisjs.com/guides/basics/cookies#configuration) for available options. [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#defining-csp-policy) Defining CSP policy --------------------------------------------------------------------------------------------------------------- [CSP (Content security policy)](https://web.dev/csp/) protects your applications from XSS attacks by defining trusted sources for loading JavaScript, CSS, fonts, images, and so on. The CSP guard is disabled by default. However, we recommend you enable it and configure the policy directives inside the `config/shield.ts` file. Copy code to clipboard import { defineConfig } from '@adonisjs/shield' const shieldConfig = defineConfig({ csp: { enabled: true, directives: { // policy directives go here }, reportOnly: false, }, }) export default shieldConfig enabled Turn the CSP guard on or off. directives Configure the CSP directives. You can view the list of available directives on [https://content-security-policy.com/](https://content-security-policy.com/#directive) Copy code to clipboard const shieldConfig = defineConfig({ csp: { enabled: true, directives: { defaultSrc: [`'self'`], scriptSrc: [`'self'`, 'https://cdnjs.cloudflare.com'], fontSrc: [`'self'`, 'https://fonts.googleapis.com'] }, reportOnly: false, }, }) export default shieldConfig reportOnly The CSP policy will not block the resources when the `reportOnly` flag is enabled. Instead, it will report the violations on an endpoint configured using the `reportUri` directive. Copy code to clipboard const shieldConfig = defineConfig({ csp: { enabled: true, directives: { defaultSrc: [`'self'`], reportUri: ['/csp-report'] }, reportOnly: true, }, }) Also, register the `csp-report` endpoint to collect the violation reports. Copy code to clipboard router.post('/csp-report', async ({ request }) => { const report = request.input('csp-report') }) ### [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#using-nonce) Using Nonce You may allow inline `script` and `style` tags by defining the [nonce attribute](https://content-security-policy.com/nonce/) on them. The value of the nonce attribute can be accessed inside Edge templates using the `cspNonce` property. Copy code to clipboard Also, use the `@nonce` keyword inside the directives config to allow nonce-based inline scripts and styles. Copy code to clipboard const shieldConfig = defineConfig({ csp: { directives: { defaultSrc: [`'self'`, '@nonce'], }, }, }) ### [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#loading-assets-from-the-vite-dev-server) Loading assets from the Vite Dev server If you are using the [Vite integration](https://docs.adonisjs.com/guides/basics/vite) , you can use the following CSP keywords to allow assets served by the Vite Dev server. * The `@viteDevUrl` adds the Vite dev server URL to the allowed list. * The `@viteHmrUrl` adds the Vite HMR websocket server URL to the allowed list. Copy code to clipboard const shieldConfig = defineConfig({ csp: { directives: { defaultSrc: [`'self'`, '@viteDevUrl'], connectSrc: ['@viteHmrUrl'] }, }, }) If you are deploying the Vite bundled output to a CDN server, you must replace `@viteDevUrl` with the `@viteUrl` keyword to allow assets from both the development server and the CDN server. Copy code to clipboard directives: { defaultSrc: [`'self'`, '@viteDevUrl'], defaultSrc: [`'self'`, '@viteUrl'], connectSrc: ['@viteHmrUrl'] }, ### [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#adding-nonce-to-styles-injected-by-vite) Adding Nonce to styles injected by Vite Currently, Vite does not allow defining a `nonce` attribute to the `style` tags injected by it inside the DOM. There is an [open PR](https://github.com/vitejs/vite/pull/11864) for the same, and we are hoping it will be resolved soon. [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#configuring-hsts) Configuring HSTS --------------------------------------------------------------------------------------------------------- The [**Strict-Transport-Security (HSTS)**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) response header informs the browsers to always load the website using HTTPS. You can configure the header directives using the `config/shield.ts` file. Copy code to clipboard import { defineConfig } from '@adonisjs/shield' const shieldConfig = defineConfig({ hsts: { enabled: true, maxAge: '180 days', includeSubDomains: true, }, }) enabled Turn the hsts guard on or off. maxAge Defines the `max-age` attribute. The value should either be a number in seconds or a string-based time expression. Copy code to clipboard { // Remember for 10 seconds maxAge: 10, } Copy code to clipboard { // Remember for 2 days maxAge: '2 days', } includeSubDomains Defines the `includeSubDomains` directive to apply the setting on subdomains. [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#configuring-x-frame-protection) Configuring X-Frame protection ------------------------------------------------------------------------------------------------------------------------------------- The [**X-Frame-Options**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header is used to indicate if a browser is allowed to render a website embedded inside an `iframe`, `frame`, `embed`, or `object` tags. If you have configured CSP, you may instead use the [frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive and disable the `xFrame` guard. You can configure the header directives using the `config/shield.ts` file. Copy code to clipboard import { defineConfig } from '@adonisjs/shield' const shieldConfig = defineConfig({ xFrame: { enabled: true, action: 'DENY' }, }) enabled Turn the xFrame guard on or off. action The `action` property defines the header value. It could be `DENY`, `SAMEORIGIN`, or `ALLOW-FROM`. Copy code to clipboard { action: 'DENY' } In the case of `ALLOW-FROM`, you must also define the `domain` property. Copy code to clipboard { action: 'ALLOW-FROM', domain: 'https://foo.com', } [](https://docs.adonisjs.com/guides/security/securing-ssr-applications#disabling-mime-sniffing) Disabling MIME sniffing ----------------------------------------------------------------------------------------------------------------------- The [**X-Content-Type-Options**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) header instructs browsers to follow the `content-type` header and not perform MIME sniffing by inspecting the content of an HTTP response. Once you enable this guard, Shield will define the `X-Content-Type-Options: nosniff` header for all HTTP responses. Copy code to clipboard import { defineConfig } from '@adonisjs/shield' const shieldConfig = defineConfig({ contentTypeSniffing: { enabled: true, }, }) * * * [Security\ \ CORS](https://docs.adonisjs.com/guides/security/cors) [Security\ \ Rate limiting](https://docs.adonisjs.com/guides/security/rate-limiting) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/security/securing_ssr_applications.md) --- # HTTP tests (Testing) | AdonisJS Documentation Toggle docs menu HTTP tests [](https://docs.adonisjs.com/guides/testing/http-tests#http-tests) HTTP tests ============================================================================= HTTP tests refer to testing your application endpoints by making an actual HTTP request against them and asserting the response body, headers, cookies, session, etc. HTTP tests are performed using the [API client plugin](https://japa.dev/docs/plugins/api-client) of Japa. The API client plugin is a stateless request library similar to `Axios` or `fetch` but more suited for testing. If you want to test your web apps inside a real browser and interact with them programmatically, we recommend using the [Browser client](https://docs.adonisjs.com/guides/testing/browser-tests) that uses Playwright for testing. [](https://docs.adonisjs.com/guides/testing/http-tests#setup) Setup ------------------------------------------------------------------- The first step is to install the following packages from the npm packages registry. npm Copy code to clipboard npm i -D @japa/api-client ### [](https://docs.adonisjs.com/guides/testing/http-tests#registering-the-plugin) Registering the plugin Before moving forward, register the plugin inside the `tests/bootstrap.ts` file. tests/bootstrap.ts Copy code to clipboard import { apiClient } from '@japa/api-client' export const plugins: Config['plugins'] = [\ assert(),\ apiClient(),\ pluginAdonisJS(app),\ ] The `apiClient` method optionally accepts the `baseURL` for the server. If not provided, it will use the `HOST` and the `PORT` environment variables. Copy code to clipboard import env from '#start/env' export const plugins: Config['plugins'] = [\ apiClient({\ baseURL: `http://${env.get('HOST')}:${env.get('PORT')}`\ })\ ] [](https://docs.adonisjs.com/guides/testing/http-tests#basic-example) Basic example ----------------------------------------------------------------------------------- Once the `apiClient` plugin is registered, you may access the `client` object from [TestContext](https://japa.dev/docs/test-context) to make an HTTP request. The HTTP tests must be written inside the folder configured for the `functional` tests suite. You may use the following command to create a new test file. Copy code to clipboard node ace make:test users/list --suite=functional Copy code to clipboard import { test } from '@japa/runner' test.group('Users list', () => { test('get a list of users', async ({ client }) => { const response = await client.get('/users') response.assertStatus(200) response.assertBody({ data: [\ {\ id: 1,\ email: 'foo@bar.com',\ }\ ] }) }) }) To view all the available request and assertion methods, make sure to [go through the Japa documentation](https://japa.dev/docs/plugins/api-client) . [](https://docs.adonisjs.com/guides/testing/http-tests#open-api-testing) Open API testing ----------------------------------------------------------------------------------------- The assertion and API client plugins allow you to use Open API spec files for writing assertions. Instead of manually testing the response against a fixed payload, you may use a spec file to test the shape of the HTTP response. It is a great way to keep your Open API spec and server responses in sync. Because if you remove a certain endpoint from the spec file or change the response data shape, your tests will fail. ### [](https://docs.adonisjs.com/guides/testing/http-tests#registering-schema) Registering schema AdonisJS does not offer tooling for generating Open API schema files from code. You may write it by hand or use graphical tools to create it. Once you have a spec file, save it inside the `resources` directory (create the directory if missing) and register it with the `openapi-assertions` plugin within the `tests/bootstrap.ts` file. Copy code to clipboard npm i -D @japa/openapi-assertions tests/bootstrap.ts Copy code to clipboard import app from '@adonisjs/core/services/app' import { openapi } from '@japa/openapi-assertions' export const plugins: Config['plugins'] = [\ assert(),\ openapi({\ schemas: [app.makePath('resources/open_api_schema.yaml')]\ })\ apiClient(),\ pluginAdonisJS(app)\ ] ### [](https://docs.adonisjs.com/guides/testing/http-tests#writing-assertions) Writing assertions Once the schema is registered, you can use the `response.assertAgainstApiSpec` method to assert against the API spec. Copy code to clipboard test('paginate posts', async ({ client }) => { const response = await client.get('/posts') response.assertAgainstApiSpec() }) * The `response.assertAgainstApiSpec` method will use the **request method**, the **endpoint**, and the **response status code** to find the expected response schema. * An exception will be raised when the response schema cannot be found. Otherwise, the response body will be validated against the schema. Only the response's shape is tested, not the actual values. Therefore, you may have to write additional assertions. For example: Copy code to clipboard // Assert that the response is as per the schema response.assertAgainstApiSpec() // Assert for expected values response.assertBodyContains({ data: [{ title: 'Adonis 101' }, { title: 'Lucid 101' }] }) [](https://docs.adonisjs.com/guides/testing/http-tests#readingwriting-cookies) Reading/writing cookies ------------------------------------------------------------------------------------------------------ You may send cookies during the HTTP request using the `withCookie` method. The method accepts the cookie name as the first argument and the value as the second. Copy code to clipboard await client .get('/users') .withCookie('user_preferences', { limit: 10 }) The `withCookie` method defines a [signed cookie](https://docs.adonisjs.com/guides/basics/cookies#signed-cookies) . In addition, you may use the `withEncryptedCookie` or `withPlainCookie` methods to send other types of cookies to the server. Copy code to clipboard await client .get('/users') .withEncryptedCookie('user_preferences', { limit: 10 }) Copy code to clipboard await client .get('/users') .withPlainCookie('user_preferences', { limit: 10 }) ### [](https://docs.adonisjs.com/guides/testing/http-tests#reading-cookies-from-the-response) Reading cookies from the response You may access the cookies set by your AdonisJS server using the `response.cookies` method. The method returns an object of cookies as a key-value pair. Copy code to clipboard const response = await client.get('/users') console.log(response.cookies()) You may use the `response.cookie` method to access a single cookie value by its name. Or use the `assertCookie` method to assert the cookie value. Copy code to clipboard const response = await client.get('/users') console.log(response.cookie('user_preferences')) response.assertCookie('user_preferences') [](https://docs.adonisjs.com/guides/testing/http-tests#populating-session-store) Populating session store --------------------------------------------------------------------------------------------------------- If you are using the [`@adonisjs/session`](https://docs.adonisjs.com/guides/basics/session) package to read/write session data in your application, you may also want to use the `sessionApiClient` plugin to populate the session store when writing tests. ### [](https://docs.adonisjs.com/guides/testing/http-tests#setup-1) Setup The first step is registering the plugin inside the `tests/bootstrap.ts` file. tests/bootstrap.ts Copy code to clipboard import { sessionApiClient } from '@adonisjs/session/plugins/api_client' export const plugins: Config['plugins'] = [\ assert(),\ pluginAdonisJS(app),\ sessionApiClient(app)\ ] And then, update the `.env.test` file (create one if it is missing) and set the `SESSON_DRIVER` to `memory`. .env.test Copy code to clipboard SESSION_DRIVER=memory ### [](https://docs.adonisjs.com/guides/testing/http-tests#making-requests-with-session-data) Making requests with session data You may use the `withSession` method on the Japa API client to make an HTTP request with some pre-defined session data. The `withSession` method will create a new session ID and populate the memory store with the data, and your AdonisJS application code can read the session data as usual. After the request finishes, the session ID and its data will be destroyed. Copy code to clipboard test('checkout with cart items', async ({ client }) => { await client .post('/checkout') .withSession({ cartItems: [\ {\ id: 1,\ name: 'South Indian Filter Press Coffee'\ },\ {\ id: 2,\ name: 'Cold Brew Bags',\ }\ ] }) }) Like the `withSession` method, you may use the `withFlashMessages` method to set flash messages when making an HTTP request. Copy code to clipboard const response = await client .get('posts/1') .withFlashMessages({ success: 'Post created successfully' }) response.assertTextIncludes(`Post created successfully`) ### [](https://docs.adonisjs.com/guides/testing/http-tests#reading-session-data-from-the-response) Reading session data from the response You may access the session data set by your AdonisJS server using the `response.session()` method. The method returns the session data as an object of a key-value pair. Copy code to clipboard const response = await client .post('/posts') .json({ title: 'some title', body: 'some description', }) console.log(response.session()) // all session data console.log(response.session('post_id')) // value of post_id You may read flash messages from the response using the `response.flashMessage` or `response.flashMessages` method. Copy code to clipboard const response = await client.post('/posts') response.flashMessages() response.flashMessage('errors') response.flashMessage('success') Finally, you may write assertions for the session data using one of the following methods. Copy code to clipboard const response = await client.post('/posts') /** * Assert a specific key (with optional value) exists * in the session store */ response.assertSession('cart_items') response.assertSession('cart_items', [{\ id: 1,\ }, {\ id: 2,\ }]) /** * Assert a specific key is not in the session store */ response.assertSessionMissing('cart_items') /** * Assert a flash message exists (with optional value) * in the flash messages store */ response.assertFlashMessage('success') response.assertFlashMessage('success', 'Post created successfully') /** * Assert a specific key is not in the flash messages store */ response.assertFlashMissing('errors') /** * Assert for validation errors in the flash messages * store */ response.assertHasValidationError('title') response.assertValidationError('title', 'Enter post title') response.assertValidationErrors('title', [\ 'Enter post title',\ 'Post title must be 10 characters long.'\ ]) response.assertDoesNotHaveValidationError('title') [](https://docs.adonisjs.com/guides/testing/http-tests#authenticating-users) Authenticating users ------------------------------------------------------------------------------------------------- If you use the `@adonisjs/auth` package to authenticate users in your application, you may use the `authApiClient` Japa plugin to authenticate users when making HTTP requests to your application. The first step is registering the plugin inside the `tests/bootstrap.ts` file. tests/bootstrap.ts Copy code to clipboard import { authApiClient } from '@adonisjs/auth/plugins/api_client' export const plugins: Config['plugins'] = [\ assert(),\ pluginAdonisJS(app),\ authApiClient(app)\ ] If you are using session-based authentication, make sure to also set up the session plugin. See [Populating session store - Setup](https://docs.adonisjs.com/guides/testing/http-tests#setup-1) . That's all. Now, you may login users using the `loginAs` method. The method accepts the user object as the only argument and marks the user as logged in for the current HTTP request. Copy code to clipboard import User from '#models/user' test('get payments list', async ({ client }) => { const user = await User.create(payload) await client .get('/me/payments') .loginAs(user) }) The `loginAs` method uses the default guard configured inside the `config/auth.ts` file for authentication. However, you may specify a custom guard using the `withGuard` method. For example: Copy code to clipboard await client .get('/me/payments') .withGuard('api_tokens') .loginAs(user) [](https://docs.adonisjs.com/guides/testing/http-tests#making-a-request-with-a-csrf-token) Making a request with a CSRF token ----------------------------------------------------------------------------------------------------------------------------- If forms in your application use [CSRF protection](https://docs.adonisjs.com/guides/security/securing-ssr-applications) , you may use the `withCsrfToken` method to generate a CSRF token and pass it as a header during the request. Before using the `withCsrfToken` method, register the following Japa plugins inside the `tests/bootstrap.ts` file and also make sure to [switch the `SESSION_DRIVER` env variable](https://docs.adonisjs.com/guides/testing/http-tests#setup-1) to `memory`. tests/bootstrap.ts Copy code to clipboard import { shieldApiClient } from '@adonisjs/shield/plugins/api_client' import { sessionApiClient } from '@adonisjs/session/plugins/api_client' export const plugins: Config['plugins'] = [\ assert(),\ pluginAdonisJS(app),\ sessionApiClient(app),\ shieldApiClient()\ ] Copy code to clipboard test('create a post', async ({ client }) => { await client .post('/posts') .form(dataGoesHere) .withCsrfToken() }) [](https://docs.adonisjs.com/guides/testing/http-tests#the-route-helper) The route helper ----------------------------------------------------------------------------------------- You may use the `route` helper from the TestContext to create a URL for a route. Using the route helper ensures that whenever you update your routes, you do not have to come back and fix all the URLs inside your tests. The `route` helper accepts the same set of arguments accepted by the global template method [route](https://docs.adonisjs.com/guides/basics/routing#url-builder) . Copy code to clipboard test('get a list of users', async ({ client, route }) => { const response = await client.get( route('users.list') ) response.assertStatus(200) response.assertBody({ data: [\ {\ id: 1,\ email: 'foo@bar.com',\ }\ ] }) }) * * * [Testing\ \ Introduction](https://docs.adonisjs.com/guides/testing/introduction) [Testing\ \ Browser tests](https://docs.adonisjs.com/guides/testing/browser-tests) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/testing/http_tests.md) --- # Social authentication (Authentication) | AdonisJS Documentation Toggle docs menu Social authentication [](https://docs.adonisjs.com/guides/authentication/social-authentication#social-authentication) Social authentication ===================================================================================================================== You can implement social authentication in your AdonisJS applications using the `@adonisjs/ally` package. Ally comes with the following inbuilt drivers, alongside an extensible API to [register custom drivers](https://docs.adonisjs.com/guides/authentication/social-authentication#creating-a-custom-social-driver) . * Twitter * Facebook * Spotify * Google * GitHub * Discord * LinkedIn Ally does not store any users or access tokens on your behalf. It implements the OAuth2 and OAuth1 protocols, authenticates a user with social service, and provides user details. You can store that information inside a database and use the [auth](https://docs.adonisjs.com/guides/authentication/introduction) package to login the user within your application. [](https://docs.adonisjs.com/guides/authentication/social-authentication#installation) Installation --------------------------------------------------------------------------------------------------- Install and configure the package using the following command : Copy code to clipboard node ace add @adonisjs/ally # Define providers as CLI flags node ace add @adonisjs/ally --providers=github --providers=google See steps performed by the add command 1. Installs the `@adonisjs/ally` package using the detected package manager. 2. Registers the following service provider inside the `adonisrc.ts` file. Copy code to clipboard { providers: [\ // ...other providers\ () => import('@adonisjs/ally/ally_provider')\ ] } 3. Create the `config/ally.ts` file. This file contains the configuration settings for selected OAuth providers. 4. Defines the environment variables to store `CLIENT_ID` and `CLIENT_SECRET` for selected OAuth providers. [](https://docs.adonisjs.com/guides/authentication/social-authentication#configuration) Configuration ----------------------------------------------------------------------------------------------------- The `@adonisjs/ally` package configuration is stored inside the `config/ally.ts` file. You can define config for multiple services within a single config file. See also: [Config stub](https://github.com/adonisjs/ally/blob/main/stubs/config/ally.stub) Copy code to clipboard import { defineConfig, services } from '@adonisjs/ally' defineConfig({ github: services.github({ clientId: env.get('GITHUB_CLIENT_ID')!, clientSecret: env.get('GITHUB_CLIENT_SECRET')!, callbackUrl: '', }), twitter: services.twitter({ clientId: env.get('TWITTER_CLIENT_ID')!, clientSecret: env.get('TWITTER_CLIENT_SECRET')!, callbackUrl: '', }), }) ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#configuring-the-callback-url) Configuring the callback URL OAuth providers require you to register a callback URL to handle the redirect response after the user authorizes the login request. The callback URL must be registered with the OAuth service provider. For example: If you are using GitHub, you must log in to your GitHub account, [create a new app](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) and define the callback URL using the GitHub interface. Also, you must register the same callback URL within the `config/ally.ts` file using the `callbackUrl` property. [](https://docs.adonisjs.com/guides/authentication/social-authentication#usage) Usage ------------------------------------------------------------------------------------- Once the package has been configured, you can interact with Ally APIs using the `ctx.ally` property. You can switch between the configured auth providers using the `ally.use()` method. For example: Copy code to clipboard router.get('/github/redirect', ({ ally }) => { // GitHub driver instance const gh = ally.use('github') }) router.get('/twitter/redirect', ({ ally }) => { // Twitter driver instance const twitter = ally.use('twitter') }) // You could also dynamically retrieve the driver router.get('/:provider/redirect', ({ ally, params }) => { const driverInstance = ally.use(params.provider) }).where('provider', /github|twitter/) ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#redirecting-the-user-for-authentication) Redirecting the user for authentication The first step in social authentication is to redirect the user to an OAuth service and wait for them to either approve or deny the authentication request. You can perform the redirect using the `.redirect()` method. Copy code to clipboard router.get('/github/redirect', ({ ally }) => { return ally.use('github').redirect() }) You can pass a callback function to define custom scopes or query string values during the redirect. Copy code to clipboard router.get('/github/redirect', ({ ally }) => { return ally .use('github') .redirect((request) => { request.scopes(['user:email', 'repo:invite']) request.param('allow_signup', false) }) }) ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#handling-callback-response) Handling callback response The user will be redirected back to your application's `callbackUrl` after they approve or deny the authentication request. Within this route, you can call the `.user()` method to get the logged-in user details and the access token. However, you must also check the response for possible error states. Copy code to clipboard router.get('/github/callback', async ({ ally }) => { const gh = ally.use('github') /** * User has denied access by canceling * the login flow */ if (gh.accessDenied()) { return 'You have cancelled the login process' } /** * OAuth state verification failed. This happens when the * CSRF cookie gets expired. */ if (gh.stateMisMatch()) { return 'We are unable to verify the request. Please try again' } /** * GitHub responded with some error */ if (gh.hasError()) { return gh.getError() } /** * Access user info */ const user = await gh.user() return user }) [](https://docs.adonisjs.com/guides/authentication/social-authentication#user-properties) User properties --------------------------------------------------------------------------------------------------------- Following is the list of properties you can access from the return value of the `.user()` method call. The properties are consistent among all the underlying drivers. Copy code to clipboard const user = await gh.user() user.id user.email user.emailVerificationState user.name user.nickName user.avatarUrl user.token user.original ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#id) id A unique ID returned by the OAuth provider. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#email) email The email address returned by the OAuth provider. The value will be `null` if the OAuth request does not ask for the user's email address. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#emailverificationstate) emailVerificationState Many OAuth providers allow users with unverified emails to log in and authenticate OAuth requests. You should use this flag to ensure only users with verified emails can log in. Following is the list of possible values. * `verified`: The user's email address is verified with the OAuth provider. * `unverified`: The user's email address is not verified. * `unsupported`: The OAuth provider does not share the email verification state. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#name) name The name of the user returned by the OAuth provider. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#nickname) nickName A publicly visible nick name of the user. The value of `nickName` and `name` will be the same if the OAuth provider has no concept of nicknames. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#avatarurl) avatarUrl The HTTP(s) URL to the user's public profile picture. ### [](https://docs.adonisjs.com/guides/authentication/social-authentication#token) token The token property is the reference to the underlying access token object. The token object has the following sub-properties. Copy code to clipboard user.token.token user.token.type user.token.refreshToken user.token.expiresAt user.token.expiresIn | Property | Protocol | Description | | --- | --- | --- | | `token` | OAuth2 / OAuth1 | The value of the access token. The value is available for the `OAuth2` and the `OAuth1` protocols. | | `secret` | OAuth1 | The token secret applicable only for `OAuth1` protocol. Currently, Twitter is the only official driver using OAuth1. | | `type` | OAuth2 | The token type. Usually, it will be a [bearer token](https://oauth.net/2/bearer-tokens/)Hello {{ await cache.get('username') }}
[](https://docs.adonisjs.com/guides/digging-deeper/cache#ace-commands) Ace Commands ----------------------------------------------------------------------------------- The `@adonisjs/cache` package also provides a set of Ace commands to interact with the cache from the terminal. ### [](https://docs.adonisjs.com/guides/digging-deeper/cache#cacheclear) `cache:clear` Clears the cache for the specified store. If not specified, it will clear the default one. Copy code to clipboard # Clear the default cache store node ace cache:clear # Clear a specific cache store node ace cache:clear redis # Clear a specific namespace node ace cache:clear store --namespace users # Clear multiple specific tags node ace cache:clear store --tags products --tags users ### [](https://docs.adonisjs.com/guides/digging-deeper/cache#cachedelete) `cache:delete` Deletes a specific cache key from the specified store. If not specified, it will delete from the default one. Copy code to clipboard # Delete a specific cache key node ace cache:delete cache-key # Delete a specific cache key from a specific store node ace cache:delete cache-key store ### [](https://docs.adonisjs.com/guides/digging-deeper/cache#cacheprune) `cache:prune` Some cache drivers, like the database driver, do not automatically remove expired keys because they lack native TTL support. You can use the `cache:prune` command to manually remove expired keys. On stores that support TTL, this command will result in a no-op. Copy code to clipboard # Prune expired keys from the default cache store node ace cache:prune # Prune expired keys from a specific cache store node ace cache:prune store * * * [Testing\ \ Mocks & Fakes](https://docs.adonisjs.com/guides/testing/mocks-and-fakes) [Digging deeper\ \ Drive](https://docs.adonisjs.com/guides/digging-deeper/drive) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/digging_deeper/cache.md) --- # Events (References) | AdonisJS Documentation Toggle docs menu Events [](https://docs.adonisjs.com/guides/references/events#events-reference) Events reference ======================================================================================== In this guide, we look at the list of events dispatched by the framework core and the official packages. Check out the [emitter](https://docs.adonisjs.com/guides/digging-deeper/emitter) documentation to learn more about its usage. [](https://docs.adonisjs.com/guides/references/events#httprequest_completed) http:request\_completed ---------------------------------------------------------------------------------------------------- The [`http:request_completed`](https://github.com/adonisjs/http-server/blob/main/src/types/server.ts#L65) event is dispatched after an HTTP request is completed. The event contains an instance of the [HttpContext](https://docs.adonisjs.com/guides/concepts/http-context) and the request duration. The `duration` value is the output of the `process.hrtime` method. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' import string from '@adonisjs/core/helpers/string' emitter.on('http:request_completed', (event) => { const method = event.ctx.request.method() const url = event.ctx.request.url(true) const duration = event.duration console.log(`${method} ${url}: ${string.prettyHrTime(duration)}`) }) [](https://docs.adonisjs.com/guides/references/events#httpserver_ready) http:server\_ready ------------------------------------------------------------------------------------------ The event is dispatched once the AdonisJS HTTP server is ready to accept incoming requests. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('http:server_ready', (event) => { console.log(event.host) console.log(event.port) /** * Time it took to boot the app and start * the HTTP server. */ console.log(event.duration) }) [](https://docs.adonisjs.com/guides/references/events#container_bindingresolved) container\_binding:resolved ------------------------------------------------------------------------------------------------------------ The event is dispatched after the IoC container resolves a binding or constructs a class instance. The `event.binding` property will be a string (binding name) or a class constructor, and the `event.value` property is the resolved value. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('container_binding:resolved', (event) => { console.log(event.binding) console.log(event.value) }) [](https://docs.adonisjs.com/guides/references/events#sessioninitiated) session:initiated ----------------------------------------------------------------------------------------- The `@adonisjs/session` package emits the event when the session store is initiated during an HTTP request. The `event.session` property is an instance of the [Session class](https://github.com/adonisjs/session/blob/main/src/session.ts) . Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session:initiated', (event) => { console.log(`Initiated store for ${event.session.sessionId}`) }) [](https://docs.adonisjs.com/guides/references/events#sessioncommitted) session:committed ----------------------------------------------------------------------------------------- The `@adonisjs/session` package emits the event when the session data is written to the session store during an HTTP request. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session:committed', (event) => { console.log(`Persisted data for ${event.session.sessionId}`) }) [](https://docs.adonisjs.com/guides/references/events#sessionmigrated) session:migrated --------------------------------------------------------------------------------------- The `@adonisjs/session` package emits the event when a new session ID is generated using the `session.regenerate()` method. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session:migrated', (event) => { console.log(`Migrating data to ${event.toSessionId}`) console.log(`Destroying session ${event.fromSessionId}`) }) [](https://docs.adonisjs.com/guides/references/events#i18nmissingtranslation) i18n:missing:translation ------------------------------------------------------------------------------------------------------ The event is dispatched by the `@adonisjs/i18n` package when a translation for a specific key and locale is missing. You may listen to this event to find the missing translations for a given locale. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('i18n:missing:translation', function (event) { console.log(event.identifier) console.log(event.hasFallback) console.log(event.locale) }) [](https://docs.adonisjs.com/guides/references/events#mailsending) mail:sending ------------------------------------------------------------------------------- The `@adonisjs/mail` package emits the event before sending an email. In the case of the `mail.sendLater` method call, the event will be emitted when the mail queue processes the job. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('mail:sending', (event) => { console.log(event.mailerName) console.log(event.message) console.log(event.views) }) [](https://docs.adonisjs.com/guides/references/events#mailsent) mail:sent ------------------------------------------------------------------------- After sending the email, the event is dispatched by the `@adonisjs/mail` package. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('mail:sent', (event) => { console.log(event.response) console.log(event.mailerName) console.log(event.message) console.log(event.views) }) [](https://docs.adonisjs.com/guides/references/events#mailqueueing) mail:queueing --------------------------------------------------------------------------------- The `@adonisjs/mail` package emits the event before queueing the job to send the email. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('mail:queueing', (event) => { console.log(event.mailerName) console.log(event.message) console.log(event.views) }) [](https://docs.adonisjs.com/guides/references/events#mailqueued) mail:queued ----------------------------------------------------------------------------- After the email has been queued, the event is dispatched by the `@adonisjs/mail` package. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('mail:queued', (event) => { console.log(event.mailerName) console.log(event.message) console.log(event.views) }) [](https://docs.adonisjs.com/guides/references/events#queuedmailerror) queued:mail:error ---------------------------------------------------------------------------------------- The event is dispatched when the [MemoryQueue](https://github.com/adonisjs/mail/blob/main/src/messengers/memory_queue.ts) implementation of the `@adonisjs/mail` package is unable to send the email queued using the `mail.sendLater` method. If you are using a custom queue implementation, you must capture the job errors and emit this event. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('queued:mail:error', (event) => { console.log(event.error) console.log(event.mailerName) }) [](https://docs.adonisjs.com/guides/references/events#session_authlogin_attempted) session\_auth:login\_attempted ----------------------------------------------------------------------------------------------------------------- The event is dispatched by the [SessionGuard](https://github.com/adonisjs/auth/blob/main/src/guards/session/guard.ts) implementation of the `@adonisjs/auth` package when the `auth.login` method is called either directly or internally by the session guard. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:login_attempted', (event) => { console.log(event.guardName) console.log(event.user) }) [](https://docs.adonisjs.com/guides/references/events#session_authlogin_succeeded) session\_auth:login\_succeeded ----------------------------------------------------------------------------------------------------------------- The event is dispatched by the [SessionGuard](https://github.com/adonisjs/auth/blob/main/src/guards/session/guard.ts) implementation of the `@adonisjs/auth` package after a user has been logged in successfully. You may use this event to track sessions associated with a given user. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:login_succeeded', (event) => { console.log(event.guardName) console.log(event.sessionId) console.log(event.user) console.log(event.rememberMeToken) // (if created one) }) [](https://docs.adonisjs.com/guides/references/events#session_authauthentication_attempted) session\_auth:authentication\_attempted ----------------------------------------------------------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/auth` package when an attempt is made to validate the request session and check for a logged-in user. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:authentication_attempted', (event) => { console.log(event.guardName) console.log(event.sessionId) }) [](https://docs.adonisjs.com/guides/references/events#session_authauthentication_succeeded) session\_auth:authentication\_succeeded ----------------------------------------------------------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/auth` package after the request session has been validated and the user is logged in. You may access the logged-in user using the `event.user` property. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:authentication_succeeded', (event) => { console.log(event.guardName) console.log(event.sessionId) console.log(event.user) console.log(event.rememberMeToken) // if authenticated using token }) [](https://docs.adonisjs.com/guides/references/events#session_authauthentication_failed) session\_auth:authentication\_failed ----------------------------------------------------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/auth` package when the authentication check fails, and the user is not logged in during the current HTTP request. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:authentication_failed', (event) => { console.log(event.guardName) console.log(event.sessionId) console.log(event.error) }) [](https://docs.adonisjs.com/guides/references/events#session_authlogged_out) session\_auth:logged\_out ------------------------------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/auth` package after the user has been logged out. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('session_auth:logged_out', (event) => { console.log(event.guardName) console.log(event.sessionId) /** * The value of the user will be null when logout is called * during a request where no user was logged in in the first place. */ console.log(event.user) }) [](https://docs.adonisjs.com/guides/references/events#access_tokens_authauthentication_attempted) access\_tokens\_auth:authentication\_attempted ------------------------------------------------------------------------------------------------------------------------------------------------ The event is dispatched by the `@adonisjs/auth` package when an attempt is made to validate the access token during an HTTP request. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('access_tokens_auth:authentication_attempted', (event) => { console.log(event.guardName) }) [](https://docs.adonisjs.com/guides/references/events#access_tokens_authauthentication_succeeded) access\_tokens\_auth:authentication\_succeeded ------------------------------------------------------------------------------------------------------------------------------------------------ The event is dispatched by the `@adonisjs/auth` package after the access token has been verified. You may access the authenticated user using the `event.user` property. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('access_tokens_auth:authentication_succeeded', (event) => { console.log(event.guardName) console.log(event.user) console.log(event.token) }) [](https://docs.adonisjs.com/guides/references/events#access_tokens_authauthentication_failed) access\_tokens\_auth:authentication\_failed ------------------------------------------------------------------------------------------------------------------------------------------ The event is dispatched by the `@adonisjs/auth` package when the authentication check fails. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('access_tokens_auth:authentication_failed', (event) => { console.log(event.guardName) console.log(event.error) }) [](https://docs.adonisjs.com/guides/references/events#authorizationfinished) authorization:finished --------------------------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/bouncer` package after the authorization check has been performed. The event payload includes the final response you may inspect to know the status of the check. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('authorization:finished', (event) => { console.log(event.user) console.log(event.response) console.log(event.parameters) console.log(event.action) }) [](https://docs.adonisjs.com/guides/references/events#cachecleared) cache:cleared --------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/cache` package after the cache has been cleared using the `cache.clear` method. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('cache:cleared', (event) => { console.log(event.store) }) [](https://docs.adonisjs.com/guides/references/events#cachedeleted) cache:deleted --------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/cache` package after a cache key has been deleted using the `cache.delete` method. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('cache:deleted', (event) => { console.log(event.key) }) [](https://docs.adonisjs.com/guides/references/events#cachehit) cache:hit ------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/cache` package when a cache key is found in the cache store. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('cache:hit', (event) => { console.log(event.key) console.log(event.value) }) [](https://docs.adonisjs.com/guides/references/events#cachemiss) cache:miss --------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/cache` package when a cache key is not found in the cache store. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('cache:miss', (event) => { console.log(event.key) }) [](https://docs.adonisjs.com/guides/references/events#cachewritten) cache:written --------------------------------------------------------------------------------- The event is dispatched by the `@adonisjs/cache` package after a cache key has been written to the cache store. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('cache:written', (event) => { console.log(event.key) console.log(event.value) }) * * * [References\ \ Edge helpers and tags](https://docs.adonisjs.com/guides/references/edge) [References\ \ Exceptions](https://docs.adonisjs.com/guides/references/exceptions) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/references/events.md) --- # Exceptions (References) | AdonisJS Documentation Toggle docs menu Exceptions [](https://docs.adonisjs.com/guides/references/exceptions#exceptions-reference) Exceptions reference ==================================================================================================== In this guide we will go through the list of known exceptions raised by the framework core and the official packages. Some of the exceptions are marked as **self-handled**. [Self-handled exceptions](https://docs.adonisjs.com/guides/basics/exception-handling#defining-the-handle-method) can convert themselves to an HTTP response. [](https://docs.adonisjs.com/guides/references/exceptions#e_route_not_found) E\_ROUTE\_NOT\_FOUND ------------------------------------------------------------------------------------------------- The exception is raised when the HTTP server receives a request for a non-existing route. By default, the client will get a 404 response, and optionally, you may render an HTML page using [status pages](https://docs.adonisjs.com/guides/basics/exception-handling#status-pages) . * **Status code**: 404 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_ROUTE_NOT_FOUND) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_row_not_found) E\_ROW\_NOT\_FOUND --------------------------------------------------------------------------------------------- The exception is raised when the database query for finding one item fails \[e.g when using `Model.findOrFail()`\]. By default, the client will get a 404 response, and optionally, you may render an HTML page using [status pages](https://docs.adonisjs.com/guides/basics/exception-handling#status-pages) . * **Status code**: 404 * **Self handled**: No Copy code to clipboard import { errors as lucidErrors } from '@adonisjs/lucid' if (error instanceof lucidErrors.E_ROW_NOT_FOUND) { // handle error console.log(`${error.model?.name || 'Row'} not found`) } [](https://docs.adonisjs.com/guides/references/exceptions#e_authorization_failure) E\_AUTHORIZATION\_FAILURE ------------------------------------------------------------------------------------------------------------ The exception is raised when a bouncer authorization check fails. The exception is self-handled and [uses content-negotiation](https://docs.adonisjs.com/guides/security/authorization#throwing-authorizationexception) to return an appropriate error response to the client. * **Status code**: 403 * **Self handled**: Yes * **Translation identifier**: `errors.E_AUTHORIZATION_FAILURE` Copy code to clipboard import { errors as bouncerErrors } from '@adonisjs/bouncer' if (error instanceof bouncerErrors.E_AUTHORIZATION_FAILURE) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_too_many_requests) E\_TOO\_MANY\_REQUESTS ----------------------------------------------------------------------------------------------------- The exception is raised by the [@adonisjs/rate-limiter](https://docs.adonisjs.com/guides/security/rate-limiting) package when a request exhausts all the requests allowed during a given duration. The exception is self-handled and [uses content-negotiation](https://docs.adonisjs.com/guides/security/rate-limiting#handling-throttleexception) to return an appropriate error response to the client. * **Status code**: 429 * **Self handled**: Yes * **Translation identifier**: `errors.E_TOO_MANY_REQUESTS` Copy code to clipboard import { errors as limiterErrors } from '@adonisjs/limiter' if (error instanceof limiterErrors.E_TOO_MANY_REQUESTS) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_bad_csrf_token) E\_BAD\_CSRF\_TOKEN ----------------------------------------------------------------------------------------------- The exception is raised when a form using [CSRF protection](https://docs.adonisjs.com/guides/security/securing-ssr-applications#csrf-protection) is submitted without the CSRF token, or the CSRF token is invalid. * **Status code**: 403 * **Self handled**: Yes * **Translation identifier**: `errors.E_BAD_CSRF_TOKEN` Copy code to clipboard import { errors as shieldErrors } from '@adonisjs/shield' if (error instanceof shieldErrors.E_BAD_CSRF_TOKEN) { } The `E_BAD_CSRF_TOKEN` exception is [self-handled](https://github.com/adonisjs/shield/blob/main/src/errors.ts#L20) , and the user will be redirected back to the form, and you can access the error using the flash messages. Copy code to clipboard @error('E_BAD_CSRF_TOKEN'){{ message }}
@end [](https://docs.adonisjs.com/guides/references/exceptions#e_oauth_missing_code) E\_OAUTH\_MISSING\_CODE ------------------------------------------------------------------------------------------------------- The `@adonisjs/ally` package raises the exception when the OAuth service does not provide the OAuth code during the redirect. You can avoid this exception if you [handle the errors](https://docs.adonisjs.com/guides/authentication/social-authentication#handling-callback-response) before calling the `.accessToken` or `.user` methods. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors as allyErrors } from '@adonisjs/bouncer' if (error instanceof allyErrors.E_OAUTH_MISSING_CODE) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_oauth_state_mismatch) E\_OAUTH\_STATE\_MISMATCH ----------------------------------------------------------------------------------------------------------- The `@adonisjs/ally` package raises the exception when the CSRF state defined during the redirect is missing. You can avoid this exception if you [handle the errors](https://docs.adonisjs.com/guides/authentication/social-authentication#handling-callback-response) before calling the `.accessToken` or `.user` methods. * **Status code**: 400 * **Self handled**: No Copy code to clipboard import { errors as allyErrors } from '@adonisjs/bouncer' if (error instanceof allyErrors.E_OAUTH_STATE_MISMATCH) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_unauthorized_access) E\_UNAUTHORIZED\_ACCESS -------------------------------------------------------------------------------------------------------- The exception is raised when one of the authentication guards is not able to authenticate the request. The exception is self-handled and uses [content-negotiation](https://docs.adonisjs.com/guides/authentication/session-guard#handling-authentication-exception) to return an appropriate error response to the client. * **Status code**: 401 * **Self handled**: Yes * **Translation identifier**: `errors.E_UNAUTHORIZED_ACCESS` Copy code to clipboard import { errors as authErrors } from '@adonisjs/auth' if (error instanceof authErrors.E_UNAUTHORIZED_ACCESS) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_invalid_credentials) E\_INVALID\_CREDENTIALS -------------------------------------------------------------------------------------------------------- The exception is raised when the auth finder is not able to verify the user credentials. The exception is handled and use [content-negotiation](https://docs.adonisjs.com/guides/authentication/verifying-user-credentials#handling-exceptions) to return an appropriate error response to the client. * **Status code**: 400 * **Self handled**: Yes * **Translation identifier**: `errors.E_INVALID_CREDENTIALS` Copy code to clipboard import { errors as authErrors } from '@adonisjs/auth' if (error instanceof authErrors.E_INVALID_CREDENTIALS) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_cannot_lookup_route) E\_CANNOT\_LOOKUP\_ROUTE --------------------------------------------------------------------------------------------------------- The exception is raised when you attempt to create a URL for a route using the [URL builder](https://docs.adonisjs.com/guides/basics/routing#url-builder) . * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_CANNOT_LOOKUP_ROUTE) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_http_exception) E\_HTTP\_EXCEPTION ---------------------------------------------------------------------------------------------- The `E_HTTP_EXCEPTION` is a generic exception for throwing errors during an HTTP request. You can use this exception directly or create a custom exception extending it. * **Status code**: Defined at the time of raising the exception * **Self handled**: Yes Throw exception Copy code to clipboard import { errors } from '@adonisjs/core' throw errors.E_HTTP_EXCEPTION.invoke( { errors: ['Cannot process request'] }, 422 ) Handle exception Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_HTTP_EXCEPTION) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_http_request_aborted) E\_HTTP\_REQUEST\_ABORTED ----------------------------------------------------------------------------------------------------------- The `E_HTTP_REQUEST_ABORTED` is a sub-class of the `E_HTTP_EXCEPTION` exception. This exception is raised by the [response.abort](https://docs.adonisjs.com/guides/basics/response#aborting-request-with-an-error) method. Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_HTTP_REQUEST_ABORTED) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_insecure_app_key) E\_INSECURE\_APP\_KEY --------------------------------------------------------------------------------------------------- The exception is raised when the length of `appKey` is smaller than 16 characters. You can use the [generate](https://docs.adonisjs.com/guides/references/commands#generatekey) ace command to generate a secure app key. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_INSECURE_APP_KEY) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_app_key) E\_MISSING\_APP\_KEY ------------------------------------------------------------------------------------------------- The exception is raised when the `appKey` property is not defined inside the `config/app.ts` file. By default, the value of the `appKey` is set using the `APP_KEY` environment variable. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_APP_KEY) { // handle error } [](https://docs.adonisjs.com/guides/references/exceptions#e_invalid_env_variables) E\_INVALID\_ENV\_VARIABLES ------------------------------------------------------------------------------------------------------------- The exception is raised when one or more environment variables fail the validation. The detailed validation errors can be accessed using the `error.help` property. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_INVALID_ENV_VARIABLES) { console.log(error.help) } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_command_name) E\_MISSING\_COMMAND\_NAME ----------------------------------------------------------------------------------------------------------- The exception is raised when a command does not define the `commandName` property or its value is an empty string. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_COMMAND_NAME) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_command_not_found) E\_COMMAND\_NOT\_FOUND ----------------------------------------------------------------------------------------------------- The exception is raised by Ace when unable to find a command. * **Status code**: 404 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_COMMAND_NOT_FOUND) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_flag) E\_MISSING\_FLAG ------------------------------------------------------------------------------------------ The exception is raised when executing a command without passing a required CLI flag. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_FLAG) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_flag_value) E\_MISSING\_FLAG\_VALUE ------------------------------------------------------------------------------------------------------- The exception is raised when trying to execute a command without providing any value to a non-boolean CLI flag. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_FLAG_VALUE) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_arg) E\_MISSING\_ARG ---------------------------------------------------------------------------------------- The exception is raised when executing a command without defining the required arguments. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_ARG) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_missing_arg_value) E\_MISSING\_ARG\_VALUE ----------------------------------------------------------------------------------------------------- The exception is raised when executing a command without defining the value for a required argument. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_MISSING_ARG_VALUE) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_unknown_flag) E\_UNKNOWN\_FLAG ------------------------------------------------------------------------------------------ The exception is raised when executing a command with an unknown CLI flag. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_UNKNOWN_FLAG) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_invalid_flag) E\_INVALID\_FLAG ------------------------------------------------------------------------------------------ The exception is raised when the value provided for a CLI flag is invalid—for example, passing a string value to a flag that accepts numeric values. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors } from '@adonisjs/core' if (error instanceof errors.E_INVALID_FLAG) { console.log(error.commandName) } [](https://docs.adonisjs.com/guides/references/exceptions#e_multiple_redis_subscriptions) E\_MULTIPLE\_REDIS\_SUBSCRIPTIONS --------------------------------------------------------------------------------------------------------------------------- The `@adonisjs/redis` package raises the exception when you attempt to [subscribe to a given pub/sub channel](https://docs.adonisjs.com/guides/database/redis#pubsub) multiple times. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors as redisErrors } from '@adonisjs/redis' if (error instanceof redisErrors.E_MULTIPLE_REDIS_SUBSCRIPTIONS) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_multiple_redis_psubscriptions) E\_MULTIPLE\_REDIS\_PSUBSCRIPTIONS ----------------------------------------------------------------------------------------------------------------------------- The `@adonisjs/redis` package raises the exception when you attempt to [subscribe to a given pub/sub pattern](https://docs.adonisjs.com/guides/database/redis#pubsub) multiple times. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors as redisErrors } from '@adonisjs/redis' if (error instanceof redisErrors.E_MULTIPLE_REDIS_PSUBSCRIPTIONS) { } [](https://docs.adonisjs.com/guides/references/exceptions#e_mail_transport_error) E\_MAIL\_TRANSPORT\_ERROR ----------------------------------------------------------------------------------------------------------- The exception is raised by the `@adonisjs/mail` package when unable to send the email using a given transport. Usually, this will happen when the HTTP API of the email service returns a non-200 HTTP response. You may access the network request error using the `error.cause` property. The `cause` property is the [error object](https://github.com/sindresorhus/got/blob/main/documentation/8-errors.md) returned by `got` (npm package). * **Status code**: 400 * **Self handled**: No Copy code to clipboard import { errors as mailErrors } from '@adonisjs/mail' if (error instanceof mailErrors.E_MAIL_TRANSPORT_ERROR) { console.log(error.cause) } [](https://docs.adonisjs.com/guides/references/exceptions#e_session_not_mutable) E\_SESSION\_NOT\_MUTABLE --------------------------------------------------------------------------------------------------------- The exception is raised by the `@adonisjs/session` package when the session store is initiated in the read-only mode. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors as sessionErrors } from '@adonisjs/session' if (error instanceof sessionErrors.E_SESSION_NOT_MUTABLE) { console.log(error.message) } [](https://docs.adonisjs.com/guides/references/exceptions#e_session_not_ready) E\_SESSION\_NOT\_READY ----------------------------------------------------------------------------------------------------- The exception is raised by the `@adonisjs/session` package when the session store has not been initiated yet. This will be the case when you are not using the session middleware. * **Status code**: 500 * **Self handled**: No Copy code to clipboard import { errors as sessionErrors } from '@adonisjs/session' if (error instanceof sessionErrors.E_SESSION_NOT_READY) { console.log(error.message) } * * * [References\ \ Events](https://docs.adonisjs.com/guides/references/events) [References\ \ Helpers](https://docs.adonisjs.com/guides/references/helpers) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/references/exceptions.md) --- # Edge helpers and tags (References) | AdonisJS Documentation Toggle docs menu Edge helpers and tags [](https://docs.adonisjs.com/guides/references/edge#edge-helpers-and-tags) Edge helpers and tags ================================================================================================ In this guide, we will learn about the **helpers and the tags** contributed to Edge by the AdonisJS official packages. The helpers shipped with Edge are not covered in this guide and must reference [Edge](https://edgejs.dev/docs/helpers) documentation for the same. [](https://docs.adonisjs.com/guides/references/edge#request) request -------------------------------------------------------------------- Reference to the instance of ongoing [HTTP request](https://docs.adonisjs.com/guides/basics/request) . The property is only available when a template is rendered using the `ctx.view.render` method. Copy code to clipboard {{ request.url() }} {{ request.input('signature') }} [](https://docs.adonisjs.com/guides/references/edge#routesignedroute) route/signedRoute --------------------------------------------------------------------------------------- Helper functions to create URL for a route using the [URL builder](https://docs.adonisjs.com/guides/basics/routing#url-builder) . Unlike the URL builder, the view helpers do not have a fluent API and accept the following parameters. | | | | --- | --- | | Position | Description | | 1st | The route identifier or the route pattern | | 2nd | Route params are defined as an array or an object. | | 3rd | The options object with the following properties.{{ flashMessages.get('inputErrorsBag.title') }}
@end @if(flashMessages.has('notification')){{ auth.user.email }}
@end If you are displaying the logged-in user info on a public page (not protected by the auth middleware), then you may want to first silently check if the user is logged-in or not. Copy code to clipboard {{-- Check if user is logged-in --}} @eval(await auth.use('web').check()) @if(auth.use('web').isAuthenticated){{ auth.use('web').user.email }}
@end [](https://docs.adonisjs.com/guides/references/edge#asset) asset ---------------------------------------------------------------- Resolve the URL of an asset processed by Vite. Learn more about [referencing assets inside Edge templates](https://docs.adonisjs.com/guides/basics/vite#referencing-assets-inside-edge-templates) . Copy code to clipboard
[](https://docs.adonisjs.com/guides/references/edge#embedimage--embedimagedata)
embedImage / embedImageData
-----------------------------------------------------------------------------------------------------------
The `embedImage` and the `embedImageData` helpers are added by the [mail](https://docs.adonisjs.com/guides/digging-deeper/mail#embedding-images)
package and are only available when rendering a template to send an email.
Copy code to clipboard
[](https://docs.adonisjs.com/guides/references/edge#flashmessage)
@flashMessage
-------------------------------------------------------------------------------
The `@flashMessage` tag provides a better DX for reading flash messages for a given key conditionally.
**Instead of writing conditionals**
Copy code to clipboard
@if(flashMessages.has('notification'))
{{ flashMessages.get('errorsBag.E_BAD_CSRF_TOKEN') }}
@end **You may prefer using the tag** Copy code to clipboard @error('E_BAD_CSRF_TOKEN'){{ $message }}
@end [](https://docs.adonisjs.com/guides/references/edge#inputerror) @inputError --------------------------------------------------------------------------- The `@inputError` tag provides a better DX for reading validation error messages stored inside the `inputErrorsBag` key in `flashMessages`. **Instead of writing conditionals** Copy code to clipboard @if(flashMessages.has('inputErrorsBag.title')) @each(message in flashMessages.get('inputErrorsBag.title')){{ message }}
@end @end **You may prefer using the tag** Copy code to clipboard @inputError('title') @each(message in $messages){{ message }}
@end @end [](https://docs.adonisjs.com/guides/references/edge#vite) @vite --------------------------------------------------------------- The `@vite` tag accepts an array of entry point paths and returns the `script` and the `link` tags for the same. The path you provide to the `@vite` tag should match exactly the path registered inside the `vite.config.js` file. Copy code to clipboard export default defineConfig({ plugins: [\ adonisjs({\ entrypoints: ['resources/js/app.js'],\ }),\ ] }) Copy code to clipboard @vite(['resources/js/app.js']) You can define the script tag attributes as the 2nd argument. For example: Copy code to clipboard @vite(['resources/js/app.js'], { defer: true, }) [](https://docs.adonisjs.com/guides/references/edge#vitereactrefresh) @viteReactRefresh --------------------------------------------------------------------------------------- The `@viteReactRefresh` tag returns a [script tag to enable React HMR](https://vitejs.dev/guide/backend-integration.html#:~:text=you%27ll%20also%20need%20to%20add%20this%20before%20the%20above%20scripts) for project using the [@vitejs/plugin-react](https://www.npmjs.com/package/@vitejs/plugin-react) package. Copy code to clipboard @viteReactRefresh() Output HTML Copy code to clipboard [](https://docs.adonisjs.com/guides/references/edge#cancannot) @can/@cannot --------------------------------------------------------------------------- The `@can` and `@cannot` tags allows you write authorization checks in Edge templates by referencing the ability name or the policy name as a string. The first argument is the ability or the policy reference followed by the arguments accepted by the check. See also: [Pre-registering abilities and policies](https://docs.adonisjs.com/guides/security/authorization#pre-registering-abilities-and-policies) Copy code to clipboard @can('editPost', post) {{-- Can edit post --}} @end @can('PostPolicy.edit', post) {{-- Can edit post --}} @end Copy code to clipboard @cannot('editPost', post) {{-- Cannot edit post --}} @end @cannot('editPost', post) {{-- Cannot edit post --}} @end * * * [References\ \ Commands](https://docs.adonisjs.com/guides/references/commands) [References\ \ Events](https://docs.adonisjs.com/guides/references/events) © 2025 AdonisJS [Edit this page](https://github.com/adonisjs/v6-docs/blob/main/content/docs/references/edge.md) --- # Commands (References) | AdonisJS Documentation Toggle docs menu Commands [](https://docs.adonisjs.com/guides/references/commands#commands-reference) Commands reference ============================================================================================== In this guide, we cover the usage of all the commands shipped with the framework core and the official packages. You may also view the commands help using the `node ace list` command or the `node acefoo © bar
') // <p> foo © bar </p> Optionally, you can encode non-ASCII symbols using the `encodeSymbols` option. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.escapeHTML('foo © bar
', { encodeSymbols: true, }) // <p> foo © bar </p> [](https://docs.adonisjs.com/guides/references/helpers#encodesymbols) encodeSymbols ----------------------------------------------------------------------------------- You may encode non-ASCII symbols in a string value using the `encodeSymbols` helper. Under the hood, we use [he.encode](https://www.npmjs.com/package/he#heencodetext-options) method. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.encodeSymbols('foo © bar ≠ baz 𝌆 qux') // 'foo © bar ≠ baz 𝌆 qux' [](https://docs.adonisjs.com/guides/references/helpers#prettyhrtime) prettyHrTime --------------------------------------------------------------------------------- Pretty print the diff of [process.hrtime](https://nodejs.org/api/process.html#processhrtimetime) method. Copy code to clipboard import { hrtime } from 'node:process' import string from '@adonisjs/core/helpers/string' const startTime = hrtime() await someOperation() const endTime = hrtime(startTime) console.log(string.prettyHrTime(endTime)) [](https://docs.adonisjs.com/guides/references/helpers#isempty) isEmpty ----------------------------------------------------------------------- Check if a string value is empty. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.isEmpty('') // true string.isEmpty(' ') // true [](https://docs.adonisjs.com/guides/references/helpers#truncate) truncate ------------------------------------------------------------------------- Truncate a string at a given number of characters. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.truncate('This is a very long, maybe not that long title', 12) // Output: This is a ve... By default, the string is truncated exactly at the given index. However, you can instruct the method to wait for the words to complete. Copy code to clipboard string.truncate('This is a very long, maybe not that long title', 12, { completeWords: true, }) // Output: This is a very... You can customize the suffix using the `suffix` option. Copy code to clipboard string.truncate('This is a very long, maybe not that long title', 12, { completeWords: true, suffix: '... Read more ', }) // Output: This is a very... Read more [](https://docs.adonisjs.com/guides/references/helpers#excerpt) excerpt ----------------------------------------------------------------------- The `excerpt` method is identical to the `truncate` method. However, it strips the HTML tags from the string. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.excerpt('This is a very long, maybe not that long title
', 12, { completeWords: true, }) // Output: This is a very... [](https://docs.adonisjs.com/guides/references/helpers#slug) slug ----------------------------------------------------------------- Generate slug for a string value. The method is exported from the [slugify package](https://www.npmjs.com/package/slugify) ; therefore, consult its documentation for available options. Copy code to clipboard import string from '@adonisjs/core/helpers/string' console.log(string.slug('hello ♥ world')) // hello-love-world You can add custom replacements for Unicode values as follows. Copy code to clipboard string.slug.extend({ '☢': 'radioactive' }) console.log(string.slug('unicode ♥ is ☢')) // unicode-love-is-radioactive [](https://docs.adonisjs.com/guides/references/helpers#interpolate) interpolate ------------------------------------------------------------------------------- Interpolate variables inside a string. The variables must be inside double curly braces. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.interpolate('hello {{ user.username }}', { user: { username: 'virk' } }) // hello virk Curly braces can be escaped using the `\\` prefix. Copy code to clipboard string.interpolate('hello \\{{ users.0 }}', {}) // hello {{ users.0 }} [](https://docs.adonisjs.com/guides/references/helpers#plural) plural --------------------------------------------------------------------- Convert a word to its plural form. The method is exported from the [pluralize package](https://www.npmjs.com/package/pluralize) . Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.plural('test') // tests [](https://docs.adonisjs.com/guides/references/helpers#isplural) isPlural ------------------------------------------------------------------------- Find if a word already is in plural form. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.isPlural('tests') // true [](https://docs.adonisjs.com/guides/references/helpers#pluralize) pluralize --------------------------------------------------------------------------- This method combines the `singular` and the `plural` methods and uses one or the other based on the count. For example: Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.pluralize('box', 1) // box string.pluralize('box', 2) // boxes string.pluralize('box', 0) // boxes string.pluralize('boxes', 1) // box string.pluralize('boxes', 2) // boxes string.pluralize('boxes', 0) // boxes The `pluralize` property exports [additional methods](https://www.npmjs.com/package/pluralize) to register custom uncountable, irregular, plural, and singular rules. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.pluralize.addUncountableRule('paper') string.pluralize.addSingularRule(/singles$/i, 'singular') [](https://docs.adonisjs.com/guides/references/helpers#singular) singular ------------------------------------------------------------------------- Convert a word to its singular form. The method is exported from the [pluralize package](https://www.npmjs.com/package/pluralize) . Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.singular('tests') // test [](https://docs.adonisjs.com/guides/references/helpers#issingular) isSingular ----------------------------------------------------------------------------- Find if a word is already in a singular form. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.isSingular('test') // true [](https://docs.adonisjs.com/guides/references/helpers#camelcase) camelCase --------------------------------------------------------------------------- Convert a string value to camelcase. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.camelCase('user_name') // userName Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'test' | | 'test string' | 'testString' | | 'Test String' | 'testString' | | 'TestV2' | 'testV2' | | '_foo\_bar_' | 'fooBar' | | 'version 1.2.10' | 'version1210' | | 'version 1.21.0' | 'version1210' | [](https://docs.adonisjs.com/guides/references/helpers#capitalcase) capitalCase ------------------------------------------------------------------------------- Convert a string value to a capital case. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.capitalCase('helloWorld') // Hello World Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'Test' | | 'test string' | 'Test String' | | 'Test String' | 'Test String' | | 'TestV2' | 'Test V 2' | | 'version 1.2.10' | 'Version 1.2.10' | | 'version 1.21.0' | 'Version 1.21.0' | [](https://docs.adonisjs.com/guides/references/helpers#dashcase) dashCase ------------------------------------------------------------------------- Convert a string value to a dash case. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.dashCase('helloWorld') // hello-world Optionally, you can capitalize the first letter of each word. Copy code to clipboard string.dashCase('helloWorld', { capitalize: true }) // Hello-World Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'test' | | 'test string' | 'test-string' | | 'Test String' | 'test-string' | | 'Test V2' | 'test-v2' | | 'TestV2' | 'test-v-2' | | 'version 1.2.10' | 'version-1210' | | 'version 1.21.0' | 'version-1210' | [](https://docs.adonisjs.com/guides/references/helpers#dotcase) dotCase ----------------------------------------------------------------------- Convert a string value to a dot case. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.dotCase('helloWorld') // hello.World Optionally, you can convert the first letter of all the words to lowercase. Copy code to clipboard string.dotCase('helloWorld', { lowerCase: true }) // hello.world Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'test' | | 'test string' | 'test.string' | | 'Test String' | 'Test.String' | | 'dot.case' | 'dot.case' | | 'path/case' | 'path.case' | | 'TestV2' | 'Test.V.2' | | 'version 1.2.10' | 'version.1210' | | 'version 1.21.0' | 'version.1210' | [](https://docs.adonisjs.com/guides/references/helpers#nocase) noCase --------------------------------------------------------------------- Remove all sorts of casing from a string value. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.noCase('helloWorld') // hello world Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'test' | | 'TEST' | 'test' | | 'testString' | 'test string' | | 'testString123' | 'test string123' | | 'testString\_1\_2\_3' | 'test string 1 2 3' | | 'ID123String' | 'id123 string' | | 'foo bar123' | 'foo bar123' | | 'a1bStar' | 'a1b star' | | 'CONSTANT\_CASE ' | 'constant case' | | 'CONST123\_FOO' | 'const123 foo' | | 'FOO\_bar' | 'foo bar' | | 'XMLHttpRequest' | 'xml http request' | | 'IQueryAArgs' | 'i query a args' | | 'dot.case' | 'dot case' | | 'path/case' | 'path case' | | 'snake\_case' | 'snake case' | | 'snake\_case123' | 'snake case123' | | 'snake\_case\_123' | 'snake case 123' | | '"quotes"' | 'quotes' | | 'version 0.45.0' | 'version 0 45 0' | | 'version 0..78..9' | 'version 0 78 9' | | 'version 4\_99/4' | 'version 4 99 4' | | ' test ' | 'test' | | 'something\_2014\_other' | 'something 2014 other' | | 'amazon s3 data' | 'amazon s3 data' | | 'foo\_13\_bar' | 'foo 13 bar' | [](https://docs.adonisjs.com/guides/references/helpers#pascalcase) pascalCase ----------------------------------------------------------------------------- Convert a string value to a Pascal case. Great for generating JavaScript class names. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.pascalCase('user team') // UserTeam Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'Test' | | 'test string' | 'TestString' | | 'Test String' | 'TestString' | | 'TestV2' | 'TestV2' | | 'version 1.2.10' | 'Version1210' | | 'version 1.21.0' | 'Version1210' | [](https://docs.adonisjs.com/guides/references/helpers#sentencecase) sentenceCase --------------------------------------------------------------------------------- Convert a value to a sentence. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.sentenceCase('getting_started-with-adonisjs') // Getting started with adonisjs Following are some of the conversion examples. | Input | Output | | --- | --- | | 'test' | 'Test' | | 'test string' | 'Test string' | | 'Test String' | 'Test string' | | 'TestV2' | 'Test v2' | | 'version 1.2.10' | 'Version 1 2 10' | | 'version 1.21.0' | 'Version 1 21 0' | [](https://docs.adonisjs.com/guides/references/helpers#snakecase) snakeCase --------------------------------------------------------------------------- Convert value to snake case. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.snakeCase('user team') // user_team Following are some of the conversion examples. | Input | Output | | --- | --- | | '\_id' | 'id' | | 'test' | 'test' | | 'test string' | 'test\_string' | | 'Test String' | 'test\_string' | | 'Test V2' | 'test\_v2' | | 'TestV2' | 'test\_v\_2' | | 'version 1.2.10' | 'version\_1210' | | 'version 1.21.0' | 'version\_1210' | [](https://docs.adonisjs.com/guides/references/helpers#titlecase) titleCase --------------------------------------------------------------------------- Convert a string value to the title case. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.titleCase('small word ends on') // Small Word Ends On Following are some of the conversion examples. | Input | Output | | --- | --- | | 'one. two.' | 'One. Two.' | | 'a small word starts' | 'A Small Word Starts' | | 'small word ends on' | 'Small Word Ends On' | | 'we keep NASA capitalized' | 'We Keep NASA Capitalized' | | 'pass camelCase through' | 'Pass camelCase Through' | | 'follow step-by-step instructions' | 'Follow Step-by-Step Instructions' | | 'this vs. that' | 'This vs. That' | | 'this vs that' | 'This vs That' | | 'newcastle upon tyne' | 'Newcastle upon Tyne' | | 'newcastle \*upon\* tyne' | 'Newcastle \*upon\* Tyne' | [](https://docs.adonisjs.com/guides/references/helpers#random) random --------------------------------------------------------------------- Generate a cryptographically secure random string of a given length. The output value is a URL-safe base64 encoded string. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.random(32) // 8mejfWWbXbry8Rh7u8MW3o-6dxd80Thk [](https://docs.adonisjs.com/guides/references/helpers#sentence) sentence ------------------------------------------------------------------------- Convert an array of words to a comma-separated sentence. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.sentence(['routes', 'controllers', 'middleware']) // routes, controllers, and middleware You can replace the `and` with an `or` by specifying the `options.lastSeparator` property. Copy code to clipboard string.sentence(['routes', 'controllers', 'middleware'], { lastSeparator: ', or ', }) In the following example, the two words are combined using the `and` separator, not the comma (usually advocated in English). However, you can use a custom separator for a pair of words. Copy code to clipboard string.sentence(['routes', 'controllers']) // routes and controllers string.sentence(['routes', 'controllers'], { pairSeparator: ', and ', }) // routes, and controllers [](https://docs.adonisjs.com/guides/references/helpers#condensewhitespace) condenseWhitespace --------------------------------------------------------------------------------------------- Remove multiple whitespaces from a string to a single whitespace. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.condenseWhitespace('hello world') // hello world string.condenseWhitespace(' hello world ') // hello world [](https://docs.adonisjs.com/guides/references/helpers#seconds) seconds ----------------------------------------------------------------------- Parse a string-based time expression to seconds. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.seconds.parse('10h') // 36000 string.seconds.parse('1 day') // 86400 Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in seconds. Copy code to clipboard string.seconds.parse(180) // 180 You can format seconds to a pretty string using the `format` method. Copy code to clipboard string.seconds.format(36000) // 10h string.seconds.format(36000, true) // 10 hours [](https://docs.adonisjs.com/guides/references/helpers#milliseconds) milliseconds --------------------------------------------------------------------------------- Parse a string-based time expression to milliseconds. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.milliseconds.parse('1 h') // 3.6e6 string.milliseconds.parse('1 day') // 8.64e7 Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in milliseconds. Copy code to clipboard string.milliseconds.parse(180) // 180 Using the `format` method, you can format milliseconds to a pretty string. Copy code to clipboard string.milliseconds.format(3.6e6) // 1h string.milliseconds.format(3.6e6, true) // 1 hour [](https://docs.adonisjs.com/guides/references/helpers#bytes) bytes ------------------------------------------------------------------- Parse a string-based unit expression to bytes. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.bytes.parse('1KB') // 1024 string.bytes.parse('1MB') // 1048576 Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in bytes. Copy code to clipboard string.bytes.parse(1024) // 1024 Using the `format` method, you can format bytes to a pretty string. The method is exported directly from the [bytes](https://www.npmjs.com/package/bytes) package. Please reference the package README for available options. Copy code to clipboard string.bytes.format(1048576) // 1MB string.bytes.format(1024 * 1024 * 1000) // 1000MB string.bytes.format(1024 * 1024 * 1000, { thousandsSeparator: ',' }) // 1,000MB [](https://docs.adonisjs.com/guides/references/helpers#ordinal) ordinal ----------------------------------------------------------------------- Get the ordinal letter for a given number. Copy code to clipboard import string from '@adonisjs/core/helpers/string' string.ordinal(1) // 1st string.ordinal(2) // '2nd' string.ordinal(3) // '3rd' string.ordinal(4) // '4th' string.ordinal(23) // '23rd' string.ordinal(24) // '24th' [](https://docs.adonisjs.com/guides/references/helpers#safeequal) safeEqual --------------------------------------------------------------------------- Check if two buffer or string values are the same. This method does not leak any timing information and prevents [timing attack](https://javascript.plainenglish.io/what-are-timing-attacks-and-how-to-prevent-them-using-nodejs-158cc7e2d70c) . Under the hood, this method uses Node.js [crypto.timeSafeEqual](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b) method, with support for comparing string values. _(crypto.timeSafeEqual does not support string comparison)_ Copy code to clipboard import { safeEqual } from '@adonisjs/core/helpers' /** * The trusted value, it might be saved inside the db */ const trustedValue = 'hello world' /** * Untrusted user input */ const userInput = 'hello' if (safeEqual(trustedValue, userInput)) { // both are the same } else { // value mismatch } [](https://docs.adonisjs.com/guides/references/helpers#cuid) cuid ----------------------------------------------------------------- Create a secure, collision-resistant ID optimized for horizontal scaling and performance. This method uses the [@paralleldrive/cuid2](https://github.com/paralleldrive/cuid2) package under the hood. Copy code to clipboard import { cuid } from '@adonisjs/core/helpers' const id = cuid() // tz4a98xxat96iws9zmbrgj3a You can use the `isCuid` method to check if a value is a valid CUID. Copy code to clipboard import { cuid, isCuid } from '@adonisjs/core/helpers' const id = cuid() isCuid(id) // true [](https://docs.adonisjs.com/guides/references/helpers#compose) compose ----------------------------------------------------------------------- The `compose` helper allows you to use TypeScript class mixins with a cleaner API. Following is an example of mixin usage without the `compose` helper. Copy code to clipboard class User extends UserWithAttributes(UserWithAge(UserWithPassword(UserWithEmail(BaseModel)))) {} Following is an example with the `compose` helper. * There is no nesting. * The order of mixins is from (left to right/top to bottom). Whereas earlier, it was inside out. Copy code to clipboard import { compose } from '@adonisjs/core/helpers' class User extends compose( BaseModel, UserWithEmail, UserWithPassword, UserWithAge, UserWithAttributes ) {} [](https://docs.adonisjs.com/guides/references/helpers#base64) base64 --------------------------------------------------------------------- Utility methods to base64 encode and decode values. Copy code to clipboard import { base64 } from '@adonisjs/core/helpers' base64.encode('hello world') // aGVsbG8gd29ybGQ= Like the `encode` method, you can use the `urlEncode` to generate a base64 string safe to pass in a URL. The `urlEncode` method performs the following replacements. * Replace `+` with `-`. * Replace `/` with `_`. * And remove the `=` sign from the end of the string. Copy code to clipboard base64.urlEncode('hello world') // aGVsbG8gd29ybGQ You can use the `decode` and the `urlDecode` methods to decode a previously encoded base64 string. Copy code to clipboard base64.decode(base64.encode('hello world')) // hello world base64.urlDecode(base64.urlEncode('hello world')) // hello world The `decode` and the `urlDecode` methods return `null` when the input value is an invalid base64 string. You can turn on the `strict` mode to raise an exception instead. Copy code to clipboard base64.decode('hello world') // null base64.decode('hello world', 'utf-8', true) // raises exception [](https://docs.adonisjs.com/guides/references/helpers#fsreadall) fsReadAll --------------------------------------------------------------------------- Get a list of all the files from a directory. The method recursively fetches files from the main and the sub-folders. The dotfiles are ignored implicitly. Copy code to clipboard import { fsReadAll } from '@adonisjs/core/helpers' const files = await fsReadAll(new URL('./config', import.meta.url), { pathType: 'url' }) await Promise.all(files.map((file) => import(file))) You can also pass the options along with the directory path as the second argument. Copy code to clipboard type Options = { ignoreMissingRoot?: boolean filter?: (filePath: string, index: number) => boolean sort?: (current: string, next: string) => number pathType?: 'relative' | 'unixRelative' | 'absolute' | 'unixAbsolute' | 'url' } const options: PartialHello { username }
" } Copy code to clipboard {{{ t('messages.greeting', { username: 'Virk' }) }}} ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#number-format) Number format You can format numeric values within the translation messages using the `{key, type, format}` syntax. In the following example: * The `amount` is the runtime value. * The `number` is the formatting type. * And the `::currency/USD` is the currency format with a [number skeleton](https://unicode-org.github.io/icu/userguide/format_parse/numbers/skeletons.html#overview) Copy code to clipboard { "bagel_price": "The price of this bagel is {amount, number, ::currency/USD}" } Copy code to clipboard {{ t('bagel_price', { amount: 2.49 }) }} Copy code to clipboard The price of this bagel is $2.49 The following are examples of using the `number` format with different formatting styles and number skeletons. Copy code to clipboard Length of the pole: {price, number, ::measure-unit/length-meter} Copy code to clipboard Account balance: {price, number, ::currency/USD compact-long} ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#datetime-format) Date/time format You may format the [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) instances or the [luxon DateTime](https://moment.github.io/luxon/api-docs/index.html) instances using the `{key, type, format}` syntax. In the following example: * The `expectedDate` is the runtime value. * The `date` is the formatting type. * And the `medium` is the date format. Copy code to clipboard { "shipment_update": "Your package will arrive on {expectedDate, date, medium}" } Copy code to clipboard {{ t('shipment_update', { expectedDate: luxonDateTime }) }} Copy code to clipboard Your package will arrive on Oct 16, 2023 You can use the `time` format to format the value as a time. Copy code to clipboard { "appointment": "You have an appointment today at {appointmentAt, time, ::h:m a}" } Copy code to clipboard You have an appointment today at 2:48 PM ICU provides a [wide array of patterns](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table) to customize the date-time format. However, not all of them are available via ECMA402's Intl API. Therefore, we only support the following patterns. | Symbol | Description | | --- | --- | | `G` | Era designator | | `y` | year | | `M` | month in year | | `L` | stand-alone month in year | | `d` | day in month | | `E` | day of week | | `e` | local day of week e..eee is not supported | | `c` | stand-alone local day of week c..ccc is not supported | | `a` | AM/PM marker | | `h` | Hour \[1-12\] | | `H` | Hour \[0-23\] | | `K` | Hour \[0-11\] | | `k` | Hour \[1-24\] | | `m` | Minute | | `s` | Second | | `z` | Time Zone | ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#plural-rules) Plural rules ICU message syntax has first-class support for defining the plural rules within your messages. For example: In the following example, we use YAML over JSON since writing multiline text in YAML is easier. Copy code to clipboard cart_summary: "You have {itemsCount, plural, =0 {no items} one {1 item} other {# items} } in your cart" Copy code to clipboard {{ t('messages.cart_summary', { itemsCount: 1 }) }} Copy code to clipboard You have 1 item in your cart The `#` is a special token to be used as a placeholder for the numeric value. It will be formatted as `{key, number}`. Copy code to clipboard {{ t('messages.cart_summary', { itemsCount: 1000 }) }} {{-- Output --}} {{-- You have 1,000 items in your cart --}} The plural rule uses the `{key, plural, matches}` syntax. The `matches` is a literal value matched to one of the following plural categories. | Category | Description | | --- | --- | | `zero` | This category is used for languages with grammar specialized specifically for zero number of items. (Examples are Arabic and Latvian) | | `one` | This category is used for languages with grammar explicitly specialized for one item. Many languages, but not all, use this plural category. (Many popular Asian languages, such as Chinese and Japanese, do not use this category.) | | `two` | This category is used for languages that have grammar explicitly specialized for two items. (Examples are Arabic and Welsh.) | | `few` | This category is used for languages with grammar explicitly specialized for a small number of items. For some languages, this is used for 2-4 items, for some 3-10 items, and other languages have even more complex rules. | | `many` | This category is used for languages with specialized grammar for a more significant number of items. (Examples are Arabic, Polish, and Russian.) | | `other` | This category is used if the value doesn't match one of the other plural categories. Note that this is used for "plural" for languages (such as English) that have a simple "singular" versus "plural" dichotomy. | | `=value` | This is used to match a specific value regardless of the plural categories of the current locale. | > _The table's content is referenced from [formatjs.io](https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format) > _ ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#select) Select The `select` format lets you choose the output by matching a value against one of the many choices. Writing gender-specific text is an excellent example of the `select` format. Yaml Copy code to clipboard auto_reply: "{gender, select, male {He} female {She} other {They} } will respond shortly." Copy code to clipboard {{ t('messages.auto_reply', { gender: 'female' }) }} Copy code to clipboard She will respond shortly. ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#select-ordinal) Select ordinal The `select ordinal` format allows you to choose the output based on the ordinal pluralization rules. The format is similar to the `select` format. However, the value is mapped to an ordinal plural category. Copy code to clipboard anniversary_greeting: "It's my {years, selectordinal, one {#st} two {#nd} few {#rd} other {#th} } anniversary" Copy code to clipboard {{ t('messages.anniversary_greeting', { years: 2 }) }} Copy code to clipboard It's my 2nd anniversary The select ordinal format uses the `{key, selectordinal, matches}` syntax. The match is a literal value and is matched to one of the following plural categories. | Category | Description | | --- | --- | | `zero` | This category is used for languages with grammar specialized specifically for zero number of items. (Examples are Arabic and Latvian.) | | `one` | This category is used for languages with grammar explicitly specialized for one item. Many languages, but not all, use this plural category. (Many popular Asian languages, such as Chinese and Japanese, do not use this category.) | | `two` | This category is used for languages that have grammar explicitly specialized for two items. (Examples are Arabic and Welsh.) | | `few` | This category is used for languages with grammar explicitly specialized for a small number of items. For some languages, this is used for 2-4 items, for some 3-10 items, and other languages have even more complex rules. | | `many` | This category is used for languages with specialized grammar for a larger number of items. (Examples are Arabic, Polish, and Russian.) | | `other` | This category is used if the value doesn't match one of the other plural categories. Note that this is used for "plural" for languages (such as English) that have a simple "singular" versus "plural" dichotomy. | | `=value` | This is used to match a specific value regardless of the plural categories of the current locale. | > _The table's content is referenced from [formatjs.io](https://formatjs.io/docs/core-concepts/icu-syntax/#selectordinal-format) > _ [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatting-values) Formatting values -------------------------------------------------------------------------------------------- The following methods under the hood use the [Node.js Intl API](https://nodejs.org/dist/latest/docs/api/intl.html) but have better performance. [See benchmarks](https://github.com/poppinss/intl-formatter?tab=readme-ov-file#benchmarks) ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatnumber) formatNumber Format a numeric value using the `Intl.NumberFormat` class. You may pass the following arguments. 1. The value to format. 2. An optional [`options` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options) . Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatNumber(123456.789, { maximumSignificantDigits: 3 }) ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatcurrency) formatCurrency Format a numeric value as a currency using the `Intl.NumberFormat` class. The `formatCurrency` method implicitly defines the `style = currency` option. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatCurrency(200, { currency: 'USD' }) ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatdate) formatDate Format a date or a luxon date-time object using the `Intl.DateTimeFormat` class. You may pass the following arguments. 1. The value to format. It could be a [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object or a [luxon DateTime](https://moment.github.io/luxon/api-docs/index.html) object. 2. An optional [`options` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options) . Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' import { DateTime } from 'luxon' i18nManager .locale('en') .formatDate(new Date(), { dateStyle: 'long' }) // Format luxon date time instance i18nManager .locale('en') .formatDate(DateTime.local(), { dateStyle: 'long' }) ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formattime) formatTime Format a date value to a time string using the `Intl.DateTimeFormat` class. The `formatTime` method implicitly defines the `timeStyle = medium` option. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatTime(new Date()) ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatrelativetime) formatRelativeTime The `formatRelativeTime` method uses the `Intl.RelativeTimeFormat` class to format a value to a relative time representation string. The method accepts the following arguments. * The value to format. * The formatting unit. Along with the [officially supported units](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/format) , we also support an additional `auto` unit. * An optional [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat#options) object. Copy code to clipboard import { DateTime } from 'luxon' import i18nManager from '@adonisjs/i18n/services/main' const luxonDate = DateTime.local().plus({ hours: 2 }) i18nManager .locale('en') .formatRelativeTime(luxonDate, 'hours') Set the unit's value to `auto` to display the diff in the best matching unit. Copy code to clipboard const luxonDate = DateTime.local().plus({ hours: 2 }) I18n .locale('en') .formatRelativeTime(luxonDate, 'auto') // In 2 hours 👈 Copy code to clipboard const luxonDate = DateTime.local().plus({ hours: 200 }) I18n .locale('en') .formatRelativeTime(luxonDate, 'auto') // In 8 days 👈 ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatplural) formatPlural Find the plural category for a number using the `Intl.PluralRules` class. You may pass the following arguments. 1. The numeric value for which to find the plural category. 2. An optional [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/PluralRules#options) object. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager.i18nManager('en').formatPlural(0) // other i18nManager.i18nManager('en').formatPlural(1) // one i18nManager.i18nManager('en').formatPlural(2) // other ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatlist) formatList Format an array of strings to a sentence using the `Intl.ListFormat` class. You may pass the following arguments. 1. The value to format. 2. An optional [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#options) object. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatList(['Me', 'myself', 'I'], { type: 'conjunction' }) // Me, myself and I Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatList(['5 hours', '3 minutes'], { type: 'unit' }) // 5 hours, 3 minutes ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#formatdisplaynames) formatDisplayNames Format `currency`, `language`, `region`, and `calendar` codes to their display names using the `Intl.DisplayNames` class. You may pass the following arguments. 1. The code to format. The [value of `code` varies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames/of#code) depending on the `type` of formatting. 2. [Options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames/DisplayNames#options) object. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatDisplayNames('INR', { type: 'currency' }) // Indian Rupee Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' i18nManager .locale('en') .formatDisplayNames('en-US', { type: 'language' }) // American English [](https://docs.adonisjs.com/guides/digging-deeper/i18n#configuring-the-i18n-ally-vscode-extension) Configuring the i18n Ally VSCode extension ---------------------------------------------------------------------------------------------------------------------------------------------- The [i18n Ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) extension for VSCode provides an excellent workflow for **storing**, **inspecting**, and **referencing** translations with your code editor. To make the extension work seamlessly with AdonisJS, you must create the following files inside the `.vscode` directory of your project root. Copy code to clipboard mkdir .vscode touch .vscode/i18n-ally-custom-framework.yml touch .vscode/settings.json Copy/paste the following contents inside the `settings.json` file. .vscode/settings.json Copy code to clipboard { "i18n-ally.localesPaths": [\ "resources/lang"\ ], "i18n-ally.keystyle": "nested", "i18n-ally.namespace": true, "i18n-ally.editor.preferEditor": true, "i18n-ally.refactor.templates": [\ {\ "templates": [\ "{{ t('{key}'{args}) }}"\ ],\ "include": [\ "**/*.edge",\ ],\ },\ ] } Copy/paste the following contents inside the `.vscode/i18n-ally-custom-framework.yml` file. .vscode/i18n-ally-custom-framework.yml Copy code to clipboard languageIds: - edge usageMatchRegex: - "[^\\w\\d]t\\(['\"`]({key})['\"`]" sortKeys: true [](https://docs.adonisjs.com/guides/digging-deeper/i18n#listening-for-missing-translations-event) Listening for missing translations event ------------------------------------------------------------------------------------------------------------------------------------------ You may listen to the `i18n:missing:translation` event to get notified about the missing translations in your app. Copy code to clipboard import emitter from '@adonisjs/core/services/emitter' emitter.on('i18n:missing:translation', function (event) { console.log(event.identifier) console.log(event.hasFallback) console.log(event.locale) }) [](https://docs.adonisjs.com/guides/digging-deeper/i18n#force-reloading-translations) Force reloading translations ------------------------------------------------------------------------------------------------------------------ The `@adonisjs/i18n` package reads the translation files when booting the application and stores them within the memory for quick access. However, if you modify the translation files while your application is running, you may use the `reloadTranslations` method to refresh the in-memory cache. Copy code to clipboard import i18nManager from '@adonisjs/i18n/services/main' await i18nManager.reloadTranslations() [](https://docs.adonisjs.com/guides/digging-deeper/i18n#creating-a-custom-translation-loader) Creating a custom translation loader ---------------------------------------------------------------------------------------------------------------------------------- A translations loader is responsible for loading translations from a persistent store. We ship with a file system loader and provide an API to register custom loaders. A loader must implement the [TranslationsLoaderContract](https://github.com/adonisjs/i18n/blob/main/src/types.ts#L73) interface and define the `load` method that returns an object with key-value pair. The key is the locale code, and the value is a flat object with a list of translations. Copy code to clipboard import type { LoaderFactory, TranslationsLoaderContract, } from '@adonisjs/i18n/types' /** * Type for the configuration */ export type DbLoaderConfig = { connection: string tableName: string } /** * Loader implementation */ export class DbLoader implements TranslationsLoaderContract { constructor(public config: DbLoaderConfig) { } async load() { return { en: { 'messages.greeting': 'Hello world', }, fr: { 'messages.greeting': 'Bonjour le monde', } } } } /** * Factory function to reference the loader * inside the config file. */ export function dbLoader(config: DbLoaderConfig): LoaderFactory { return () => { return new DbLoader(config) } } In the above code example, we export the following values. * `DbLoaderConfig`: TypeScript type for the configuration you want to accept. * `DbLoader`: The loaders's implementation as a class. It must adhere to the `TranslationsLoaderContract` interface. * `dbLoader`: Finally, a factory function to reference the loader inside the config file. ### [](https://docs.adonisjs.com/guides/digging-deeper/i18n#using-the-loader) Using the loader Once the loader has been created, you can reference it inside the config file using the `dbLoader` factory function. Copy code to clipboard import { defineConfig } from '@adonisjs/i18n' import { dbLoader } from 'my-custom-package' const i18nConfig = defineConfig({ loaders: [\ dbLoader({\ connection: 'pg',\ tableName: 'translations'\ })\ ] }) [](https://docs.adonisjs.com/guides/digging-deeper/i18n#creating-a-custom-translation-formatter) Creating a custom translation formatter ---------------------------------------------------------------------------------------------------------------------------------------- Translation formatters are responsible for formatting the translations as per a specific format. We ship with an implementation for the ICU message syntax and provide additional APIs to register custom formatters. A formatter must implement the [TranslationsFormatterContract](https://github.com/adonisjs/i18n/blob/main/src/types.ts#L54) interface and define the `format` method to format a translation message. Copy code to clipboard import type { FormatterFactory, TranslationsLoaderContract, } from '@adonisjs/i18n/types' /** * Formatter implementation */ export class FluentFormatter implements TranslationsFormatterContract { format( message: string, locale: string, data?: Record Click here to verify your email address
`)
/**
* Plain text contents
*/
message.text(`
Verify email address
Please visit https://myapp.com to verify your email address
`)
})
#### [](https://docs.adonisjs.com/guides/digging-deeper/mail#using-edge-templates)
Using Edge templates
Since writing inline content could be cumbersome, you may use Edge templates instead. If you have already [configured Edge](https://docs.adonisjs.com/guides/views-and-templates/introduction#configuring-edge)
, you may use the `message.htmlView` and `message.textView` methods to render templates.
Create templates
Copy code to clipboard
node ace make:view emails/verify_email_html
node ace make:view emails/verify_email_text
Use them for defining contents
Copy code to clipboard
await mail.send((message) => {
message.htmlView('emails/verify_email_html', stateToShare)
message.textView('emails/verify_email_text', stateToShare)
})
#### [](https://docs.adonisjs.com/guides/digging-deeper/mail#using-mjml-for-email-markup)
Using MJML for email markup
MJML is a markup language for creating emails without writing all the complex HTML to make your emails look good in every email client.
The first step is to install the [mjml](https://npmjs.com/mjml)
package from npm.
Copy code to clipboard
npm i mjml
Once done, you can write MJML markup inside your Edge templates by wrapping it inside the `@mjml` tag.
Since the output of MJML contains the `html`, `head`, and `body` tags, it is unnecessary to define them within your Edge templates.
Copy code to clipboard
@mjml()
Hello {{ user.name }} Hello {props.user.name} Hello {user.name} Hello {props.user.name}
Following will be the output HTML
Copy code to clipboard
The following attachment will be defined automatically on the email payload.
Copy code to clipboard
{
attachments: [{\
path: '/root/app/assets/hero.jpg',\
filename: 'hero.jpg',\
cid: 'a-random-content-id'\
}]
}
#### [](https://docs.adonisjs.com/guides/digging-deeper/mail#embedding-images-from-buffers)
Embedding images from buffers
Like the `embedImage` method, you may use the `embedImageData` method to embed an image from raw data.
Copy code to clipboard
### [](https://docs.adonisjs.com/guides/digging-deeper/mail#attaching-calendar-events)
Attaching calendar events
You may attach calendar events to an email using the `message.icalEvent` method. The `icalEvent` method accepts the event contents as the first parameter and the `options` object as the second parameter.
Copy code to clipboard
const contents = 'BEGIN:VCALENDAR\r\nPRODID:-//ACME/DesktopCalendar//EN\r\nMETHOD:REQUEST\r\n...'
await mail.send((message) => {
message.icalEvent(contents, {
method: 'PUBLISH',
filename: 'invite.ics',
})
})
Since defining the event file contents manually can be cumbersome, you may pass a callback function to the `icalEvent` method and generate the invite contents using JavaScript API.
The `calendar` object provided to the callback function is a reference of the [ical-generator](https://www.npmjs.com/package/ical-generator)
npm package, so make sure to go through the package's README file as well.
Copy code to clipboard
message.icalEvent((calendar) => {
calendar
.createEvent({
summary: 'Adding support for ALS',
start: DateTime.local().plus({ minutes: 30 }),
end: DateTime.local().plus({ minutes: 60 }),
})
}, {
method: 'PUBLISH',
filename: 'invite.ics',
})
#### [](https://docs.adonisjs.com/guides/digging-deeper/mail#reading-invite-contents-from-a-file-or-a-url)
Reading invite contents from a file or a URL
You may define the invite contents from a file or an HTTP URL using the `icalEventFromFile` or `icalEventFromUrl` methods.
Copy code to clipboard
message.icalEventFromFile(
app.resourcesPath('calendar-invites/invite.ics'),
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)
Copy code to clipboard
message.icalEventFromUrl(
'https://myapp.com/users/1/invite.ics',
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)
### [](https://docs.adonisjs.com/guides/digging-deeper/mail#defining-email-headers)
Defining email headers
You may define additional email headers using the `message.header` method. The method accepts the header key as the first parameter and the value as the second parameter.
Copy code to clipboard
message.header('x-my-key', 'header value')
/**
* Define an array of values
*/
message.header('x-my-key', ['header value', 'another value'])
By default, the email headers are encoded and folded to meet the requirement of having plain ASCII messages with lines no longer than 78 bytes. However, if you want to bypass the encoding rules, you may set a header using the `message.preparedHeader` method.
Copy code to clipboard
message.preparedHeader(
'x-unprocessed',
'a really long header or value with non-ascii characters 👮',
)
### [](https://docs.adonisjs.com/guides/digging-deeper/mail#defining-list-headers)
Defining `List` headers
The message class includes helper methods to define complex headers like [List-Unsubscribe](https://sendgrid.com/en-us/blog/list-unsubscribe)
or [List-Help](https://support.optimizely.com/hc/en-us/articles/4413200569997-Setting-up-the-List-Help-header#heading-2)
with ease. You can learn about the encoding rules for `List` headers on the [nodemailer website](https://nodemailer.com/message/list-headers/)
.
Copy code to clipboard
message.listHelp('admin@example.com?subject=help')
// List-Help: