New The AdonisJS roadmap is live. See what's coming

The batteries-included TypeScript framework

Everything you need in one Node.js framework. Authentication, ORM, validation, mail, queues, cache, testing - all working together. Built for teams who want to ship products, not assemble frameworks.

Code you'll actually enjoy writing

AdonisJS combines expressive APIs with clear conventions and full TypeScript support. Explore the examples below to see how common tasks stay simple without sacrificing power

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'

router
  .group(() => {
    router.get('login', [controllers.Session, 'create'])
    router.post('login', [controllers.Session, 'store'])

    router.get('signup', [controllers.NewAccount, 'create'])
    router.post('signup', [controllers.NewAccount, 'store'])
  })
  .use(middleware.guest())

// Creates a total of 7 routes to manage posts
router.resource('posts', controllers.Posts)
import Post from '#models/post'
import { HttpContext } from '@adonisjs/core/http'

export default class PostsController {
  async index({ request }: HttpContext) {
    const posts = await Post.query().preload('author').paginate(1, 20)
    return posts
  }

  async store({ request, auth, response }: HttpContext) {
    const data = request.validateUsing(createPostValidator)    
    const post = await auth.user.related('posts').create(data)

    return response.created(post)
  }
}
import vine from '@vinejs/vine'

export const createPostValidator = vine.create({
  title: vine.string().minLength(3).maxLength(255),
  content: vine.string().minLength(10),
  coverImage: vine.file({
    size: '2mb',
    extnames: ['jpg', 'png', 'webp']
  }).optional()
})

export const createUserValidator = vine.create({
  fullName: vine.string().nullable(),
  email: vine.string().email().unique({ table: 'users' }),
  password: vine.string().confirmed()
})
import User from '#models/user'
import Comment from '#models/comment'
import { PostSchema } from '#database/schema'
// ...other imports

export default class Post extends PostSchema {
  @belongsTo(() => User)
  author: BelongsTo<typeof User>

  @hasMany(() => Comment)
  comments: HasMany<typeof Comment>
}

await Post.all()
await Post.query().preload('author').where('id', 1).firstOrFail()
await Post.query().withCount('comments').paginate(1, 20)
// ...imports
export default class AuthController {
  async login({ request, auth }: HttpContext) {
    const user = await User.verifyCredentials(email, password)
    // Create session for user
    await auth.use('web').login(user)
  }

  async loginWithToken({ request }: HttpContext) {
    const user = await User.verifyCredentials(email, password)

    // Create token for user
    const token = await User.accessTokens.create(user)
    return { token: token.value!.release() }
  }
}
// ...imports
export default class AssetsController {
  async store({ request, response }: HttpContext) {
    const { file } = await request.validateUsing(createFileValidator)

    const fileName = `${uuid()}.${file.extname}`
    await file.moveToDisk(fileName, 'r2')
    
    return response.created({ fileName, url: `/assets/${fileName}` })
  }

  async show({ params, response }: HttpContext) {
    const stream = await drive.use('r2').getStream(params.fileName)
    return response.stream(stream)
  }
}
// ...imports
export default class WelcomeMail extends BaseMail {
  subject = 'Welcome to Acme!'
  constructor(protected user: User) {
    super()
  }

  prepare() {
    this.message
      .to(this.user.email)
      .htmlView('emails/welcome', { user: this.user })
  }
}

// Usage example
await mail.send(new WelcomeMail(user))
import limiter from '@adonisjs/limiter/services/main'

export const apiThrottle = limiter.define('api', ({ auth, request }) => {
  if (auth.user) {
    const key = `user_${auth.user.id}`
    return limiter.allowRequests(100).every('1 minute').usingKey(key)
  }

  const key = `ip_${request.ip()}`
  return limiter.allowRequests(10).every('1 minute').usingKey(key)
})

// Usage example
router
  .get('api/repos', () => {})
  .use(apiThrottle)
import { test } from '@japa/runner'
import { UserFactory } from '#factories/user'

test.group('Posts store', (group) => {
  test('create post as authenticated user', async ({ client }) => {
    const user = await UserFactory.create()
    
    const response = await client
      .visit('posts.store')
      .loginAs(user)
      .json({ title: 'Hello World', content: 'My first post' })
    
    response.assertStatus(201)
    response.assertBodyContains(postData)
  })
})

Everything you need to build

Powerful developer tooling. Official packages for common requirements. Flexible architecture that adapts to your needs. Built to take you from prototype to production.

E2E type-safety
Type-safe environment variables
Type-safe event emitter
Type-safe serialization
E2E type-safety
Type-safe environment variables
Type-safe event emitter
Type-safe serialization
Vite integration
Server-side HMR
CLI / REPL
Pretty error pages
CSRF protection
CORS support
Vite integration
Server-side HMR
CLI / REPL
Pretty error pages
CSRF protection
CORS support
Opinionated Folder structure
Dependency container
ESM
OTEL
Subpath imports
Opinionated Folder structure
Dependency container
ESM
OTEL
Subpath imports

Developer experience that just works

Pretty error pages, powerful CLI, security primitives, seamless Vite integration, and end-to-end type safety. The tooling feels invisible until you need it.

Official packages, zero hunting

From caching to rate limiting to health checks, common requirements solved with official packages. No npm rabbit holes, no compatibility roulette

View all packages

MVC with a flexible view layer

