Machine as Action Controller

10.3.1 · active · verified Wed Apr 22

machine-as-action is a Node.js utility designed to bridge the gap between 'machines' – a pattern for encapsulating reusable business logic – and standard HTTP/WebSocket request-response cycles. It allows developers to define a machine and then wrap it with `asAction` to automatically handle incoming request parameters as machine inputs and map machine exits to various HTTP response types, including JSON data, rendered views, or redirects. The current stable version is 10.3.1, though recent release notes show significant changes around version 7.x. While there isn't a strict, rapid release cadence apparent from the provided data, updates have been made. Its primary differentiator lies in its deep integration with the 'machine' pattern, providing a structured and convention-over-configuration approach to expose API endpoints, particularly useful in frameworks like Sails.js where machines are a core architectural component. It offers granular control over response status codes and view rendering based on the outcome of a machine's execution, aiming to reduce boilerplate in controller logic.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to wrap an inline machine with `asAction` and simulate its execution using mock `req` and `res` objects. It covers basic JSON responses and advanced view rendering, illustrating how machine outputs are mapped to HTTP responses.

const asAction = require('machine-as-action');

// Simulate Express/Sails req/res objects for demonstration
const mockReq = (query = {}, body = {}) => ({ query, body });
const mockRes = () => {
  let _status = 200;
  let _data = null;
  let _view = null;
  let _redirect = null;

  return {
    status: function(code) { _status = code; return this; },
    json: function(data) { _data = data; console.log(`[Response JSON] Status: ${_status}, Data: ${JSON.stringify(_data)}`); },
    send: function(data) { _data = data; console.log(`[Response Send] Status: ${_status}, Data: ${data}`); },
    view: function(template, locals) { _view = { template, locals }; console.log(`[Response View] Status: ${_status}, Template: ${_view.template}, Locals: ${JSON.stringify(_view.locals)}`); },
    redirect: function(url) { _redirect = url; console.log(`[Response Redirect] Status: ${_status}, URL: ${_redirect}`); },
    serverError: function(err) { _status = 500; _data = { error: err.message }; console.error(`[Server Error] Status: ${_status}, Data: ${JSON.stringify(_data)}`); }
  };
};

// 1. Define an inline machine
const greetMachine = {
  inputs: {
    name: { type: 'string', required: true, example: 'World' }
  },
  exits: {
    success: { outputExample: 'Hello World!' },
    error: { outputExample: 'Could not greet.' }
  },
  fn: function(inputs, exits) {
    if (inputs.name) {
      return exits.success(`Hello, ${inputs.name}!`);
    }
    return exits.error(new Error('Name input is missing.'));
  }
};

// 2. Wrap the machine as an action
const greetAction = asAction(greetMachine);

// 3. Simulate an HTTP request for greetAction
console.log('--- Simulating a simple custom action ---');
greetAction(mockReq({ name: 'Registry User' }), mockRes());

// 4. Define an action with a custom responseType (e.g., 'view')
const showProfilePageAction = asAction({
  inputs: {
    userId: { type: 'string', required: true, example: 'user-123' }
  },
  exits: {
    success: {
      responseType: 'view',
      viewTemplatePath: 'profile/show',
      outputExample: { username: 'Alice', id: 'user-123' }
    }
  },
  fn: function(inputs, exits) {
    // In a real app, this would fetch user data from a database
    const userData = { username: `User ${inputs.userId}`, id: inputs.userId };
    return exits.success(userData);
  }
});

// 5. Simulate an HTTP request for the view action
console.log('\n--- Simulating an action rendering a view ---');
showProfilePageAction(mockReq({ userId: '456' }), mockRes());

view raw JSON →