本文へ移動
cccskills
無料GitHub で公開

adonisjs

Builds full-stack web applications with AdonisJS — a batteries-included Node.js framework with ORM, auth, validation, and mailer built-in. Use when someone asks to "build a web app with Node.js", "Laravel for Node.js", "full-stack Node framework", "AdonisJS", "batteries-included backend", or "Node.js with built-in ORM and auth". Covers Lucid ORM, auth (sessions, tokens, social), VineJS validation, Edge templates, and deployment.

インストール方法を見る

含まれるファイル(2)

  • SKILL.md14.4 KB
  • _scores.json1.7 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

AdonisJS

Overview

AdonisJS is a full-stack Node.js framework — the "Laravel of Node.js." Unlike Express/Fastify where you assemble everything from packages, AdonisJS ships with an ORM (Lucid), authentication, validation (VineJS), mailer, queues, and testing out of the box. Opinionated, TypeScript-first, and production-ready.

This skill targets AdonisJS 7 (@adonisjs/core 7.x, Lucid 22, Auth 10, VineJS 4), which requires Node.js 24. Version 7 changed the starter kits, generates Lucid schema classes from migrations, and adds a controllers barrel file and transformers — v6 snippets found online often no longer match.

When to Use

  • Building a complete web application (not just an API) with Node.js
  • Want an opinionated framework with conventions (like Rails/Laravel)
  • Need built-in auth (sessions, API tokens, social OAuth)
  • Database-driven applications with migrations and models
  • Teams that prefer convention over configuration

Instructions

Setup

node -v                                   # must be v24 or later
npm create adonisjs@latest bookshelf -- --kit=hypermedia
cd bookshelf
node ace serve --hmr                      # http://localhost:3333

Starter kits (--kit): hypermedia (Edge templates + Alpine.js), react and vue (Inertia), api (JSON API with access-token auth), api-monorepo. The v6 alias --kit=web no longer exists. Every kit comes with Lucid on SQLite, a users table, and working signup/login. node ace serve --watch restarts the whole process instead of using HMR.

Routes and Controllers

// start/routes.ts
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
import { controllers } from '#generated/controllers'   // barrel file, regenerated when `node ace serve`/`test` starts or by `node ace codegen`

router.resource('posts', controllers.Posts).only(['index', 'show'])
router
  .group(() => {
    router.resource('posts', controllers.Posts).only(['store', 'update', 'destroy'])
  })
  .use(middleware.auth())                 // unauthenticated visitors are redirected to /login

Controller routes get names automatically (posts.index, posts.show, …); list them with node ace list:routes. The v6 style const PostsController = () => import('#controllers/posts_controller') still works.

// app/controllers/posts_controller.ts — node ace make:controller posts --resource
import type { HttpContext } from '@adonisjs/core/http'
import Post from '#models/post'
import { createPostValidator, updatePostValidator } from '#validators/post'

export default class PostsController {
  async index({ request, view }: HttpContext) {
    const page = request.input('page', 1)
    const posts = await Post.query().preload('author').orderBy('createdAt', 'desc').paginate(page, 20)
    return view.render('pages/posts/index', { posts })
  }

  async show({ params, view }: HttpContext) {
    const post = await Post.findOrFail(params.id)   // 404 when the row is missing
    await post.load('author')
    return view.render('pages/posts/show', { post })
  }

  async store({ request, auth, response }: HttpContext) {
    const payload = await request.validateUsing(createPostValidator)
    const post = await auth.getUserOrFail().related('posts').create(payload)
    return response.redirect().toRoute('posts.show', { id: post.id })
  }

  async update({ params, request, response }: HttpContext) {
    const post = await Post.findOrFail(params.id)
    await post.merge(await request.validateUsing(updatePostValidator)).save()
    return response.redirect().toRoute('posts.show', { id: post.id })
  }

  async destroy({ params, response }: HttpContext) {
    await (await Post.findOrFail(params.id)).delete()
    return response.redirect().toRoute('posts.index')
  }
}

Lucid ORM (Models and Migrations)

