Actions

Actions are async Javascript functions that run on your app server. They are primarily used for building APIs by receiving JSON data from the browser. This is usually where you use your database, send emails or other things that can't happen in the browser directly.

Create JSON APIs

Creating a JSON API with Waveorb server actions is very easy and should be familiar if you've used NodeJS before. An action usually has a name instead of a URL path. The name is what you use with the client to access it.

AJAX, uploads and websockets all connect to the actions in the same way. This is how an action may look:

module.exports = async function($) {
  await $.filters(['authenticate', 'login-required'])
  await $.validate({
    values: {
      project_id: {
        is: 'id',
        required: true
      },
      content: {
        min: 3,
        required: true
      }
    }
  })
  const { values = {} } = $.params
  return await $.db('comment').create(values)
}

Normally you run filters first, then validations and then the rest of the function.

Return or throw stops the execution flow and return data to the client as JSON data.

Validations

Most of the time we need to validate the parameters sent to a server action. Here's a list of the built in validations:

// This is the name of the parameter
query: {
  // Run validations on specified fields
  name: {
    required: true,  // this means can not be undefined
    eq: 5,           // Equal to
    ne: 5,           // Not equal to
    gt: 5,           // Greater than
    lt: 5,           // Less than
    gte: 5,          // Greater than or equal to
    lte: 5,          // Less than or equal to
    in: [1, 2, 3],   // Must be in list
    nin: [1, 2, 3],  // Must not be in list
    length: 5,       // Length of string must be
    min: 5,          // Minimum length of string
    max: 5,          // Maximum length of string
    match: /regex/,  // Must match regex
    unique: 'user',  // Unique field in db model
    exist: 'user',   // Check if model exists in db
    matcher: async function(val, $) {
      // Validation fails on truthy value
      if (!val) {
        return $.t('some_error')
      }
      // Return nothing or undefined to pass
    },
    is: 'boolean',   // Must be true or false
    is: 'string',    // Must be a string
    is: 'number',    // Must be a number, integer or decimal (float)
    is: 'integer',   // Must be an integer
    is: 'decimal',   // Must be a decimal number
    is: 'date',      // Must be a date
    is: 'id',        // Must be an id
    is: 'object',    // Must be an object
    is: 'array',     // Must an array
    is: 'email',     // Must be an email address
    is: 'undefined', // Must be undefined
    is: 'url'        // Must be a URL
  }
}

If the any of the validations doesn't pass, an error message is returned. Have a look at the default translations for a complete list of the messages.

The error message then looks like this, with each failing field and errors as an array:

{
  error: { message: 'validation error' },
  query: { name: ['must be a URL'] }
}

Using actions from pages

In app/actions/project create a file called create.js with the following content:

module.exports = async function($) {
  await $.validate({
    values: {
      title: {
        required: true
      }
    }
  })
  return { status: 'OK' }
}

Then in one of your app's pages use this HTML:

<form onsubmit="return false">
  <label for="title">Title</label>
  <input id="title" type="text" name="title">
  <em class="error-title"></em>
  <button onclick="handleSubmit(this)">Save</button>
</form>

<script>
// Set up the Waveorb client
var api = waveorb('https://waveorb.com/api')

// Define your submit function
function handleSubmit(btn) {
  // Using the Haka form serializer to gather the data
  var values = serialize(btn.form)

  // Send the data to the action
  var result = await api('/project/create', { values })
  if (result.error) {
    // Join all the errors and display under the right input
    Object.keys(result.values).forEach(function(key) {
      text(`.error-${key}`, result.values[key].join(', '))
    })
  } else {
    // Redirect to project list
    window.location = '/projects'
  }
}
</script>

Example actions

These are example actions you can use as a template for your JSON API.

They are similar to the actions created by waveorb generate.

Create action

Use this action when you want to create a new document.

module.exports = async function($) {
  const { values = {} } = $.params
  return await $.db('model').create(values)
}

Update action

Use this action when you want to update a document.

module.exports = async function($) {
  const { query = {}, values = {} } = $.params
  return await $.db('model').update(query, values)
}

Get action

Use this action when you want to get a single document.

module.exports = async function($) {
  const { query = {}, fields = {} } = $.params
  return await $.db('model').get(query)
}

Find action

Use this action when you want to find documents. Supports fields, sort, skip and limit, and can be used for pagination.

module.exports = async function($) {
  const { query = {}, fields = {}, sort = {}, skip = 0, limit = 0 } = $.params
  return await $.db('model').find(query, { fields, sort, skip, limit })
}

Count action

Use this action to count documents.

module.exports = async function($) {
  const { query = {} } = $.params
  return { n: await $.db('model').count(query) }
}

Delete action

Use this action to delete documents.

module.exports = async function($) {
  const { query = {} } = $.params
  return await $.db('model').delete(query)
}

The model in the database queries here should be replaced with the name of your model.