set plugins

set plugins generate various kinds of resources for Architect projects. Resource setters are small, synchronous methods that can create many common kinds of resources, from HTTP routes, to environment variables, to custom runtimes.

Any Lambdas or resources defined by a set plugin is treated by Architect as a first-class primitive. For example: if you build a plugin that creates a route with set.http, Architect would treat that route as though the user had actually added it to their project manifest’s @http pragma.

Caveats

Unlike workflow lifecycle plugins, which execute only when needed, all set plugins must execute every time Architect (or any of its modules) run. Thus, they are expected to be synchronous and fast.

Because of this, we advise against such things as filesystem reads and writes from within set plugins. Definitely do not start services, or run large, CPU intensive operations from within set plugins.

Additionally, it is worth noting that set plugins are among the very first things to run in any Architect execution. Because they are run before the rest of the project been enumerated, they are not passed a complete Inventory object. If your plugin requires knowledge of your project, please access the arc property.

Plugin parameters

All set methods are synchronous functions, and receive a single argument, which is an object containing the following properties:

Property Type Description
arc object Raw Architect project object
inventory object Partial Inventory object

Valid returns

All set methods can return a single resource object, or an array of resource objects.

There is no limit to the number of resources a set plugin can return, however AWS does have limits on the number of resources in a CloudFormation deployment (and a hard cap on the size of a given CloudFormation document).

Generated resources that require a src property accept an absolute or relative file path. Additionally, file paths will be automatically platform normalized (so you do not have to use path.join() for other platforms if you’re publicly publishing your plugin).

By default, Lambdas created by the set API are assumed to run the latest version of Node.js (unless configured otherwise)


Pragmas

set.events

Register async events (as in the @events pragma). Return a single object or an array of objects with the following properties:

Property Type Description
name string Event name (follows @events syntax)
src string Absolute or relative file path to the handler

Example:

// Return a single async event
module.exports = { set: {
  events: ({ arc, inventory }) => {
    return {
      name: 'an-async-event',
      src: __dirname + '/handler' // Points to a handler dir inside the plugin
    }
  }
} }

set.http

Register HTTP routes (as in the @http pragma). Return a single object or an array of objects with the following properties:

Property Type Description
method string HTTP method (follows @http syntax)
path string HTTP path (follows @http syntax)
src string Absolute or relative file path to the handler

Example:

// Return multiple HTTP routes
module.exports = { set: {
  http: ({ arc, inventory }) => {
    let src = __dirname + '/handler' // Multiple Lambdas can use the same handler
    return [
      { method: 'get', path: '/foo', src },
      { method: 'put', name: '/bar', src }
    ]
  }
} }

set.queues

Register async event queues (as in the @queues pragma). Return a single object or an array of objects with the following properties:

Property Type Description
name string Event name (follows @queues syntax)
src string Absolute or relative file path to the handler

Example:

// Return a single async event queue
module.exports = { set: {
  queues: ({ arc, inventory }) => {
    return {
      name: 'a-queue',
      src: __dirname + '/handler' // Points to a handler dir inside the plugin
    }
  }
} }

set.scheduled

Register scheduled event (as in the @scheduled pragma). Return a single object or an array of objects with the following properties:

Property Type Description
name string Event name (follows @scheduled syntax)
rate string Rate expression, cannot be used with cron property
cron string Cron expression, cannot be used with rate property
src string Absolute or relative file path to the handler

Note: unlike in @scheduled pragma use, rate + cron properties should not be returned in parenthesis.

Example:

// Return two scheduled events: one using rate syntax, and one using cron syntax
module.exports = { set: {
  scheduled: ({ arc, inventory }) => {
    let src = __dirname + '/handler'
    return [
      {
        name: 'scheduled-using-rate',
        rate: '1 day',
        src,
      },
      {
        name: 'scheduled-using-cron',
        cron: '15 10 * * ? *',
        src,
      }
    ]
  }
} }

set['tables-streams']