Lucid 22 is migrations-first: columns are defined in the migration, and node ace migration:run regenerates database/schema.ts with one typed schema class per table. Models extend that class and add only relationships, hooks and methods. Never edit database/schema.ts by hand.

node ace make:model post -m      # app/models/post.ts + database/migrations/1790874006726_create_posts_table.ts
node ace migration:run           # creates the table, regenerates database/schema.ts
node ace migration:rollback      # undo the last batch
// database/migrations/1790874006726_create_posts_table.ts — inside up()
this.schema.createTable(this.tableName, (table) => {
  table.increments('id')
  table.integer('user_id').unsigned().notNullable().references('id').inTable('users').onDelete('CASCADE')
  table.string('title').notNullable()
  table.text('body').notNullable()
  table.timestamp('created_at').notNullable()
  table.timestamp('updated_at').nullable()
})
// app/models/post.ts — PostSchema already declares id, userId, title, body, createdAt, updatedAt
import { PostSchema } from '#database/schema'
import { belongsTo } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import User from '#models/user'

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

In app/models/user.ts add the inverse: @hasMany(() => Post) declare posts: HasMany<typeof Post> (import hasMany from @adonisjs/lucid/orm). A model may still extend BaseModel and declare @column() fields itself.

VineJS Validation

// app/validators/post.ts — node ace make:validator post
import vine from '@vinejs/vine'

export const createPostValidator = vine.create({
  title: vine.string().trim().minLength(3).maxLength(120),
  body: vine.string().trim().minLength(20),
})
export const updatePostValidator = vine.create({
  title: vine.string().trim().minLength(3).maxLength(120).optional(),
  body: vine.string().trim().minLength(20).optional(),
})

vine.create({...}) is shorthand for vine.compile(vine.object({...})). Lucid adds database rules, for example vine.string().email().unique({ table: 'users', column: 'email' }); confirm a password with passwordConfirmation: vine.string().sameAs('password'). When validation fails, request.validateUsing() throws: form posts are redirected back with errors flashed to the session, JSON clients get 422 with an errors array.

Authentication

// config/auth.ts (api kit) — a token guard and a session guard side by side
import { defineConfig } from '@adonisjs/auth'
import { sessionGuard, sessionUserProvider } from '@adonisjs/auth/session'
import { tokensGuard, tokensUserProvider } from '@adonisjs/auth/access_tokens'

const authConfig = defineConfig({
  default: 'api',
  guards: {
    api: tokensGuard({
      provider: tokensUserProvider({ tokens: 'accessTokens', model: () => import('#models/user') }),
    }),
    web: sessionGuard({
      useRememberMeTokens: false,
      provider: sessionUserProvider({ model: () => import('#models/user') }),
    }),
  },
})
export default authConfig
// app/models/user.ts — withAuthFinder hashes the password on save and adds verifyCredentials
// (compose: @adonisjs/core/helpers, hash: @adonisjs/core/services/hash,
//  withAuthFinder: @adonisjs/auth/mixins/lucid, DbAccessTokensProvider: @adonisjs/auth/access_tokens)
export default class User extends compose(UserSchema, withAuthFinder(hash)) {
  static accessTokens = DbAccessTokensProvider.forModel(User, { expiresIn: '30 days' })
}

// Session login (hypermedia / Inertia kits)
const user = await User.verifyCredentials(email, password)
await auth.use('web').login(user)

// Access token (api kit) — the plain value is available only at creation
const token = await User.accessTokens.create(user)
return { token: token.value!.release() }       // "oat_…", sent as Authorization: Bearer

