More flexible serverless framework configuration files

bg

More flexible serverless framework configuration file

Foreword

After the deployment of the pre-tutorial, I wonder if you have noticed such an issue, that is, the function name we deployed, and the endpoint of API gateway, their names and The paths all contain a dev?

This is caused by stage. When we execute sls deploy deployment, because the --stage parameter is not specified, it defaults to dev, so the function name we deployed before is included in the gateway.

So what does it do? In fact, this value is used to distinguish stages/environments for your functions and corresponding services.

For example, for a function that provides web services, we artificially divide into three environments:

  1. dev is used for developers to test themselves
  2. sit is used for integration testing
  3. prod production environment

Different environments have different endpoint allocated by their respective API gateway.

If you have a domain name, you can configure the domain name with some more primary or multi-level domain names, and resolve their CNAME to the specified API gateway address for use. Of course, if you are only in the domain name console, direct resolution will not take effect, because API Gateway has a two-factor verification, and you must enter the Custom Domain Name< of API Gateway. /code>interface, create a custom domain name and bind the ACM certificate. The resolution will take effect only after passing the review.

What is ACM Certificate? The full name of ACM is Amazon Certificate Manager, click here to see more

Don’t worry, applying for and issuing an ACM certificate is very simple. Its functions and application process are the same as our application for a free SSL/TLS certificate, which is one more resolution of our domain name. CNAME thing.

At this time we can naturally use cli option to set:

"scripts": {<!-- -->
  "deploy:dev": "sls deploy",
  "deploy:sit": "sls deploy -s sit",
  "deploy:prod": "sls deploy -s prod"
},

But as the project becomes more and more complex, you will find that using the CLI command to continuously add configuration items is both cumbersome and inefficient. Is there any way to use one variable to control a large number of configuration items? What about configuration?

In serverless framework, we can usually use dynamic variables to solve this problem.

Dynamic variables

What are dynamic variables? In fact, they are just strings written in a special way. Common ways of writing variables are as follows:

${<!-- -->variableSource}
${<!-- -->sls:stage}-lambdaName
${<!-- -->env:MY_API_KEY}
${<!-- -->file(create_request.json)}
${<!-- -->self:service}:${<!-- -->sls:stage}:UsersTableArn

serverless.yml supports the use of the above variables to reference and read configurations. They are very useful. After all, you cannot put certain configuration items, such as secret Write directly inline in the yml file.

Among them, ${} is the way to write variable references, which will replace them with real values when sls cli is run.

# ${} The second parameter is the default value
otherYamlKey: ${<!-- -->variableSource, defaultValue}

Here are some commonly used variables:

self

The first thing we must talk about is self. It is the key for our application to configure other values of itself. self points to the root of our yml configuration. node. Here I give an example, I believe you are smart enough to see its usage at a glance.

service: new-service
provider: aws
custom:
  globalSchedule: rate(10 minutes)
  # The first line quoted service: new-service
  serviceName: ${<!-- -->self:service}
  # The previous line quoted serviceName: ${self:service}
  exportName: ${<!-- -->self:custom.serviceName}-export

functions:
  hello:
    handler: handler.hello
    events:
      # ${self:someProperty}
      # self points to the root node of the yml configuration, so it can
      - schedule: ${<!-- -->self:custom.globalSchedule}
resources:
  Outputs:
    NewServiceExport:
      Value: 'A Value To Export'
      Export:
        Name: ${<!-- -->self:custom.exportName}

env

As the name suggests, use system-configured environment variables:

service: new-service
provider: aws
functions:
  hello:
    name: ${<!-- -->env:FUNC_PREFIX}-hello
    handler: handler.hello

This is generally easier to use with tools such as dotenv.

sls

This variable can get some Serverless Core values, such as instanceId and stage, the use example is as follows

service: new-service
provider: aws
 
functions:
  func1:
    name: function-1
    handler: handler.func1
    environment:
      APIG_DEPLOYMENT_ID: ApiGatewayDeployment${<!-- -->sls:instanceId}
      STAGE: ${<!-- -->sls:stage}

