Alright, sit tight, as this will be a long article. The work for v6 started with the goal of moving to ESM and improving the IoC container to be simple andhave fewer responsibilities.
But we have touched almost every part of the framework, smoothing out many rough edges, fixing some long pending issues, and rewriting some packages from scratch.
NOTE
Are you looking to migrate your applications from v5 to v6? Check out themigration-to-v6.adonisjs.com website for a complete list of breaking changes.
Also, we have created amigration CLI that can handle the majority of migration work for you.
Moving to ESM
ESM (ECMAScript Modules) vs CJS (CommonJS) might be a topic of debate among many JavaScript developers. But we are not here to discuss the merits and drawbacks of one or the other.
We went with ESM because it is part of the spec. Yes, CJS might live the entirety of this universe, but the fact that a project using CJS cannot easily import ESM modules is a big enough pain point for us.
Many prolific authors (whom we rely on) already started moving their packages to ESM. As a result, if we keep the AdonisJS source code in CJS, we cannot use the latest versions of their packages, which may also contain several security fixes.
Starting from v6, every new AdonisJS application will use TypeScript and ESM. Yes, you can still install and use packages written in CJS, as ESM allows that.
Stop relying on TypeScript compiler hooks
I am not a fan of hacking into tools, primarily when the code is written for public consumption. However, with AdonisJS v5, we hook into the TypeScript compiler API and rewrite the imports prefixed with the@ioc
keyword to IoC container lookup calls.
For example, if you write the following import.
importRoutefrom'@ioc:Adonis/Core/Route'
We will compile it to
constRoute=global[Symbol.for('ioc.use')]('Adonis/Core/Route')
There are two problems with the above transformation.
- We rely on the official compiler API. As a result, we cannot use other JIT tools like ESBuild or SWC written in other faster languages.
- We have to inject a global IoC container variable to resolve the module from the container.
Do not worry if you do not understand the container usage in this example. We have removed all this magic, and imports in v6 are regular JavaScript imports.
If you have been using AdonisJS v5 for a long time, wonder what happened to@ioc
prefixed imports. Remember, there were better ways to resolve dependencies from the container. We have found a much simpler way to resolve container dependenciesin the form of container services.
Type-safe routes and controllers binding
Earlier, we used magic strings to bind a controller to a route. For example, This is how the route + controller usage looks in v5.
Route.get('posts','PostsController.index')
The'PostsController.index'
is a magic string because, for TypeScript, it has no real meaning and cannot detect and report errors.
Starting from v6, we no longer recommend using magic strings. You can directly import controllers and bind them on a route by reference. For example:
importPostsControllerfrom'#controllers/posts_controller'router.get('posts',[PostsController,'index'])
However, there was one nice thing about magic strings. They allowed us to import the controllers lazily. Since controllers import the rest of the codebase, importing them within the routes file impacts the application's boot time.
In v6, you can lazily import a controller by wrapping it inside a function and using dynamic import.
You can detect and automatically convert controller imports to a lazy import using ourESLint plugin.
constPostsController=()=>import('#controllers/posts_controller')router.get('posts',[PostsController,'index'])
Type-safe named middleware reference
In AdonisJS v5, you reference the named middleware defined inside thestart/kernel.ts
file on a route as a string. For example:
Server.middleware.registerNamed({auth:()=>import('App/Middleware/Auth')})
Route.get('/me',()=>{}).middleware('auth')// Passing optionsRoute.get('/me',()=>{}).middleware('auth:web,api')
Since we are referencing the middleware as a string and passing the options as a string, there is no type-safety.
Starting from v6, the named middleware are defined by reference using themiddleware
collection exported from thestart/kernel.ts
file.
exportconstmiddleware=router.named({auth:()=>import('#middleware/auth_middleware')})
import{middleware}from'#start/kernel'router.get('/',()=>{}).use(middleware.auth())// Passing optionsrouter.get('/',()=>{}).use(middleware.auth({guards:['web','api']}))
Type-safe AdonisRC file
We have moved from a JSON-based RCFile (.adonisrc.json
) to a TypeScript-based RCFile (adonisrc.ts
).
This change allows us to directly importservice providers,commands, andpreload files instead of defining their import path as strings.
As a result, TypeScript can detect and report broken imports. Also, you can have better IntelliSense when modifying the values in the RCFile.
Type-safe Event emitter
To make the event emitter type-safe, we define a list of known events as a TypeScript interface, and from thereon, the emitter API only allows dispatching and listening for known events.
interfaceEventsList{'user:registered':User,'http:request_completed':{duration:[number,number],ctx:HttpContext}}
The ability to define an events list as an interface also exists with the older version of AdonisJS.
However, with v6, you can alsodefine events as classes. Class-based events encapsulate the event identifier and the event data within the same class. The class constructor serves as the identifier, and an instance of the class holds the event data. For example:
// Defining eventimporttypeUserfrom'#models/user'import{BaseEvent}from'@adonisjs/core/events'exportdefaultclassUserRegisteredextendsBaseEvent{constructor(publicuser:User){}}
// Listening for class-based eventimportemitterfrom'@adonisjs/core/services/emitter'importUserRegisteredfrom'#events/user_registered'emitter.on(UserRegistered,function(event){console.log(event.user)})
// Dispatching class-based eventimportUserRegisteredfrom'#events/user_registered'constuser=newUser()UserRegistered.dispatch(user)
You canlearn about class-based events in the documentation.
Vite integration for bundling frontend assets
Vite has become the de facto standard for building frontend applications. With this release, we ship anofficial integration for using Vite inside AdonisJS applications.
Also, we no longer recommend usingWebpack Encore for new projects. However, we will continue to maintain this package for existing v5 applications.
New scaffolding system and codemods API
The scaffolding system and codemods API are used by the package creators to configure a package or by Ace commands to scaffold a resource.
We have written anin-depth guide on the same that you must read to learn more about it.
New validation library
The current validation module of AdonisJS has served us well, but it desperately needs some improvements. Right now:
It lacks a union data type. There is no way to validate a field as a string or a number.
The API to create custom rules is rough. We have witnessed many individuals struggling to create custom rules.
The state of the package codebase was not great. It made it harder for us to make big changes confidently. A significant rewrite was needed.
Finally, we developed a framework agnostic validation library calledVineJS. VineJS will be the official validation system for AdonisJS v6.
VineJS is much faster than the version used in V5, and it's also more comprehensive. It makes it easy to create custom rules and schema types and validate complex schemas.
You can learn more about VineJS in our introduction live stream.https://www.youtube.com/watch?v=YdBt0s8NA4I
What happens to the old validator?
We will maintain the old validator (without bringing any features) you can use while migrating apps to v6. However, we recommend using VineJS for new applications.
New and more Framework agnostic packages
Lately, we have decided to extract/create more framework-agnostic packages (wherever possible) with their dedicated documentation websites.
As a result, we are happy to announce that you can use the following packages with any Node.js framework of your choice.
Japa - A backend focused testing framework for Node.js.
Edge - In the age of complex frontend libraries and frameworks, Edge embraces old-school server-side rendering. Edge is a simple, Modern, and batteries-included template engine for Node.js.
VineJS - VineJS is a data validation library for the Node.js runtime. It is at least 9 times faster than Zod and Yup.
Bento Cache - Bentocache is a robust multi-tier caching library for the Node.js runtime.
Verrou - Verrou is a library for managing locks in a Node.js application.
Improved documentation
There were some lapses in AdonisJS's documentation. For example, topics like IoC Container and Service providers were undocumented. Also, some of the guides should have been more comprehensive.
With AdonisJS v6, we have spent significant time covering all the framework aspects within the documentation. Following are some of the newly added topics.
Documented usage ofIoC container andcontainer services.
Dedicated reference section forEdge helpers,available commands,events, andexceptions list.
Extensive documentation forcreating andtesting commands using Ace.
Dedicated guide forextending the framework.
HTTP introduction guide explaining the flow of an HTTP request.
Dedicated guide for theBodyParser middleware.
Detailed guide for middleware, covering topics likemutating response,exception handling, andtesting middleware in isolation.
Better testing experience
We keep testing as one of the top priorities of the framework. Not only do we pre-configure a testing environment for your applications, but we also ensure the core APIs for the framework are testing-friendly.
Starting from v6, we ship:
First-class assertion APIs fortesting emitted events.
Ability to test ace commands, write assertions forlogger output, andtrap prompts.
Fake outgoing emails andwrite assertions against them.
Support for writingBrowser tests using Playwright.
And, adedicated VSCode extension to run Japa tests without leaving your code editor.
Changes to the folder structure
While 80% of the folder structure of a v6 application remains the same, we move fromPascalCase
tosnake_case
for naming files and folders.
Whysnake_case
? - Last year, I documented the rules and conventions I follow when writing code. I briefly talk about thereasons behind opting forsnake_case
.
NOTE
The
snake_case
naming convention is part of the official starter kits. However, you have the freedom to create and use custom starter kits with the naming conventions of your choice.
The rest 20% of folder structure changes include:
Move entry point files to the
bin
folder. You will no longer seeserver.ts
andtest.ts
inside the application's root. These files are now inside thebin
directory.Remove the
.adonisrc.json
file in favor of theadonisrc.ts
file.Learn more.Rename the
contracts
directory totypes
.Move the
Controllers
directory outside theHttp
directory. As a result, there is noHttp
directory in a v6 app. Controllers live directly inside theapp
directory.Move the
env.ts
file from the project root to thestart
directory.
MJML support and additional mail transports
The@adonisjs/mail
package now bundles additional transports forResend and theBrevo mail services. Additionally, it contributes the@mjml
Edge tag to write email content using theMJML markup language.
The contents of the following template will be auto-compiled to HTML on the fly when sending the email.Learn more
@mjml()<mjml><mj-body><mj-section><mj-column><mj-text> Hello World!</mj-text></mj-column></mj-section></mj-body></mjml>@end
Sunsetting packages
The following packages are no longer used with a brand new AdonisJS v6 application.
Remove
@adonisjs/encore
in favor of Vite. However, the package is compatible with v6 and can be used until you decide to move to Vite.Remove
@adonisjs/validator
in favor of VineJS. However, the package is compatible with v6 and can be used until you decide to move to VineJS.Remove
@adonisjs/sink
in favor of the new scaffolding system and code mods API. No longer support v6 applications.Remove
@adonisjs/require-ts
in favor of TSNode + SWC. No longer support v6 applications.Remove
@adonisjs/view
in favor of directly using Edge.js. No longer support v6 applications.
Other changes
Following is the list of additional notable changes in the v6 release.
Support loading additional dot-env files other than the
.env
file.Learn moreThe
@adonisjs/logger
package uses the latest version of Pino and supports defining multiple loggers.Learn moreSupport for experimental
partitioned
andpriority
cookie options.Remove support for serving static files from the framework core in favor of the new@adonisjs/static package.
Remove support for CORS from the framework core in favor of the new@adonisjs/cors package.
Ready to get started
Head to theofficial documentation to learn more about AdonisJS and create a new project. Or check out theLet's Learn AdonisJS 6 course fromTom at Adocasts.
Additional Links
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse