adonisjs について
adonisjs は、AdonisJS のバックエンド開発者向けスキルです。フレームワークの規約や作業手順、各種リファレンスを提供し、データベースと ORM の作業は別の lucid スキルに委ねます。
主な機能
- 01AdonisJS のルーティングとコントローラーの指針
- 02バリデーションとトランスフォーマーの規約
- 03機能開発とデバッグの手順
- 04認証、HTTP、キュー、セキュリティの資料
- 05Ace CLI コマンド例
活用シーン
- 01AdonisJS の機能実装
- 02ルートとコントローラー構成の確認
- 03フレームワーク動作のデバッグ
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lncitador/ adonisjs-maestro --skill adonisjsインストール後、Claude Code で/adonisjsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/ace-commands.mdarchitecture.mdauth.mdcache.mdcontrollers.mdevents.mdexceptions.mdhttp.mdmail.mdmiddleware.mdperformance.mdqueue.mdsecurity.mdtransformers.mdvalidations.mdworkflows/build-feature.mdcode-review.mddebug.mdevents.mdexceptions.mdproviders.md詳細
AdonisJS バックエンドスキル
Lucid との境界
このスキルでは AdonisJS フレームワーク層を扱います。データベースや ORM の作業には lucid を読み込んでください。
- マイグレーションとスキーマ生成
database/schema.ts- モデルファイルとモデルのフック/スコープ/シリアライズ
- リレーションシップとプリロード
- クエリビルダーとトランザクション
- ファクトリーとシーダー
AdonisJS のコントローラー、トランスフォーマー、ポリシー、サービスでは Lucid モデルを使用できますが、モデルの定義やクエリに関するルールは lucid に記載されています。
基本的な規約
ルーティング(start/routes.ts)
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
import { controllers } from '#generated/controllers'
// CRITICAL: fixed routes BEFORE dynamic params
router.get('/posts/create', [controllers.Posts, 'create']).use(middleware.auth())
router.post('/posts', [controllers.Posts, 'store']).use(middleware.auth())
router.get('/posts/:id', [controllers.Posts, 'show'])
router.get('/posts/:id/edit', [controllers.Posts, 'edit']).use(middleware.auth())
router.put('/posts/:id', [controllers.Posts, 'update']).use(middleware.auth())
// Guest-only
router.group(() => {
router.get('/login', [controllers.Session, 'create'])
router.post('/login', [controllers.Session, 'store'])
}).use(middleware.guest())
コントローラー(app/controllers/)
import type { HttpContext } from '@adonisjs/core/http'
import PostTransformer from '#transformers/post_transformer'
import PostPolicy from '#policies/post_policy'
import { createPostValidator } from '#validators/post'
import postsService from '#services/posts_service'
export default class PostsController {
async index({ inertia }: HttpContext) { // swap inertia for view/serialize per architecture
const posts = await postsService.listForIndex()
return inertia.render('posts/index', { posts: PostTransformer.transform(posts) })
}
async store({ request, auth, response }: HttpContext) {
const payload = await request.validateUsing(createPostValidator)
await postsService.create(auth.user!, payload)
return response.redirect().toRoute('posts.index')
}
async update({ bouncer, params, request, response, session }: HttpContext) {
const post = await postsService.findForUpdate(params.id)
await bouncer.with(PostPolicy).authorize('edit', post) // throws 403 if denied
const data = await request.validateUsing(updatePostValidator)
await postsService.update(post, data)
session.flash('success', 'Post updated successfully')
return response.redirect().toRoute('posts.show', { id: post.id })
}
}
トランスフォーマー(app/transformers/)
import { BaseTransformer } from '@adonisjs/core/transformers'
import { inject } from '@adonisjs/core'
import { HttpContext } from '@adonisjs/core/http'
import type Post from '#models/post'
import PostPolicy from '#policies/post_policy'
export default class PostTransformer extends BaseTransformer<Post> {
toObject() {
return {
...this.pick(this.resource, ['id', 'title', 'url', 'summary', 'createdAt']),
author: UserTransformer.transform(this.resource.author),
// whenLoaded() — omits field if relation was not preloaded
comments: CommentTransformer.transform(this.whenLoaded(this.resource.comments)),
}
}
// Variant — extends toObject() with permission flags
@inject()
async forDetailedView({ bouncer }: HttpContext) {
return {
...this.toObject(),
can: {
edit: await bouncer.with(PostPolicy).allows('edit', this.resource),
delete: await bouncer.with(PostPolicy).allows('delete', this.resource),
},
}
}
}
// Single: PostTransformer.transform(post)
// Array: PostTransformer.transform(posts)
// Variant: PostTransformer.transform(post).useVariant('forDetailedView')
// Paginated: PostTransformer.paginate(posts.all(), posts.getMeta())
バリデーション(app/validators/)
import vine from '@vinejs/vine'
// vine.create() — not vine.compile()
export const createPostValidator = vine.create({
title: vine.string().trim().minLength(3).maxLength(255),
url: vine.string().url(),
summary: vine.string().trim().minLength(80).maxLength(500),
})
// Clone schema to reuse rules for update
export const updatePostValidator = vine.create(
createPostValidator.schema.clone()
)
重要なインポート規則
// WRONG — never import from root package
import { HttpContext } from '@adonisjs/core'
// CORRECT sub-path imports
import type { HttpContext } from '@adonisjs/core/http'
import { inject } from '@adonisjs/core'
import { BaseTransformer } from '@adonisjs/core/transformers'
import { BasePolicy } from '@adonisjs/bouncer'
import router from '@adonisjs/core/services/router'
import { urlFor, signedUrlFor } from '@adonisjs/core/services/url_builder'
import vine from '@vinejs/vine'
import Post from '#models/post'
import { controllers } from '#generated/controllers'
ワークフロー
| 状況 | ファイル |
|---|---|
| 新機能を実装する | workflows/build-feature.md |
| エラーや予期しない動作をデバッグする | workflows/debug.md |
| サービスプロバイダー、バインディング、IoC を作成する | workflows/providers.md |
| エラーやドメイン例外をカスタマイズする | workflows/exceptions.md |
| イベントで副作用を分離する | workflows/events.md |
| PR またはコードスニペットをレビューする | workflows/code-review.md |
リファレンス
| トピック | ファイル |
|---|---|
| アーキテクチャ、フォルダー構成、3 つのレンダリングモード | references/architecture.md |
| 認証、ガード、Bouncer(.authorize と .allows) | references/auth.md |
| キャッシュ、無効化、TTL、タグ | references/cache.md |
| コントローラー、リソースとアクション、ルートの順序 | references/controllers.md |
| イベント、リスナー、エミッター | references/events.md |
| 例外ハンドラー、カスタムドメインエラー | references/exceptions.md |
| HTTP — リクエスト、レスポンス、セッション、URL ビルダー | references/http.md |
| メール — 送信、メールクラス、テンプレート、テスト | references/mail.md |
| ミドルウェア — 名前付き、パラメーター、グローバル | references/middleware.md |
| パフォーマンス — キャッシュ、キュー、コストの高い処理の遅延実行 | references/performance.md |
| キュー、ジョブ、再試行、ワーカー | references/queue.md |
| セキュリティ — ハッシュ化、暗号化、CORS、CSRF | references/security.md |
| トランスフォーマー — BaseTransformer、pick、whenLoaded、バリアント | references/transformers.md |
| VineJS バリデーション — vine.create()、すべてのフィールド型 | references/validations.md |
| Ace コマンド — 作成、引数、フラグ、プロンプト | references/ace-commands.md |
Ace CLI クイックリファレンス
node ace make:controller Post --resource
node ace make:validator post
node ace make:transformer post
node ace make:policy post
node ace make:service PostService
node ace make:event OrderPlaced
node ace make:listener SendEmail --event=OrderPlaced
node ace make:job ProcessImage
node ace make:middleware AuthMiddleware
node ace make:exception DomainException
node ace make:command SendReminders
node ace make:mail OrderConfirmation
node ace list:routes
node ace repl
node ace generate:key
# Types in @generated/data are auto-generated by the dev server — no manual command
アンチパターン
| 誤り | 正しい方法 |
|---|---|
vine.compile() | vine.create() |
| コントローラー内にバリデーションを記述する | app/validators/ に個別のファイルを作成する |
| コントローラーにビジネスロジックを記述する | app/services/ に移動する |
| コントローラー内で副作用(メール送信)を発生させる | イベント + リスナーを使う |
import from '@adonisjs/core' を直接使う | '@adonisjs/core/http' などのサブパスを使う |
| 1 つのコントローラーに 8 個以上のメソッドを持たせる | 役割を絞ったアクションコントローラーに分割する |
| メール認証を認証ミドルウェアの背後に置く | 認証確認ルートは公開する |
固定ルート(/create)より前に動的ルート(:id)を置く | 固定ルートを先に定義する |
トランスフォーマーで bouncer.authorize() を使う | トランスフォーマーでは bouncer.allows() を使う |
router.makeUrl() | @adonisjs/core/services/url_builder の urlFor() を使う |
Edge で {{ route('...') }} を使う | Edge では {{ urlFor('...') }} を使う |
メール用の signedUrlFor で prefixUrl を指定しない | 外部リンクには必ず prefixUrl を渡す |
コメント
Markdown 対応