AdonisJS(アドニスJS)は、TypeScriptで書くNode.js向けのフルスタックフレームワークです。ルーティング、ORM、バリデーション、認証、テストランナーを公式パッケージとして同梱し、LaravelやRuby on Railsに近い「決まった置き場所に書けば動く」開発体験をNode.jsで提供します。現行はv7系で、v7.0.0は2026年2月25日に公開されました。2026年11月5日時点のnpm最新は、2026年10月5日公開の @adonisjs/core 7.6.0 です。
この記事では、v7のプロジェクトを実際に作成し、CRUD APIを組んでcurlで叩いた結果をもとに、インストール手順、ディレクトリ構成、Lucid ORMとVineJSの書き方、Inertia連携、v6からの変更点、2026年11月4日に公開された脆弱性の確認手順を順に説明します。検証環境はmacOS(Intel)上のNode.js v26.5.0、npm 11.17.0です(CRUDは2026年9月28日に core 7.5.2 で、版と脆弱性まわりは2026年11月5日に core 7.6.0 で確認)。
まとめ:AdonisJS v7の要点
- AdonisJSはTypeScript前提の「全部入り」Node.jsフレームワーク。ライセンスはMITで、GitHubの adonisjs/core は19,171スター(2026年11月5日時点)。
- 現行はv7系。Node.js 24以上が必須で、@adonisjs/core 7.6.0 の engines も
>=24.0.0です。 - 新規作成は
npm init adonisjs@latest。スターターキットは hypermedia/react/vue/api/api-monorepo の5種で、どれもLucid ORMと認証(ログイン・サインアップ)が最初から入ります。 - v7ではマイグレーションからモデルの列定義(
database/schema.ts)が自動生成され、レスポンス整形はTransformer、ルートのコントローラー参照は自動生成のバレルファイル経由になりました。 - 旧来の
npm i -g @adonisjs/cliとadonis newはv4時代のもので、現行プロジェクトでは使いません。 - 実装上の落とし穴として、Lucid 22.4.2 の
make:model --controllerはコントローラーを作らず(core 7.6.0でも同じ)、SQLiteでは真偽値が1・0で返ります。対処は本文で示します。 - 2026年11月4日に、multipart処理の脆弱性2件(認証不要のDoS)が公開されました。v7は @adonisjs/bodyparser 11.0.5、v6は10.1.6で修正済みです。既存プロジェクトはロックファイルに古い版が残るため、
npm update @adonisjs/bodyparserで上げます。
既存アプリの保守担当者は脆弱性対応、新規導入を試す方はインストール手順から確認してください。
AdonisJSの位置づけ:Express・NestJS・Laravelとの比較
Express・NestJSとの同梱範囲の違い
Node.jsのWebフレームワークは、何を同梱するかで性格が分かれます。Express.jsはルーティングとミドルウェアの仕組みだけを持ち、ORMやバリデーションは自分で選んで組み合わせます。NestJSはDIとモジュール構造を提供しますが、ORMはTypeORMやPrismaなど外部から選ぶ前提です。AdonisJSはORM(Lucid)、バリデーション(VineJS)、認証、セッション、テスト(Japa)まで公式パッケージで揃え、生成コマンド(ace)で決まった場所にファイルを作ります。
| 項目 | AdonisJS v7 | NestJS | Express 5 |
|---|---|---|---|
| 言語 | TypeScript前提 | TypeScript/JavaScript | JavaScript(型は任意) |
| ORM | Lucid(公式) | 外部から選択 | 外部から選択 |
| バリデーション | VineJS(公式) | class-validator等 | 外部から選択 |
| 認証 | @adonisjs/auth(公式) | Passport等 | 外部から選択 |
| テンプレート | Edge/Inertia | 外部から選択 | 外部から選択 |
| CLI生成 | node ace make:* | nest generate | なし |
AdonisJSはMVCの分け方(Model=Lucidモデル、View=EdgeまたはInertia、Controller=app/controllers)をそのまま採っています。部品を選ぶ自由より、構成を議論しなくて済むことを優先するフレームワークです。NestJSのDTOとValidationPipeの考え方はDTOとNestJSのValidationPipe設定で扱っています。
Laravel・Ruby on Railsとの対応表
LaravelやRailsの経験者は、どのファイルとコマンドが何に当たるかを先に押さえると早く読めます。名前が違うだけで、ルート→コントローラー→モデル→マイグレーションの分担は3つとも同じです。
| 役割 | AdonisJS v7 | Laravel | Ruby on Rails |
|---|---|---|---|
| ルート定義 | start/routes.ts | routes/web.php | config/routes.rb |
| CLI | node ace | php artisan | bin/rails |
| コントローラー生成 | make:controller | make:controller | generate controller |
| ORM | Lucid | Eloquent | Active Record |
| マイグレーション実行 | migration:run | migrate | db:migrate |
| 入力検証 | VineJS(app/validators) | FormRequest | モデルの validates |
| テンプレート | Edge | Blade | ERB |
| JSONの整形 | Transformer | API Resource | Jbuilder等 |
大きく違うのはモデルの列定義です。v7はマイグレーション実行後にデータベースを走査して database/schema.ts を自動生成し、モデルはそれを継承します。列が生成済みのTypeScriptの型として並ぶため、モデルに無いプロパティへのアクセスは型チェックで検出できます。ただし where('nonexistent_column', 1) のようにクエリビルダーへ文字列で渡す列名までは検査されません。LaravelでAPIを組む流れはLaravel APIの作り方で解説しているので、手順を見比べると対応がつかみやすくなります。
v7(2026年2月25日公開)で変わった点
公式ブログはv7を「破壊的変更を抑えたv6からの段階的アップグレード」と説明し、公式パッケージ45以上を更新して @adonisjs/otel・@adonisjs/content・edge-markdown の3つを新設しました。書き方に直接影響する変更は次のとおりです。
| 項目 | v6 | v7 |
|---|---|---|
| Node.js | 20以上 | 24以上 |
| TypeScript実行 | ts-node | @poppinss/ts-exec(SWCベース) |
| モデルの列定義 | @column を手書き | database/schema.ts を自動生成 |
| レスポンス整形 | モデルを暗黙にシリアライズ | Transformerで明示 |
| ルートのコントローラー参照 | 動的importを列挙 | #generated/controllers のバレル |
| 新規作成時の構成 | 対話でパッケージを選択 | 認証込みのスターターキット |
| 暗号化 | 単一設定 | 名前付きドライバ(config/encryption.ts) |
v6のプロジェクトを上げる場合は、公式の「v6 to v7」アップグレードガイドが手順を15段階(全パッケージ更新、ts-nodeからts-execへの置き換え、adonisrc.tsのフック設定、暗号化設定、router.makeUrl の置き換え、Request/Responseクラスの改名、Inertia連携の書き換えなど)に分けています。まずNode.js 24へ上げ、CIと本番サーバーのNode.jsも揃えてから着手してください。v6のドキュメントは v6-docs.adonisjs.com に残っています。v7の開発中に見つかったセキュリティ問題の修正はv6にもバックポートされ、次の章で扱う2026年11月の修正もv6向けに10.1.6が出ています。ただし、v6の保守終了日は2026年11月5日時点で公式に示されていません。
2026年11月4日公開のbodyparser脆弱性:既存プロジェクトの確認と更新手順
2026年11月4日、AdonisJSのリクエスト本文を解析する @adonisjs/bodyparser について、深刻度High(CVSS 7.5)の勧告が2件公開されました。どちらも multipart/form-data のリクエスト1件で、認証なしにサーバーを止められる問題です。bodyparserは既定でmultipartを解析するため、ファイルアップロードのルートを作っていないアプリも対象になります。
| 勧告 | 起きること | 修正版(v7/v6) |
|---|---|---|
| GHSA-8w45-cghr-9rh4 | パート名 __proto__ 等で例外・停止 | 11.0.5/10.1.6 |
| GHSA-jf2h-r4qp-9rp8 | ReDoS・メモリ枯渇・500応答 | 11.0.5/10.1.6 |
1件目のGHSA-8w45-cghr-9rh4は、ファイルパートの名前を __proto__ や constructor にすると、ハンドラーの検索が Object.prototype から継承したメンバーを拾い、try/catchの外で TypeError が起きるものです。未処理のPromise拒否でワーカーが落ちるか、リクエストが応答しないまま残ります。回避策は無く、更新するしかありません。
2件目のGHSA-jf2h-r4qp-9rp8は、bodyparserが使うフォーク版の @poppinss/multiparty に、上流 multiparty 4.3.0・4.3.1 のセキュリティ修正が入っていなかった問題です。勧告の検証では、約1MBのリクエスト1件で Content-Disposition のファイル名解析がイベントループを約20秒止めました。パートヘッダーにはサイズの上限が無く、multipartの limit 設定も効かないため、送られた分だけメモリが増えます。修正は @poppinss/multiparty 3.0.1 に入っています。
ロックファイルに古い版が残る理由と更新コマンド
@adonisjs/core 7.6.0 は bodyparser を ^11.0.4 の範囲で要求します。修正前の11.0.4もこの範囲に入るので、既存の package-lock.json に11.0.4が記録されていれば、npm install を実行しても入れ替わりません。11.0.4を記録したロックファイルを作って確かめたところ、npm install 後も11.0.4のままで、次のコマンドで11.0.5に上がりました。
npm ls @adonisjs/bodyparser @poppinss/multiparty # 現在の版を確認
npm update @adonisjs/bodyparser # 11.0.5(v6なら10.1.6)へ更新
npm ls @adonisjs/bodyparser @poppinss/multiparty # bodyparser 11.0.5・multiparty 3.0.1 を確認
2026年11月5日に npx create-adonisjs@latest で新しく作ったプロジェクトには、最初から11.0.5と3.0.1が入っていました。気をつけたいのは npm audit です。同じ日に11.0.4のプロジェクトで実行しても、今回の2件は出ませんでした(highは0件。出たのは minimatch 経由のmoderate 27件と deepmerge のlow 1件で、11.0.5のプロジェクトと同じ結果)。2件はadonisjs/coreリポジトリの勧告として公開されたもので、GitHubの全体の勧告データベースにはまだ載っていません。auditが通っても、npm ls で版を直接見てください。ロックファイルと更新コマンドの仕組みはnpmとは?CLIとレジストリの役割で扱っています。
2026年に公開されたAdonisJS関連の勧告
bodyparserのmultipart処理は、2026年に入ってから繰り返し修正されています。長く運用するアプリでは、デプロイのたびにbodyparserとLucidの版を確認する手順をCIに入れておくべきです。
| 公開日 | 識別子 | 内容 | v7系の修正版 |
|---|---|---|---|
| 2026-01-02 | CVE-2026-21440 | アップロードのパストラバーサル | bodyparser 11.0.0-next.6 |
| 2026-01-13 | CVE-2026-22814 | Lucidの一括代入 | lucid 22.0.0-next.6 |
| 2026-02-06 | CVE-2026-25754 | multipartのプロトタイプ汚染 | bodyparser 11.0.0-next.9 |
| 2026-02-06 | CVE-2026-25762 | multipartのメモリ無制限消費 | bodyparser 11.0.0-next.9 |
| 2026-04-14 | CVE-2026-40255 | redirect().back() のオープンリダイレクト | http-server 8.2.0(core 7.4.0以降を推奨) |
| 2026-05-28 | CVE-2026-48795 | CVE-2026-25754の修正漏れ | bodyparser 11.0.3 |
| 2026-11-04 | GHSA-8w45/GHSA-jf2h | multipartのDoS(上記2件) | bodyparser 11.0.5 |
上の4件はv7の正式公開前に修正済みで、v7.0.0以降なら影響しません。公式のv7公開記事も、開発期間中に4件のCVEを修正し、v6にもバックポートしたと説明しています。CVE-2026-48795は、プロトタイプ汚染の修正が user.__proto__.polluted のように手前に別の名前を挟んだ入力で迂回できた問題です。
インストール手順:npm init adonisjsとスターターキット5種
Node.js 24以上の確認と作成コマンド
最初に node -v で24以上であることを確かめます。Node.jsの版の選び方はNode.jsとは?仕組み・版の選び方を参照してください。作成コマンドは次の1行で、対話形式でキットを選びます。
npm init adonisjs@latest my-app
CIやスクリプトなど対話できない環境では、キットとパッケージマネージャーをフラグで渡します(create-adonisjs 3.4.0 で確認)。非対話環境でフラグを省くと、キット一覧を示して終了します。
npx create-adonisjs@latest my-api --kit=api --pkg=npm
上記の検証環境では、この1コマンドでダウンロード、依存パッケージのインストール、アプリの準備、SQLiteへのマイグレーション実行まで進み、全体で1分4秒(9月28日)と3分1秒(11月5日)かかりました。差のほとんどは依存パッケージのダウンロード時間です。マイグレーションを後回しにしたい場合は --skip-migrations を付けます。
5種類のスターターキットと選び方
create-adonisjs 3.4.0 に定義されているキットは次の5種です(v7公開時のブログが紹介しているのは hypermedia・api・react・vue の4種)。
--kit の値 |
用途 | 認証 |
|---|---|---|
| hypermedia | Edgeテンプレートのサーバー描画 | セッション+CSRF |
| react | Inertia+React 19のSPA | セッション |
| vue | Inertia+VueのSPA | セッション |
| api | JSONを返すREST API | アクセストークン |
| api-monorepo | API+フロントのモノレポ | アクセストークン |
選び方は単純です。画面をサーバーで描くならhypermedia、ReactかVueで画面を書きたいが別途APIを設計したくないならreact/vue、スマホアプリや別リポジトリのフロントに返すならapiを選びます。外部システムとつなぐAPIを設計段階から相談したい場合は、一創のAPI開発・システム連携でも支援しています。
旧CLI(adonis new)を使わない理由
古い解説記事にある npm i -g @adonisjs/cli と adonis new は、v4時代の手順です。npmレジストリの公開記録では、@adonisjs/cli が最後に公開されたのは2021年3月30日の4.0.13(latestタグ)です。現行のv6・v7では、プロジェクト作成は npm init adonisjs、以降の操作はプロジェクト内の node ace で行います。グローバルインストールは不要です。
生成されるディレクトリ構成と各ファイルの役割
apiキットで作成した直後の主要ファイルは次のとおりです(node_modules等を除く)。
my-api/
├── adonisrc.ts # プロバイダ・コマンド・フックの登録
├── ace.js # node ace の入口
├── app/
│ ├── controllers/ # access_tokens/new_account/profile
│ ├── middleware/ # auth_middleware など
│ ├── models/user.ts
│ ├── transformers/user_transformer.ts
│ └── validators/user.ts
├── bin/server.ts # HTTPサーバーの起動
├── config/ # app・auth・database・cors・encryption など
├── database/
│ ├── migrations/
│ ├── schema.ts # 自動生成(手で編集しない)
│ └── schema_rules.ts # 生成ルールの上書き
├── start/
│ ├── routes.ts # ルート定義
│ ├── kernel.ts # ミドルウェアの登録
│ └── env.ts # 環境変数のスキーマ
├── tests/bootstrap.ts # Japa の設定
└── .adonisjs/ # 自動生成(controllers.ts・APIクライアント型)
古い記事にある start/app.js はv4の構成です。一方 resources/ はv7でも hypermedia キットに含まれ、Edgeテンプレート(resources/views)の置き場所として使われます。apiキットには無いだけです。v7で手を入れるのは主に app・database/migrations・start/routes.ts の3か所で、database/schema.ts と .adonisjs/ 配下は自動生成なので編集しません。package.json の imports に #models/* や #controllers/* などのサブパスが定義され、相対パスを書かずにimportできます。自動生成ファイルは開発サーバーの起動時に更新されますが、core 7.5.0(2026年8月21日)で追加された node ace codegen を使えば、サーバーを起動せずに再生成できます。CIで型チェックの前に実行する用途に向きます。
CRUD APIを作る手順:モデル・マイグレーション・コントローラー
ここからはapiキットに投稿(Post)のCRUDを追加します。以降の node ace はすべて作成したプロジェクトのディレクトリ(cd my-api で移動した先)で実行します。コードはすべて冒頭の検証環境で動作を確認したものです。
make:modelとmigration:runによるスキーマクラスの生成
モデルとマイグレーションを同時に作ります。
node ace make:model Post --migration --transformer
生成されたマイグレーションに列を足します。
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'posts'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.string('title', 120).notNullable()
table.text('body').notNullable()
table.boolean('is_published').notNullable().defaultTo(false)
table.timestamp('created_at')
table.timestamp('updated_at')
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
node ace migration:run を実行すると、テーブル作成に続いてデータベースを走査し、database/schema.ts に PostSchema クラスが書き出されます。snake_caseの列はcamelCaseのプロパティ(is_published → isPublished)になり、created_at には autoCreate が付きます。モデル側は生成されたクラスを継承するだけです。
import { PostSchema } from '#database/schema'
export default class Post extends PostSchema {}
v6までのように、マイグレーションと同じ列をモデルの @column で書き直す必要はなくなりました。
make:model --controller でコントローラーが生成されない不具合(Lucid 22.4.2)
ヘルプには -c, --controller と -c, --transformer が並んでおり、短縮形 -c が重複しています。@adonisjs/lucid 22.4.2 のソース(build/commands/make_model.js)でも両フラグに alias: 'c' が付いていました。実際に node ace make:model Tag --controller を実行すると、作られたのはモデルとTransformerで、コントローラーは生成されませんでした。2026年11月5日に core 7.6.0 の新規プロジェクトで node ace make:model Post --controller を実行し直しても、作られたのは app/models/post.ts と app/transformers/post_transformer.ts だけでした(Lucidは22.4.2から更新されていません)。コントローラーは別コマンドで作ります。
node ace make:controller posts --api
--api は create・edit(フォーム表示用)を除いた index・store・show・update・destroy の5メソッドを生成します。
VineJSのバリデーションと422応答
バリデーターは app/validators/post.ts として保存します(コントローラーの #validators/post がこのファイルを指します)。v7のスターターキットは vine.create で書かれています(vine.compile もVineJS 4.4.0に残っています)。
import vine from '@vinejs/vine'
export const createPostValidator = vine.create({
title: vine.string().trim().minLength(3).maxLength(120),
body: vine.string().trim().minLength(1),
isPublished: vine.boolean().optional(),
})
export const updatePostValidator = vine.create({
title: vine.string().trim().minLength(3).maxLength(120).optional(),
body: vine.string().trim().minLength(1).optional(),
isPublished: vine.boolean().optional(),
})
コントローラー内の request.validateUsing で検証に失敗すると例外が投げられ、後続の保存処理は実行されずに422が返ります。titleを1文字・bodyを空で送った結果は次のとおりで、エラーは項目ごとの配列になります。
{"errors":[{"message":"The title field must have at least 3 characters","rule":"minLength","field":"title","meta":{"min":3}},{"message":"The body field must be defined","rule":"required","field":"body"}]}
空文字の body が required 違反になるのは、VineJS自体の挙動ではなく、キットの config/bodyparser.ts で convertEmptyStringsToNull: true が有効になっており、空文字が検証前に null へ変換されるためです。スターターキットの signupValidator でも、fullName は nullable() で定義されており、キーを省くと422になり、null を明示して送ると通りました。キー自体を省略可能にしたい項目には optional() を付けます。
コントローラーとTransformerによるレスポンス整形
v7ではモデルをそのまま返さず、Transformerで公開する項目を選びます。make:model で作られたTransformerは id だけを返す雛形なので、項目を足します。
import { BaseTransformer } from '@adonisjs/core/transformers'
import Post from '#models/post'
export default class PostTransformer extends BaseTransformer<Post> {
toObject() {
return this.pick(this.resource, ['id', 'title', 'body', 'isPublished', 'createdAt'])
}
}
コントローラーは、検証→保存→Transformerで整形、の流れで書きます。
import Post from '#models/post'
import PostTransformer from '#transformers/post_transformer'
import { createPostValidator, updatePostValidator } from '#validators/post'
import type { HttpContext } from '@adonisjs/core/http'
export default class PostsController {
async index({ serialize }: HttpContext) {
const posts = await Post.query().orderBy('id', 'desc')
return serialize(PostTransformer.transform(posts))
}
async store({ request, response, serialize }: HttpContext) {
const payload = await request.validateUsing(createPostValidator)
const post = await Post.create(payload)
response.status(201)
return serialize(PostTransformer.transform(post))
}
async show({ params, serialize }: HttpContext) {
const post = await Post.findOrFail(params.id)
return serialize(PostTransformer.transform(post))
}
async update({ params, request, serialize }: HttpContext) {
const post = await Post.findOrFail(params.id)
const payload = await request.validateUsing(updatePostValidator)
await post.merge(payload).save()
return serialize(PostTransformer.transform(post))
}
async destroy({ params, response }: HttpContext) {
const post = await Post.findOrFail(params.id)
await post.delete()
return response.noContent()
}
}
findOrFail は該当行が無いと E_ROW_NOT_FOUND を投げ、フレームワークが404に変換します。開発環境(NODE_ENV=development)ではこの404応答にスタックトレースが含まれるので、本番で NODE_ENV=production になっているかをデプロイ時に確かめてください。
ルーティングとcurlでの動作確認
start/routes.ts の /api/v1 グループに1行足します。controllers は自動生成のバレルファイルで、開発サーバーの起動時に Posts が追記されます。
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'
router
.group(() => {
// auth・account のルートはキットの既定のまま
router.resource('posts', controllers.Posts).apiOnly()
})
.prefix('/api/v1')
npm run dev(中身は node ace serve --hmr)で起動し、3333番ポートに対してcurlで確認しました。作成リクエストは次の形です。Accept: application/json を付けると、検証エラーや404もJSONで返ります。
curl -i -X POST http://localhost:3333/api/v1/posts \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data '{"title":"First post","body":"Hello","isPublished":true}'
各リクエストの応答は次の表のとおりです。2026年11月5日に core 7.6.0 で、後述の「書き込みだけ認証必須」の構成にしてトークンを付けて送り直しても、投稿APIの6行は同じステータスでした。
| リクエスト | ステータス |
|---|---|
| POST /api/v1/posts(正しい値) | 201 |
| POST /api/v1/posts(titleが1文字) | 422 |
| GET /api/v1/posts | 200 |
| PUT /api/v1/posts/1 | 200 |
| GET /api/v1/posts/99(存在しない) | 404 |
| DELETE /api/v1/posts/1 | 204 |
| GET /api/v1/account/profile(トークンなし) | 401 |
最後の行はapiキットに同梱された認証の動きです。/api/v1/auth/signup と /api/v1/auth/login がアクセストークンを返し、middleware.auth() を付けたルートはトークンが無いと401になります。投稿APIの書き込みだけを認証必須にする場合は、先ほどの router.resource の行を次のように置き換え、アクションを指定してミドルウェアを付けます。この形でトークンなしのPOSTが401、サインアップで得たトークンを Authorization: Bearer で付けたPOSTが201になることを確かめました。トークンはサインアップ応答の data.token(oat_ で始まる文字列)に入っています。これは「ログインしているか」の確認だけで、他人の投稿を更新・削除できないようにする所有者チェック(認可)は含みません。必要なら @adonisjs/bouncer のポリシーなどで別途実装します。
router
.resource('posts', controllers.Posts)
.apiOnly()
.use(['store', 'update', 'destroy'], middleware.auth())
curl -X POST http://localhost:3333/api/v1/auth/signup \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
--data '{"fullName":"Hanako","email":"[email protected]","password":"secret-pass-123","passwordConfirmation":"secret-pass-123"}'
curl -i -X POST http://localhost:3333/api/v1/posts \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-H 'Authorization: Bearer oat_...' \
--data '{"title":"First post","body":"Hello"}'
なお、Node.js 26で起動すると module.register() の非推奨警告(DEP0205)が出ますが、動作には影響しませんでした。
Lucid ORMの特徴:対応データベースとSQLiteの落とし穴
Knexベースのクエリビルダーと対応データベース
LucidはKnexの上に作られたActive Record型のORMです。モデルのメソッド(Post.create、findOrFail、merge().save())でCRUDを書き、複雑な条件は Post.query() のクエリビルダーで組み立てます。キットの config/database.ts は既定がSQLite(better-sqlite3)で、PostgreSQL(pg)、MySQL(mysql2)、SQL Server(tedious)、libSQL(@libsql/client、Turso向け)の接続例がコメントで用意されています。MariaDBはmysql2クライアントで接続します。ドライバーのパッケージを入れて接続設定を切り替えます。
リレーションは hasMany・belongsTo などのデコレーターでモデルに宣言し、preload で一括読み込みします。同じNode.js向けORMのTypeORMやPrismaとの書き味の違いは、TypeORMの使い方とTypeORMとPrismaの比較が参考になります。
SQLiteで真偽値が1・0で返る問題とconsumeでの補正
生成された PostSchema では isPublished の型が boolean ですが、SQLiteには真偽値型が無く、読み出した値は数値になります。実際、作成直後のレスポンスは "isPublished":true なのに、一覧を取り直すと "isPublished":1 が返りました。モデル側で列を宣言し直し、consume で変換すると一覧でも false/true が返るようになりました。
import { PostSchema } from '#database/schema'
import { column } from '@adonisjs/lucid/orm'
export default class Post extends PostSchema {
@column({ consume: (value) => Boolean(value) })
declare isPublished: boolean
}
また、isPublished を省略して作成したときのレスポンスには isPublished 自体が含まれません。データベースの既定値(false)は、保存後にモデルへ読み戻されないためです。作成直後に既定値込みの値を返したい場合は、保存後に await post.refresh() を呼びます(追加後のレスポンスに "isPublished":false が含まれることを確認)。本番でPostgreSQLを使うなら真偽値の問題は起きませんが、テストだけSQLiteで回す構成では型のずれが出ます。
シーダーで初期データを投入する手順
node ace make:seeder Post で database/seeders/post_seeder.ts が作られ、run() の中にモデル操作を書いて node ace db:seed で投入します。
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import Post from '#models/post'
export default class extends BaseSeeder {
async run() {
await Post.createMany([
{ title: 'Seeded post 1', body: 'body 1', isPublished: true },
{ title: 'Seeded post 2', body: 'body 2' },
])
}
}
Inertia連携:React・Vueで画面を作る構成
Inertiaはサーバーのルーティングとコントローラーをそのままにしたまま、画面だけをReactやVueのコンポーネントで描く仕組みです。JSON APIを別に設計せず、コントローラーからページ名とpropsを渡します。考え方はLaravelのLaravel Inertiaと同じで、コントローラーからページ名とpropsを返す書き方、ページコンポーネントの置き場所の分け方をそのまま移せます。
2026年8月9日公開の @adonisjs/inertia 5.0.0 から、クライアント側はInertia 3(@inertiajs/* の3系)が必須になりました。npm上の最新は10月22日公開の5.0.5です。reactキットの package.json(2026年11月5日時点の adonisjs/starter-kits の inertia-react)で宣言されている範囲は、@adonisjs/inertia ^5.0.1、@inertiajs/react ^3.7.1、react ^19.3.0、vite ^8.3.0 で、vueキットは @inertiajs/vue3 ^3.7.1、vue ^3.5.43 です。config/inertia.ts にはSSR用の設定と inertia/ssr.tsx が用意されていますが、既定値は ssr: { enabled: false } です。SSRを有効にすると、初回のHTMLレスポンスにページ本文が描画済みで含まれます。公開ページの初期表示や検索エンジン向けにそれが必要なときに有効化します。
v7のInertia連携では、ルート名とpropsの型がコード生成でフロントへ渡り、型付きの <Link>・<Form> コンポーネントでURLを組み立てられます。v6のInertia構成からの移行は、アップグレードガイドの「Inertia integration rewrite」でファイル構成と共有データの置き場所の変更が説明されています。
AdonisJSを選ぶ場面と選ばない場面
向いているのは、少人数でTypeScriptのWebアプリやAPIを一から作り、ORM・認証・バリデーションの選定と配線に時間を使いたくない場合です。Laravelの経験があるチームなら、ルート→コントローラー→モデル→マイグレーションの流れと、Inertiaによる画面構成をほぼそのまま持ち込めます。
次の条件に当てはまる場合は採用しません。
- Node.js 24へ上げられない実行環境:v7はNode.js 24未満を切っています。社内の実行基盤やサーバーレスのランタイムが24に対応していなければ、v6に留まるか別のフレームワークを選ぶことになります。
- ORMにPrismaやDrizzleを使うと決まっている:Lucidを外すと、スキーマ生成、
unique等のDB参照バリデーション、認証のLucid連携(withAuthFinderやアクセストークンの保存)を自前で作り直すことになり、その実装と保守の負担が採用の利点を上回ります。 - 既存のNestJS・Expressの資産に一部だけ足したい:AdonisJSはアプリ全体の構成を前提にするため、既存サーバーへの部分導入には向きません。
- 日本語の情報量を重視する:国内の解説記事の多くはv6以前のもので、v7の書き方(スキーマクラス、Transformer、バレルファイル)には公式ドキュメントで当たる必要があります。
AdonisJSに関するよくある質問
AdonisJSの読み方は?
「アドニスジェイエス」と読み、日本語の記事では「アドニスJS」とも表記されます。ORMのLucidは「ルーシッド」と読みます。表記は公式が「AdonisJS」、パッケージ名が @adonisjs/core です。検索では「adonis js」「adonis.js」のように区切った表記も使われますが、同じフレームワークを指します。Adonisという名前のほかのソフトウェアと区別したいときは「AdonisJS」と続けて書くのが確実です。
AdonisJSの最新バージョンは?
メジャー版はv7で、v7.0.0は2026年2月25日に公開されました。2026年11月5日時点のnpm上の最新は @adonisjs/core 7.6.0(10月5日公開・codemodsの機能追加)、Lucidは22.4.2、プロジェクト作成用の create-adonisjs は3.4.0です。v7はNode.js 24以上が必須で、v6からは公式アップグレードガイドの手順で移行します。最新の版番号は npm view @adonisjs/core version で確認できます。
AdonisJSはMVCフレームワークですか?
はい。LucidのモデルがModel、Edgeテンプレートまたは Inertia のページがView、app/controllers のクラスがControllerにあたり、ディレクトリ構成もこの分担に沿っています。apiキットのようにViewを持たない構成でも、ルート→コントローラー→モデルの流れは同じです。入力検証はv6から app/validators のValidator(VineJS)に分けて書く形で、v7ではこれにレスポンス整形のTransformerが加わり、出力の責務もコントローラーの外に出ました。
AdonisJSとNestJSやExpressの違いは?
Expressはルーティングとミドルウェアだけの最小構成で、ORMやバリデーションは自分で選びます。NestJSはDIとモジュール分割の枠組みを提供しますが、ORMは外部から選ぶ前提です。AdonisJSはORM・バリデーション・認証・テストまで公式パッケージで揃える点が違い、選定の手間が少ない代わりに、Lucid以外のORMを使うと利点の多くが消えます。
AdonisJSのORM(Lucid)でリレーションは使えますか?
使えます。LucidはhasOne・hasMany・belongsTo・manyToMany・hasManyThroughのリレーションを持ち、モデルにデコレーターで宣言して preload でまとめて読み込みます。v7ではリレーションもTransformerで整形し、関連データを返すときは関連モデル側のTransformerを呼び出します。Knexベースなので、ORMで書きにくいクエリは db.rawQuery で直接SQLを書けます。