Register DynamoDB event streams (as in the @tables-streams pragma). Return a single object or an array of objects with the following properties:

Property Type Description
name string Event name (follows @tables-streams syntax)
table string Logical DynamoDB table name (as in your project manifest)
src string Absolute or relative file path to the handler

Example:

// Return a single table stream
module.exports = { set: {
  'tables-streams': ({ arc, inventory }) => {
    return {
      name: 'a-table-stream-event',
      table: 'my-logical-table-name',
      src: __dirname + '/handler' // Points to a handler dir inside the plugin
    }
  }
} }

set.ws

Register WebSocket routes (as in the @ws pragma). Return a single object or an array of objects with the following properties:

Property Type Description
name string Route name (follows @ws syntax)
src string Absolute or relative file path to the handler

Note: WebSockets is required to have three default routes ($connect, $disconnect, $default), which Architect populates with the addition of the @ws pragma. If the consumer of your plugin does not specify @ws in their manifest, using set.ws will infer it for them; you should not attempt to return any of the default routes in your set.ws plugin.

Example:

// Return a single WebSocket route
module.exports = { set: {
  ws: ({ arc, inventory }) => {
    return {
      name: 'refresh',
      src: __dirname + '/handler' // Points to a handler dir inside the plugin
    }
  }
} }

Resources

set.env

Register environment variables for all Lambdas. To create an environment variable for all Lambdas, return an object with names and values. To create an environment variable specific to Architect’s built in testing, staging, and production environments, return an object containing one or more of those properties, each containing an object with names and value.

Note: if an object or array is passed as a value, the set.env API will automatically JSON-serialize it into your environment variable.

Examples:

// Return an environment variable for all Lambdas
module.exports = { set: {
  env: ({ arc, inventory }) => {
    return {
      API_SECRET: process.env.API_SECRET // Handy for exporting secrets in CI/CD
    }
  }
} }
// Return a different environment variables for different stages
module.exports = { set: {
  env: ({ arc, inventory }) => {
    return {
      testing: {
        API_SECRET: 'sample-key'
      },
      staging: {
        API_SECRET: process.env.API_SECRET
      },
      production: {
        API_SECRET: process.env.API_SECRET
      },
    }
  }
} }

set.customLambdas

Register bare Lambdas without a pre-associated event source. set.customLambdas pairs nicely with deploy.start, where you can customize a custom Lambda’s event source in CloudFormation. Return a single object or an array of objects with the following properties:

Property Type Description
name string Bare Lambda name
src string Absolute or relative file path to the handler

Example:

// Return a single async event
module.exports = { set: {
  customLambdas: ({ arc, inventory }) => {
    return {
      name: 'a-custom-lambda',
      src: __dirname + '/handler' // Points to a handler dir inside the plugin
    }
  }
} }

set.runtimes

Register custom runtimes for Lambdas. Return a single object or an array of objects. Each runtime type has its own set of requirements; transpiled is currently supported, compiled + interpeted are coming soon.

transpiled

A transpiled runtime assumes a source tree is authored per Architect’s typical project structure, with some form of transpilation step that results in build artifacts in a dist directory composed in an interpreted language (such as JavaScript).

For example, using Architect TypeScript, the get /foo handler is authored in src/http/get-foo/index.ts, and automatically transpiled to (and run / deployed from) build/http/get-foo/index.js.

Property Type Required Description
name string Yes Custom runtime name
type string Yes Must be transpiled
baseRuntime string Yes Lambda runtime identifier or alias
build string No Relative build dir path; defaults to build

Note: Architect’s built in shared code affordances are permanently disabled for transpiled output.

Example:

// Enable a custom build directory with a custom runtime pragma (`@typescript`)
module.exports = { set: {
  runtime: ({ arc, inventory }) => {
    let { arc } = inventory.inv._project
    let build = '.build'
    if (arc.typescript) {
      arc.typescript.forEach(s => {
        if (Array.isArray(s) && s[0] === 'build' && typeof s[1] === 'string') {
          build = s[1]
        }
      })
    }
    return {
      name: 'typescript',
      type: 'transpiled',
      baseRuntime: 'nodejs14.x',
      build,
    }
  }
} }

