はじめに
Node.jsでバックエンドを作るとき、こんな経験はありませんか?Expressでプロジェクトを始めたものの、ORMはどれにするか、バリデーションライブラリは何を選ぶか、認証はどう組むか……と、コードを書き始める前の「ライブラリ選定」だけで丸一日が溶けてしまう。選んだライブラリ同士の相性が悪くて、つなぎ込みのグルーコードばかりが増えていく。
PHPのLaravelやRubyのRailsを触ったことがある方なら、「全部入りで最初から気持ちよく書ける、あの感覚がNode.jsにも欲しい」と思ったことがあるはずです。
その答えのひとつが、今回紹介するAdonisJSです。ルーティングからORM、バリデーション、認証、メール送信までを一貫した設計で提供する、TypeScriptファーストのフルスタックフレームワークです。
AdonisJSとは
AdonisJSは、Node.js向けのフルスタックWebフレームワークです。「Everything you need in one Node.js framework(必要なものはすべてこのフレームワークの中に)」を掲げ、Laravelの思想をNode.jsとTypeScriptの世界に持ち込んだ存在としてよく語られます。
GitHubのスター数は約19,000。Ledger、Renault Group、日産、Paytmといった企業での採用実績もあり、「知る人ぞ知る」から「実戦投入できる選択肢」へと着実に成長しています。2026年7月時点でもコアパッケージはv7系として活発に更新が続いています。
主な特徴
- バッテリー同梱(batteries included) - ORM(Lucid)、バリデーション(VineJS)、認証、メール、キュー、キャッシュ、テストまで公式パッケージで完結します。ライブラリ選定と組み合わせ検証の時間をほぼゼロにできます
- TypeScriptファースト - フレームワーク自体がTypeScriptで書かれており(リポジトリの99.9%がTypeScript)、ルートからモデルまでエンドツーエンドの型安全が効きます
- 優れた開発体験 -
aceという強力なCLI、Vite統合とHMR、原因が一目で分かる美しいエラーページなど、開発中の「待ち時間」と「調査時間」を削る工夫が随所にあります
インストール
AdonisJSは Node.js 24以上、npm 11以上 を要求します。プロジェクトの作成はコマンド1つです。
npm create adonisjs@latest my-app
作成時に4種類の公式スターターキットから選べます。
| キット | 用途 |
|---|---|
| Hypermedia | Edgeテンプレート + Alpine.jsによるサーバーレンダリング構成 |
| React | Inertia.js + Reactのフルスタック構成 |
| Vue | Inertia.js + Vueのフルスタック構成 |
| API | フロントエンド分離型のAPIサーバー構成 |
キットを指定して作成する場合は --kit オプションを使います。
npm create adonisjs@latest my-app -- --kit=api
作成できたら、開発サーバーを起動しましょう。
cd my-app
node ace serve --hmr
http://localhost:3333 にアクセスして、ウェルカムページが表示されれば準備完了です。--hmr フラグにより、コードを変更してもサーバーを再起動せずに反映されます。
基本的な使い方
ルーティング
ルートは start/routes.ts に定義します。Expressに慣れていれば違和感なく読めるはずです。
import router from '@adonisjs/core/services/router'
// 静的ルート
router.get('/', () => 'Hello world from the home page.')
// 動的パラメータ
router.get('/posts/:id', ({ params }) => {
return `This is post with id ${params.id}`
})
面白いのはパラメータのマッチャーです。where で制約を付けると、型まで変換してくれます。
router
.get('/posts/:id', ({ params }) => {
console.log(typeof params.id) // 'number' — 文字列ではなく数値になる
})
.where('id', router.matchers.number())
コントローラー
処理が増えてきたらコントローラーに切り出します。ace コマンドで雛形を生成できます。
node ace make:controller posts
最新のAdonisJSでは、生成されたコントローラーが型情報付きで自動収集され、ルート定義から型安全に参照できます。
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'
// 個別に割り当てる場合
router.get('/posts/:id', [controllers.Posts, 'show'])
// index / show / store / update / destroy などCRUDの7ルートを一括生成
router.resource('posts', controllers.Posts)
メソッド名のタイポはコンパイル時に検出されます。「ルート定義は文字列だらけで型が効かない」という従来のNode.jsフレームワークの弱点が、きれいに解消されています。
バリデーション
AdonisJSのバリデーションは、公式製のVineJSが担います。公称でZodの2倍以上高速で、非同期バリデーション(DBを見にいく一意性チェックなど)を最初からサポートしているのが特徴です。
バリデータは app/validators にリソースごとに定義します。
// app/validators/post.ts
import vine from '@vinejs/vine'
export const createPostValidator = vine.create({
title: vine.string(),
body: vine.string(),
publishedAt: vine.date(),
})
コントローラーからは request.validateUsing で呼び出すだけです。
// app/controllers/posts_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import { createPostValidator } from '#validators/post'
export default class PostsController {
async store({ request }: HttpContext) {
// バリデーション済み & 型付きのペイロードが返る
const payload = await request.validateUsing(createPostValidator)
return payload
}
}
バリデーションに失敗した場合の例外処理を自分で書く必要はありません。グローバルな例外ハンドラが自動的に422レスポンス(またはフォームへのエラー返却)へ変換してくれます。この「エラーハンドリングを書かなくていい」体験は、一度味わうと戻れなくなります。
実践的なユースケース
ここでは「ブログ記事API」を題材に、Lucid ORMとバリデーションを組み合わせた実装を見てみましょう。
Lucid ORMでモデルを定義する
Lucidは Active Record パターンのORMです。最新版では、マイグレーションから自動生成されるスキーマクラスをモデルが継承する設計になっており、カラム定義とビジネスロジックが明確に分離されます。
// database/schema.ts(自動生成される側のイメージ)
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { DateTime } from 'luxon'
export class PostsSchema extends BaseModel {
static table = 'posts'
@column({ isPrimary: true })
declare id: number
@column()
declare title: string
@column()
declare status: string
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
}
モデル本体には、リレーションや業務ロジックだけを書きます。スキーマクラスは再生成されるため、自分のコードはこちらに追加していきます。
// app/models/post.ts
import { belongsTo } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import { PostsSchema } from '#database/schema'
import User from './user.js'
export default class Post extends PostsSchema {
@belongsTo(() => User, { foreignKey: 'authorId' })
declare author: BelongsTo<typeof User>
isPublished(): boolean {
return this.status === 'published'
}
async publish() {
this.status = 'published'
await this.save()
}
}
クエリを書く
クエリビルダーはメソッドチェーンで直感的に書けます。リレーションの事前読み込み(N+1問題の回避)も preload 一発です。
import Post from '#models/post'
// 公開済み記事を著者情報付きで新しい順に取得
const posts = await Post.query()
.where('status', 'published')
.preload('author')
.orderBy('createdAt', 'desc')
// 1件取得して公開処理(見つからなければ自動で404)
const post = await Post.findOrFail(params.id)
await post.publish()
findOrFail は該当レコードがない場合に例外を投げ、フレームワークがそのまま404レスポンスに変換します。「取得→存在チェック→404を返す」という定型コードが不要になるわけです。
認証付きAPIルートを組む
ルートグループとミドルウェアを組み合わせれば、認証必須のAPI群もすっきり定義できます。
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
import { controllers } from '#generated/controllers'
router
.group(() => {
router.resource('posts', controllers.Posts)
})
.prefix('/api')
.use(middleware.auth())
.as('api')
セッション認証・アクセストークンの両方が公式サポートされているため、SPA向けAPIでもサーバーレンダリング構成でも同じ書き味で認証を導入できます。
更新時の一意性チェック
実務でよくある「自分自身を除いたメールアドレスの重複チェック」も、VineJSのメタデータ機能でバリデータ側に閉じ込められます。
import vine from '@vinejs/vine'
export const updateUserValidator = vine
.withMetaData<{ userId: number }>()
.create({
email: vine.string().email().unique({
table: 'users',
filter: (db, value, field) => {
db.whereNot('id', field.meta.userId)
},
}),
})
// コントローラー側
const payload = await request.validateUsing(updateUserValidator, {
meta: { userId: auth.user!.id },
})
コントローラーは「バリデータを呼ぶだけ」の薄さを保てるので、コードレビューでもロジックの置き場所に迷いません。
まとめ
AdonisJSの魅力を振り返ってみましょう。
- ORM・バリデーション・認証・メールまで公式パッケージで完結し、ライブラリ選定の消耗から解放される
- TypeScriptファースト設計で、ルーティングからモデルまで型安全が貫かれている
findOrFailや自動バリデーションエラー変換など、定型コードを書かせない仕組みが徹底している- Laravel経験者なら驚くほどスムーズに、Express経験者でも自然に移行できる学習曲線
「Node.jsでフルスタックに作りたいが、寄せ集め構成はもう疲れた」という方には、間違いなく試す価値のあるフレームワークです。まずは npm create adonisjs@latest でAPIキットを作成し、この記事のブログAPIを写経するところから始めてみてください。フレームワークが「決めてくれる」心地よさを、きっと実感できるはずです。
