All skills

Inertia.js server-side rendering, redirects, shared data, forms, validation, and client-side patterns for The Boring JavaScript Stack. Use this skill when building pages, handling form submissions, managing redirects, sharing data, or working with any Inertia.js feature in a Sails.js application.

Use this Skill: https://skilld.dev/gh/sailscastshq/boring-stack/inertia

This session only. Nothing lands on disk.

rulesactions-and-exits.md

≈1.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Actions and Exits

The Four Action Patterns

In The Boring JavaScript Stack, every Sails action falls into one of four patterns based on its exit response type.

Pattern A: Render a Page (responseType: 'inertia')

Used for GET routes that display a page. The action returns { page, props }.

// api/controllers/dashboard/view-dashboard.js
module.exports = {
  exits: {
    success: { responseType: 'inertia' }
  },
  fn: async function () {
    var recentInvoices = await Invoice.find({
      creator: this.req.me.id
    })
      .sort('createdAt DESC')
      .limit(5)

    return {
      page: 'dashboard/index',
      props: { recentInvoices }
    }
  }
}

Pattern B: Redirect After Mutation (responseType: 'redirect')

Used for POST/PATCH/PUT/DELETE routes that succeed and redirect. The action returns a URL string. Inertia automatically follows the redirect with a GET request.

// api/controllers/auth/signup.js
module.exports = {
  inputs: {
    fullName: { type: 'string', required: true },
    email: { type: 'string', isEmail: true, required: true },
    password: { type: 'string', required: true, minLength: 8 }
  },
  exits: {
    success: { responseType: 'redirect' },
    emailTaken: { responseType: 'badRequest' }
  },
  fn: async function ({ fullName, email, password }) {
    var user = await User.create({ fullName, email, password })
      .intercept('E_UNIQUE', 'emailTaken')
      .fetch()

    this.req.session.userId = user.id
    return '/dashboard'
  }
}

Pattern C: Inertia Redirect / Full Page Visit (responseType: 'inertiaRedirect')

Used when you need to force a full page reload after a mutation. This sends a 409 status with an X-Inertia-Location header, which tells the Inertia client to do a full window visit. Use this when:

  • Shared data has changed and needs a fresh load (e.g., after profile update)
  • You want to ensure all cached once() props are refreshed
  • The URL is external or requires a full reload
// api/controllers/setting/update-profile.js
module.exports = {
  inputs: {
    fullName: { type: 'string' },
    email: { type: 'string', isEmail: true },
    avatar: { type: 'ref' }
  },
  exits: {
    success: { responseType: 'inertiaRedirect' },
    invalid: { responseType: 'badRequest' }
  },
  fn: async function ({ fullName, email, avatar }) {
    await User.updateOne({ id: this.req.me.id }).set({ fullName, email })

    sails.inertia.refreshOnce('loggedInUser')
    sails.inertia.flash('success', 'Profile updated successfully!')
    return '/settings/profile'
  }
}

Pattern D: Validation Error (responseType: 'badRequest')

Used when form validation fails. The action throws with a problems array. The badRequest response handler stores errors in the session and redirects back.

// Throwing validation errors:
throw {
  emailTaken: {
    problems: [{ email: 'An account with this email already exists.' }]
  }
}

// Or using .intercept():
await User.create({ email, password }).intercept('E_UNIQUE', () => ({
  emailTaken: {
    problems: [{ email: 'Email is already taken.' }]
  }
}))

Complete Action Example

A real-world action combining multiple exits:

// api/controllers/auth/login.js
module.exports = {
  inputs: {
    email: { type: 'string', isEmail: true, required: true },
    password: { type: 'string', required: true },
    rememberMe: { type: 'boolean', defaultsTo: false }
  },
  exits: {
    success: { responseType: 'redirect' },
    badCombo: { responseType: 'badRequest' },
    twoFactorRequired: { responseType: 'redirect' }
  },
  fn: async function ({ email, password, rememberMe }) {
    var user = await User.findOne({ email: email.toLowerCase() })
    if (!user) {
      throw { badCombo: { problems: [{ login: 'Wrong email/password.' }] } }
    }

    await sails.helpers.passwords
      .checkPassword(password, user.password)
      .intercept('incorrect', () => ({
        badCombo: { problems: [{ login: 'Wrong email/password.' }] }
      }))

    if (user.twoFactorEnabled) {
      this.req.session.partialLogin = { userId: user.id }
      throw { twoFactorRequired: '/verify-2fa' }
    }

    this.req.session.userId = user.id
    var returnUrl =
      (await sails.helpers.returnUrl.get(this.req)) || '/dashboard'
    return returnUrl
  }
}

Exit Response Types Reference

Response Type Status Use Case Action Returns
'inertia' 200 Render a page { page, props }
'redirect' 302/303 Redirect after mutation URL string
'inertiaRedirect' 409 Force full page reload URL string
'badRequest' 303 (redirect back) Validation error { problems: [...] }
'serverError' 500 Server error Error object
'notFound' 404 Resource not found (any)

Naming Conventions

Actions follow a naming convention based on their purpose:

  • View actions (GET): view-home.js, view-login.js, view-profile.js
  • Mutation actions (POST/PATCH/DELETE): signup.js, login.js, update-profile.js, delete-profile.js
  • API actions: create-team.js, update-team.js, remove-member.js

Accessing Request Context in Actions

Inside an action2 fn:

fn: async function (inputs) {
  // Access request/response
  this.req    // Request object
  this.res    // Response object

  // Session
  this.req.session.userId

  // Current user (if set by a hook)
  this.req.me

  // Inertia methods
  sails.inertia.share('key', value)
  sails.inertia.flash('success', 'Done!')
  sails.inertia.refreshOnce('loggedInUser')

  // Destructured inputs
  const { email, password } = inputs
}

Source: SKILL.md on GitHub

3 warnings17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides comprehensive documentation and guidelines for integrating Inertia.js within The Boring JavaScript Stack (Sails.js). No security vulnerabilities or malicious patterns were detected.

  • Socket17d

    1 alert: gptAnomaly

  • Snyk17d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    16/16 files flagged

Signed by skilld at fb2b7e0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 7 months ago
Other metadata
metadata
{
  "author": "sailscastshq",
  "version": "1.0.0",
  "tags": "inertia, sails, boring-stack, spa, forms, redirects, shared-data, props"
}

README badge

README badge for sailscastshq/boring-stack/inertia