Protect routes with .use(middleware.auth()) or middleware.auth({ guards: ['api'] }). Tokens are opaque and stored hashed in auth_access_tokens. Social login: npm install @adonisjs/ally@6 && node ace configure @adonisjs/ally --providers=github (Ally 6 is the AdonisJS 7 line; npm's latest tag currently resolves to 5.x for AdonisJS 6, so a bare node ace add @adonisjs/ally fails with ERESOLVE), then ally.use('github').redirect() and await ally.use('github').user() in the callback.

Upgrading from v6

Node.js 24; replace ts-node with @poppinss/ts-exec in ace.js; add hooks: { init: [indexEntities()] } to adonisrc.ts; move the app key to config/encryption.ts; router.makeUrl() → urlFor(); Request/Response classes → HttpRequest/HttpResponse; flash key errors.* → inputErrorsBag.*. Full list: https://docs.adonisjs.com/v6-to-v7

Examples

Example 1: Full CRUD web application

User prompt: "Build a blog with AdonisJS — posts CRUD, user auth, and server-rendered pages."

npm create adonisjs@latest bookshelf -- --kit=hypermedia
cd bookshelf
node ace make:model post -m
node ace make:controller posts --resource
node ace make:validator post
# fill in the migration, model, validator, controller and routes shown above
node ace codegen && node ace migration:run   # codegen first: routes.ts needs controllers.Posts in the barrel
node ace serve --hmr
{{-- resources/views/pages/posts/index.edge --}}
<h1>Posts</h1>
<ul>
  @each(post in posts)
    <li>
      <a href="{{ urlFor('posts.show', { id: post.id }) }}">{{ post.title }}</a>
      by {{ post.author.fullName ?? post.author.email }}
    </li>
  @end
</ul>
<p>Total: {{ posts.total }}</p>

After signing up at /signup and posting the form to /posts, GET /posts renders <a href="/posts/1">Why we moved to AdonisJS 7</a> by Maya Okafor and Total: 1. Forms need the CSRF field — the kit's @form component adds it; in a hand-written form use {{ csrfField() }}.

Example 2: REST API with token auth

User prompt: "Create a REST API with AdonisJS, API token authentication, and pagination."

npm create adonisjs@latest inventory -- --kit=api
cd inventory
node ace make:model product -m && node ace make:transformer product
node ace make:controller products --api && node ace make:validator product
node ace migration:run   # after adding sku, name, price_cents and stock columns to the new migration
// start/routes.ts — inside the existing .prefix('/api/v1') group
router.resource('products', controllers.Products).apiOnly().use('*', middleware.auth())

// app/transformers/product_transformer.ts
export default class ProductTransformer extends BaseTransformer<Product> {
  toObject() { return this.pick(this.resource, ['id', 'sku', 'name', 'priceCents', 'stock']) }
}

// app/controllers/products_controller.ts
async index({ request, serialize }: HttpContext) {
  const products = await Product.query().orderBy('name').paginate(request.input('page', 1), 20)
  return serialize(ProductTransformer.paginate(products.all(), products.getMeta()))
}

async store({ request, response, serialize }: HttpContext) {
  const product = await Product.create(await request.validateUsing(createProductValidator))
  response.status(201)
  return serialize(ProductTransformer.transform(product))
}
API=http://localhost:3333/api/v1
curl -s -X POST $API/auth/signup -H 'Content-Type: application/json' \
  -d "{\"fullName\":\"Lena Fischer\",\"email\":\"lena@inventory.dev\",\"password\":\"$INVENTORY_PASSWORD\",\"passwordConfirmation\":\"$INVENTORY_PASSWORD\"}"
# {"data":{"token":"oat_…","user":{"id":1,"fullName":"Lena Fischer",…}}} — export the token as INVENTORY_TOKEN; POST $API/auth/login returns the same shape
curl -s -X POST $API/products -H "Authorization: Bearer $INVENTORY_TOKEN" -H 'Content-Type: application/json' -d '{"sku":"MUG-BLK-350","name":"Black mug 350 ml","priceCents":1290,"stock":48}'   # 201

curl -s "$API/products?page=1" -H "Authorization: Bearer $INVENTORY_TOKEN"
# {"data":[{"id":1,"sku":"MUG-BLK-350","name":"Black mug 350 ml","priceCents":1290,"stock":48}],
#  "metadata":{"total":1,"perPage":20,"currentPage":1,"lastPage":1,…}}

Without a token the API answers 401 {"errors":[{"message":"Unauthorized access"}]}. In Japa tests (node ace make:test products --suite=functional, node ace test functional) authenticate with client.get('/api/v1/products').loginAs(user).

Guidelines

  • Use node ace for everything — generators, migrations, REPL, build; node ace add @adonisjs/mail (or any official package) installs and configures it
  • Controllers stay thin — move business logic to services
  • VineJS for all input — never trust request.body() directly
  • serializeAs: null — the generated password column already has it; use transformers to decide which fields an API returns
  • Lucid relations are lazy — use .preload() or model.load(); transformers never query, so preload what they read
  • Migrations are sequential — don't modify old migrations; create new ones. Production needs node ace migration:run --force. migration:rollback --force also runs there (it drops tables) unless migrations.disableRollbacksInProduction: true is set on the connection in config/database.ts
  • make:model --controller quirk — in Lucid 22.4 it generates a transformer instead of a controller; run make:controller separately
  • CSRF — the hypermedia kit enables CSRF protection (config/shield.ts), so POST/PUT/PATCH/DELETE requests without a token are rejected; the api kit ships with it disabled
  • Secrets — APP_KEY and database credentials live in .env, validated in start/env.ts; .env is not copied into the build
  • Testing with Japa — built-in test runner, no Jest needed
  • Deploy: node ace build, then cd build && npm ci --omit=dev && node bin/server.js with NODE_ENV=production

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Scripts and configures production rendering in Autodesk 3ds Max with the V-Ray and Corona renderers: output size and files, render elements, denoising, light mix, batch and command-line rendering, and network rendering. Use when a user asks to set up a production render, render several cameras in one batch, render from the command line or on a render farm, add render passes for compositing, or cut render time for archviz and product shots.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Covers scripting Autodesk 3ds Max, the 3D modeling and rendering application, with MAXScript and Python (pymxs): scene manipulation, object creation, material assignment, camera and light setup, batch operations, and file I/O. Use when tasks involve automating repetitive 3ds Max workflows, batch processing scenes, running scripts headless with 3dsmaxbatch, creating custom tools, or scripting scene setup for archviz, product visualization, or VFX.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

3proxy

無料

3proxy is a small open-source proxy server that runs HTTP/HTTPS, SOCKS4/5, SNI and TCP/UDP port-mapping proxies from one config file. Use when a user asks to set up an HTTP or SOCKS5 proxy, add proxy users and passwords, write 3proxy access rules, chain or rotate upstream (parent) proxies, limit bandwidth, connections or monthly traffic per user, run 3proxy in Docker, or fix a 3proxy.cfg that will not start.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Builds Agent2Agent (A2A) servers and clients, the open protocol (originally from Google, now under the Linux Foundation) that lets AI agents from different frameworks call each other. Use when the user wants to create an A2A-compliant agent, build an Agent Card, implement task management, connect agents across frameworks, set up agent discovery, handle streaming responses, implement push notifications, or orchestrate multi-agent workflows. Trigger words: a2a, agent to agent, agent2agent, a2a protocol, a2a server, a2a client, agent card, agent interoperability, agent collaboration, multi-agent, agent discovery, a2a sdk, a2a task.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Plans a controlled experiment (A/B test) so its result can be trusted: writes the hypothesis, picks one primary metric and the guardrails, computes sample size and run time, specifies how visitors are assigned and when exposure is logged, and reads out the result with a confidence interval. Use when someone says "set up an A/B test", "split test this page", "how many visitors do I need", "how long should the experiment run", "is this result significant", "can I stop the test early", or wants to test a headline, price, layout or onboarding change against the current version.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

ably

無料

Ably is a hosted realtime messaging service: clients publish and subscribe to named channels over WebSockets, see who is present, replay message history, and resume after a dropped connection. Use when a user asks to "add realtime updates", "push live notifications to the browser", "show who is online", "add a chat room with typing indicators", "publish from a serverless function", or "authenticate Ably clients without exposing the API key". Covers the ably 2.x JavaScript SDK (Realtime and REST), JWT token authentication, presence, history and rewind, batch publishing, and the @ably/chat 1.x SDK.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

TerminalSkills のスキルをすべて見る

このスキルの問題を報告する