AdonisJS gives you complete MVC architecture, but the View is up to you. Pair your models and controllers with React, Vue, server-rendered templates, or build a pure API. Structured backend, flexible frontend.

Our approach towards frontend
Backend controller
import User from '#models/user'
import type { HttpContext } from '@adonisjs/core/http'
import UserTransformer from '#transformers/user_transformer'

export default class UsersController {
  async index({ request, inertia }: HttpContext) {
    const users = await User.all()
    
    return inertia.render('users/index', {
      // props serialization with type inference
      users: UserTransformer.transform(users)
    })
  }
}
import User from '#models/user'
import type { HttpContext } from '@adonisjs/core/http'
import UserTransformer from '#transformers/user_transformer'

export default class UsersController {
  async index({ request, inertia }: HttpContext) {
    const users = await User.all()
    
    return inertia.render('users/index', {
      // props serialization with type inference
      users: UserTransformer.transform(users)
    })
  }
}
import User from '#models/user'
import type { HttpContext } from '@adonisjs/core/http'

export default class UsersController {
  async index({ request, view }: HttpContext) {
    const users = await User.all()
    
    return view.render('users/index', {
      users
    })
  }
}
import User from '#models/user'
import type { HttpContext } from '@adonisjs/core/http'
import UserTransformer from '#transformers/user_transformer'

export default class UsersController {
  async index({ request, serialize }: HttpContext) {
    const users = await User.all()
    
    return serialize(UserTransformer.transform(users))
  }
}
<script setup lang="ts">
  import { Data } from '~/generated/data'
  defineProps<{ users: Data.User[] }>()
</script>

<template>
  <div v-for="user in users" :key="user.id">
    <p>{{ user.email }}</p>
  </div>
</template>
import { InertiaProps } from '~/types'
import { Data } from '~/generated/data'

type PageProps = InertiaProps<{ users: Data.User[] }>

export default function UsersIndex({ users }: PageProps) {
  return <>
    {users.map((user) => (
      <div key={user.id}>
        <p>{user.email}</p>
      </div>
    ))}
  </>
}
@each(user in users)
  <div>
    <p>{{ user.email }}</p>
  <div>
@end
{
  "data": [
    {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
    }
  ]
}

Releases

Changelog
May 05 adonisjs/vite
Fix broken assets issue when the first request hit server before Vite devserver is ready
Apr 27 adonisjs/cache
Release 1.3.2
Apr 27 adonisjs/create-adonisjs
Display AI-friendly error when invoked via AI agents
Apr 26 adonisjs/content
Add loader to fetch Github projects and cache them
Apr 14 adonisjs/http-server
Security fix: open redirect in response.redirect().back()
Apr 13 adonisjs/ally
Consider email_verified flag in LinkedIN OC response and better defaults

Going strong for over a decade

No hype, no chasing trends. Just regular updates and commitment to building something that lasts

MIT Licensed

Proudly supported by our partners

These companies and individuals keep AdonisJS thriving. Their monthly support funds development, maintenance, and community growth - allowing us to stay independent and focused on building the best framework we can

Become a partner - 300k monthly visits

The framework you've been looking for

Developers are discovering what Laravel and Rails fans have known for years - batteries-included frameworks just work better

Read more stories
@techreagan
@techreagan

@adonisframework is the first ever framework I learnt from the docs, I'm in love with this framework, it's just like @Laravel but for the @nodejs world. I will be like I'm stuck how can i solve this, the docs got you covered. It's gonna be my long term buddy.

@britzdm
@britzdm

I've been using @adonisframework for the last two days an I've already got more done then I did in the last 3 weeks with just using expressjs.

r/node

I've been using Adonis for almost five years. I currently have applications running versions 4, 5, and 6, and for the most part I've really enjoyed the experience - it's my go-to for new projects. I've been playing with the recent support for SSR via Inertia and it has been pretty decent so far.

@mdthansil
@mdthansil

Every day I use AdonisJS, and it uncovers new gems for me; I love it more and more! The combination of AdonisJS, InertiaJS, and React is simply amazing. I'm truly enjoying the experience. Thank you, @adonisframework, and the team.

@andruu
@andruu

So funny seeing everyone looking/begging for the node/ts equivalent of rails/Laravel when it's existed for years. I started using @adonisframework in 2016 in production for one of the biggest startups in Asia. If you want full stack with everything built in take a look.

Anjali
Anjali Pandey

Working on a recent project introduced me to AdonisJS, and it's been a great experience.

Coming from Node.js and Express, AdonisJS stood out with its clean MVC architecture, built-in authentication and validation, a powerful ORM, and a TypeScript-first approach.

It delivers a smooth, structured developer experience—very much like Laravel for Node.js. AdonisJS is an impressive framework that truly elevates backend development.

Ready to build?

Whether you're exploring AdonisJS for the first time or ready to build your next product, here's how to get started

SCREENCASTS

Video tutorials and courses teaching AdonisJS from basics to advanced

SCREENCASTS
Visit Adocasts
AdonisJS Plus

Pre-built full-stack components and starter kits to accelerate your projects

AdonisJS Plus
Visit AdonisJS Plus
COMMUNITY

Active Discord community where developers help each other build better applications

COMMUNITY
Join Discord
DOCS

Comprehensive documentation covering every feature, written with clarity and care.

DOCS
Read documentation