The essence of the ${sls:stage} command is actually the abbreviation of ${opt:stage, self:provider.stage, "dev"}, so You also understand why the default value is dev.

opt

This variable is used to get the value in Options passed in by the CLI:

service: new-service
provider: aws
functions:
  hello:
    name: ${<!-- -->opt:stage}-hello
    handler: handler.hello

file

The core method/variable of modular configuration. Use this variable method to read files and reference them. For example, we can introduce additional yml, json, js file:

# You can even import the entire yml file as a configuration
custom: ${<!-- -->file(./myCustomFile.yml)}
provider:
  name: aws
  environment:
    #Introduce json file
    MY_SECRET: ${<!-- -->file(./config.${<!-- -->opt:stage, 'dev'}.json):CREDS}
 
functions:
  hello:
    handler: handler.hello
    events:
      - schedule: ${<!-- -->file(./myCustomFile.yml):globalSchedule} # Or you can reference a specific property
  world:
    handler: handler.world
    events:
      # You can even import `js` files
      - schedule: ${<!-- -->file(./scheduleConfig.js):rate}

At this time, it will read different configuration files under relative paths based on the stage parameter we passed in.

There are certain restrictions on the code that introduces js files. It must be in commonjs format, and must export a js object, or export a function. The method of exporting objects is very simple and not very versatile. Here we take the method as an example:

//Currently it must be in commonjs format and return an object as the value
// The method can be synchronous or asynchronous
module.exports = async ({<!-- --> options, resolveVariable }) => {<!-- -->
  // We can resolve other variables via `resolveVariable`
  const stage = await resolveVariable('sls:stage');
  const region = await resolveVariable('opt:region, self:provider.region, "us-east-1"');
  ...
 
  // Resolver may return any JSON value (null, boolean, string, number, array or plain object)
  return {<!-- -->
    prop1: 'someValue',
    prop2: 'someOther value'
  }
}

We can get other variables in it and perform additional calculations, or we can request remote data acquisition here as additional configuration for deployment.

More

In addition, it can also reference variables of more services, such as CloudFormation, S3, SSM and other services, and more For comprehensive variables, please see: Official variable directory address

Configure other supported formats

In fact, the current serverless cli not only accepts serverless.yml, but also accepts serverless.ts, serverless.json code>, serverless.js format.

Here the author only recommends two formats serverless.yml and serverless.js

Why not serverless.json? Because the expressiveness of json files is weak, and even jsonc is required to support comments, so I gave up.

Why not serverless.ts? Because it will cause errors if used directly. See issues/48 for details, which is not worry-free. It is better to use the combination of js + jsdoc. There are smart prompts and flexibility.

So let’s summarize the advantages of serverless.yml and serverless.js:

  • serverless.yml: Simple enough, more flexible with dynamic variables

  • serverless.js: You can use nodejs api, or it can be used with dynamic variables, which is more flexible, and the configuration can use the native js writing method.

Here we take the serverless.js configuration file as an example:

Smart prompt, requires npm i -D @serverless/typescript

/**
 * @typedef {import('@serverless/typescript').AWS} AWS
 * @type {AWS}
 */
const serverlessConfiguration = {<!-- -->
  service: 'aws-node-ts-hello-world',
  frameworkVersion: '3',
  provider: {<!-- -->
    // ...
  },
  functions: {<!-- -->
    // ...
  },
}

module.exports = serverlessConfiguration

Of course, since it is a nodejs runtime, you can split the configuration file into multiple files and then use commonjs to reference them natively. You can also use environment variables, node:fs, node:path and other modules to dynamically generate configuration files based on conditions, or even use some third-party libraries to do some additional It’s such a flexible job!

Next Chapter

Now you have just scratched the surface of serverless framework dynamic configuration files.

The next article, “Combining with traditional nodejs web framework”, will introduce in detail how to deploy express/koa and serverless based on them 2 application, welcome to read.

Complete examples and article warehouse address

https://github.com/sonofmagic/serverless-aws-cn-guide

If you encounter any problems or find any errata, please submit issue to me

syntaxbug.com © 2021 All Rights Reserved.