Advanced usage

lambda.config

Lambdas created by set plugins are treated like any other Lambda in the system, which means they are subject to project defaults. This may be convenient: if the project using your Node.js handler created by a set plugin is also by default Node.js, you don’t have to do anything.

However, if you are publishing your project publicly, you cannot assume all consumers of your plugin are running the same runtime as you. In this case, and other cases where greater customization and specificity is required, you should include a config property in your set Lambdas.

Any of the set APIs that create Lambdas (events, http, customLambdas, etc.) accept an optional config object with named properties (and values) that are the same as those found in function config.

These include: runtime, memory, timeout, concurrency, architecture, and more, and are subject to the same limitations as any other Lambda (e.g. if specifying layers, only 5 may be specified, and they must be in the same region as the app is deployed).

Example:

// Returning this event Lambda assumes user project defaults > Architect defaults
// If the project specifies `@aws runtime python3.9`, and your handler is JS, it will not run
module.exports = { set: {
  events: ({ arc, inventory }) => {
    return {
      name: 'an-async-event',
      src: __dirname + '/handler'
    }
  }
} }
// Returning a `config` property provides control over the configuration of the returned Lambda
module.exports = { set: {
  events: ({ arc, inventory }) => {
    return {
      name: 'an-async-event',
      src: __dirname + '/handler',
      config: {
        runtime: 'nodejs14.x',
        memory: 3008, // in MB
        timeout: 10, // in seconds
      }
    }
  }
} }

Where set Lambdas can live

Like those created by modifying a project manifest, Lambdas (and their handlers) specified by set plugins can live in the user’s project. We’ll call those userland Lambdas.

However, set plugins can also point to prepackaged functions and live inside a published plugin that do not allow for customization, because they’re intended to do a specific job on behalf of the plugin consumer. We’ll call those pluginland Lambdas.

Let’s take a look at some examples of how this might work.

Userland Lambdas

Say you’re writing a plugin called local-s3, which attaches Lambdas to specific S3 events. Your users need to be able to define and maintain the logic executed by S3 event Lambdas provisioned by your plugin. You may ask your user to update their project to define some handlers in a new custom pragma (@local-s3):

@plugins
local-s3

@local-s3
create
update
delete

Since these Lambdas live in userland, set.customLambdas method might look something like this:

module.exports = { set: {
  customLambdas: ({ arc, inventory }) => {
    let localS3 = arc['local-s3']
    if (!localS3 || !Array.isArray(localS3)) return

    // Create an abritrary number of plugins from the Arc manifest
    let lambdas = localS3.map((item) => {
      let name = item[0]
      return {
        name,
        src: `src/local-s3/${name}`
      }
    })
    return lambdas
  }
} }

This approach puts the Lambdas squarely in the realm of your plugin consumers, and empowers them to make customize the resources you’re managing with the plugin.

Pluginland Lambdas

Now let’s say you’re writing a plugin called autobundle to accomplish a specific task: automatically bundling JS from your project’s views directory at get /_bundle/:entry. Your users expect to consume this plugin like any other dependency, having it dropped right in and fully maintained by the plugin author.

Assuming you published your project as arc-plugin-autobundle, you might want your set plugin to look something like this:

// node_modules/arc-plugin-autobundle/index.js
module.exports = { set: {
  http: ({ arc, inventory }) => {
    return {
      method: 'get',
      path: 'get /_bundle/:entry',
      // Assuming a handler at `node_modules/arc-plugin-autobundle/handler/index.js`
      src: __dirname + '/handler'
    }
  }
} }

Returning the Lambda above, Architect will look for your autobundle Lambda handler at node_modules/arc-plugin-autobundle/handler/index.js. Should your handler have its own dependencies, you must declare them in your plugin’s package.json file.