stageVariable comes null

I have a lambda developed in Python that is working fine. Now, I decided to use the environment variables, but they always come back null. Below, project details:

  • Serverless version: 4.4.7

  • serverless.yml

service: App0001

provider:
name: aws
runtime: python3.9
region: us-east-2
stage: ${opt:stage, self:custom.defaultStage}
functions:
inicio:
handler: services/inicio/handler.inicio
events:
- http:
path: /inicio/{tenant}
method: POST
custom:
stages:
- local
- dev
- qa
- prod
environment: ${file(./env.yml)}
defaultStage: dev
stageVariables:
DATABASE_HOST: ${self:custom.environment.dbHost.${self:provider.stage}}
DATABASE_USER: ${self:custom.environment.dbUser.${self:provider.stage}}
DATABASE_PASS: ${self:custom.environment.dbPass.${self:provider.stage}}
DATABASE_NAME: ${self:custom.environment.dbName.${self:provider.stage}}
DATABASE_PORT: ${self:custom.environment.dbPort.${self:provider.stage}}
serverless-offline:
httpPort: 4000
websocketPort: 4001
lambdaPort: 4002
plugins:

  • serverless-wsgi

  • serverless-python-requirements

  • serverless-offline

  • env.yml

awsProfile:
local: default
dev: default
qa: default
prod: default
dbHost:
local: myHost
dev: myHost
qa: myHost
prod: myHost
dbUser:
local: myUser
dev: myUser
qa: myUser
prod: myUser
dbPass:
local: myPassword
dev: myPassword
qa: myPassword
prod: myPassword
dbName:
local: dev
dev: dev
qa: dev
prod: dev
dbPort:
local: ‘3306’
dev: ‘3306’
qa: ‘3306’
prod: ‘3306’

  • event

event: {
body: ‘{\r\n “codAplicacao”: “9F707962-FDBE-11EE-9657-02240D86274B”\r\n}\r\n’,
headers: {
‘Content-Type’: ‘application/json’,
‘User-Agent’: ‘PostmanRuntime/7.42.0’,
Accept: ‘/’,
‘Postman-Token’: ‘75acc31b-4449-47f0-93dd-4dcacb09e80e’,
Host: ‘localhost:4000’,
‘Accept-Encoding’: ‘gzip, deflate, br’,
Connection: ‘keep-alive’,
‘Content-Length’: ‘66’
},
httpMethod: ‘POST’,
isBase64Encoded: false,
multiValueHeaders: {
‘Content-Type’: [ ‘application/json’ ],
‘User-Agent’: [ ‘PostmanRuntime/7.42.0’ ],
Accept: [ ‘/’ ],
‘Postman-Token’: [ ‘75acc31b-4449-47f0-93dd-4dcacb09e80e’ ],
Host: [ ‘localhost:4000’ ],
‘Accept-Encoding’: [ ‘gzip, deflate, br’ ],
Connection: [ ‘keep-alive’ ],
‘Content-Length’: [ ‘66’ ]
},
multiValueQueryStringParameters: null,
path: ‘/inicio/hortech’,
pathParameters: { tenant: ‘hortech’ },
queryStringParameters: null,
requestContext: {
accountId: ‘offlineContext_accountId’,
apiId: ‘offlineContext_apiId’,
domainName: ‘offlineContext_domainName’,
domainPrefix: ‘offlineContext_domainPrefix’,
extendedRequestId: ‘6a609116-bc33-49f7-90a4-f91420887126’,
httpMethod: ‘POST’,
identity: {
accessKey: null,
accountId: ‘offlineContext_accountId’,
apiKey: ‘offlineContext_apiKey’,
apiKeyId: ‘offlineContext_apiKeyId’,
caller: ‘offlineContext_caller’,
cognitoAuthenticationProvider: ‘offlineContext_cognitoAuthenticationProvider’,
cognitoAuthenticationType: ‘offlineContext_cognitoAuthenticationType’,
cognitoIdentityId: ‘offlineContext_cognitoIdentityId’,
cognitoIdentityPoolId: ‘offlineContext_cognitoIdentityPoolId’,
principalOrgId: null,
sourceIp: ‘::1’,
user: ‘offlineContext_user’,
userAgent: ‘PostmanRuntime/7.42.0’,
userArn: ‘offlineContext_userArn’
},
operationName: undefined,
path: ‘/inicio/hortech’,
protocol: ‘HTTP/1.1’,
requestId: ‘29812468-9b52-413b-a16f-2cbf8a1cff7c’,
requestTime: ‘29/Oct/2024:17:10:11 -0400’,
requestTimeEpoch: 1730236211431,
resourceId: ‘offlineContext_resourceId’,
resourcePath: ‘/dev/inicio/{tenant}’,
stage: ‘dev’
},
resource: ‘/inicio/{tenant}’,
stageVariables: null
}

Could you edit your post to correctly format it so it’s easier to read ?

I cant see a button to edit. I’ll reply above

I have a lambda developed in Python that is working fine. Now, I decided to use the environment variables, but they always come back null. Below, project details:

  • Serverless version: 4.4.7

serverless.yml

service: App0001

provider:
  name: aws
  runtime: python3.9
  region: us-east-2
  stage: ${opt:stage, self:custom.defaultStage}
functions:
  inicio:
    handler: services/inicio/handler.inicio
    events:
      - http:
          path: /inicio/{tenant}
          method: POST
custom:
  stages:
    - local
    - dev
    - qa
    - prod
  environment: ${file(./env.yml)}
  defaultStage: dev
  stageVariables:
    DATABASE_HOST: ${self:custom.environment.dbHost.${self:provider.stage}}
    DATABASE_USER: ${self:custom.environment.dbUser.${self:provider.stage}}
    DATABASE_PASS: ${self:custom.environment.dbPass.${self:provider.stage}}
    DATABASE_NAME: ${self:custom.environment.dbName.${self:provider.stage}}
    DATABASE_PORT: ${self:custom.environment.dbPort.${self:provider.stage}}
  serverless-offline:
    httpPort: 4000
    websocketPort: 4001
    lambdaPort: 4002
plugins:
  - serverless-wsgi
  - serverless-python-requirements
  - serverless-offline

env.yml

awsProfile:
 local: default
 dev: default
 qa: default
 prod: default
dbHost:
 local: myHost.us-east-2.rds.amazonaws.com
 dev: myHost.us-east-2.rds.amazonaws.com
 qa: myHost.us-east-2.rds.amazonaws.com
 prod: myHost.us-east-2.rds.amazonaws.com
dbUser:
 local: myUser
 dev: myUser
 qa: myUser
 prod: myUser
dbPass:
 local: myPassword
 dev: myPassword
 qa: myPassword
 prod: myPassword
dbName:
 local: dev
 dev: dev
 qa: qa
 prod: prod
dbPort:
 local: '3306'
 dev: '3306'
 qa: '3306'
 prod: '3306'

even(local running)

event: {
body: ‘{\r\n “codAplicacao”: “9F707962-FDBE-11EE-9657-02240D86274B”\r\n}\r\n’,
headers: {
‘Content-Type’: ‘application/json’,
‘User-Agent’: ‘PostmanRuntime/7.42.0’,
Accept: ‘/’,
‘Postman-Token’: ‘75acc31b-4449-47f0-93dd-4dcacb09e80e’,
Host: ‘localhost:4000’,
‘Accept-Encoding’: ‘gzip, deflate, br’,
Connection: ‘keep-alive’,
‘Content-Length’: ‘66’
},
httpMethod: ‘POST’,
isBase64Encoded: false,
multiValueHeaders: {
‘Content-Type’: [ ‘application/json’ ],
‘User-Agent’: [ ‘PostmanRuntime/7.42.0’ ],
Accept: [ ‘/’ ],
‘Postman-Token’: [ ‘75acc31b-4449-47f0-93dd-4dcacb09e80e’ ],
Host: [ ‘localhost:4000’ ],
‘Accept-Encoding’: [ ‘gzip, deflate, br’ ],
Connection: [ ‘keep-alive’ ],
‘Content-Length’: [ ‘66’ ]
},
multiValueQueryStringParameters: null,
path: ‘/inicio/hortech’,
pathParameters: { tenant: ‘hortech’ },
queryStringParameters: null,
requestContext: {
accountId: ‘offlineContext_accountId’,
apiId: ‘offlineContext_apiId’,
domainName: ‘offlineContext_domainName’,
domainPrefix: ‘offlineContext_domainPrefix’,
extendedRequestId: ‘6a609116-bc33-49f7-90a4-f91420887126’,
httpMethod: ‘POST’,
identity: {
accessKey: null,
accountId: ‘offlineContext_accountId’,
apiKey: ‘offlineContext_apiKey’,
apiKeyId: ‘offlineContext_apiKeyId’,
caller: ‘offlineContext_caller’,
cognitoAuthenticationProvider: ‘offlineContext_cognitoAuthenticationProvider’,
cognitoAuthenticationType: ‘offlineContext_cognitoAuthenticationType’,
cognitoIdentityId: ‘offlineContext_cognitoIdentityId’,
cognitoIdentityPoolId: ‘offlineContext_cognitoIdentityPoolId’,
principalOrgId: null,
sourceIp: ‘::1’,
user: ‘offlineContext_user’,
userAgent: ‘PostmanRuntime/7.42.0’,
userArn: ‘offlineContext_userArn’
},
operationName: undefined,
path: ‘/inicio/hortech’,
protocol: ‘HTTP/1.1’,
requestId: ‘29812468-9b52-413b-a16f-2cbf8a1cff7c’,
requestTime: ‘29/Oct/2024:17:10:11 -0400’,
requestTimeEpoch: 1730236211431,
resourceId: ‘offlineContext_resourceId’,
resourcePath: ‘/dev/inicio/{tenant}’,
stage: ‘dev’
},
resource: ‘/inicio/{tenant}’,
stageVariables: null
}

Oh, thanks, much easier.

But I can’t see something like “I expect to see X, but I see Y” ? Just dumping the event isn’t clear, to me, what is wrong.

Have you a super simple reproducible case you could post your Python code for, if it’s a run time issue you are seeing ?

Ok, but i think that there are something wrong with serverless.yml and env.yml declaration, because the program works well. At this momment. my goal is to implement the environment variables. When the main procedure is called, stageVariables inside of event parameter comes null.

Above, the handler code:

handler.py

import json
import pymysql
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# old code, used without stageVariables
# db_username = 'myUser'
# db_password = 'myPassword'
# db_name = 'dev'
# db_endpoint = 'myHost.us-east-2.rds.amazonaws.com'
# db_port = 3306


def inicio(event, context):
    resposta = {
        'mensagem': [],
        'retorno': {},
        'jwt': {}
    }

    retorno = {
        'dscAplicacao': '',
        'dscAppCaption': '',
        'traducoes': [],
        'idiomas': []
    }

    payload = json.loads(event.get('body', {}))

    nomUrlTenant = event['pathParameters']['tenant']
    
    # some validations

    # conn = dbConnection() old code, used without stageVariables
    conn = dbConnectionII(event)

    if conn is None:
        resposta['mensagem'].append({'level_Error': 'e', 'dsc_error': 'Connection open error'})
        return getResponse(500, resposta)
    
    # some code

    return getResponse(200, resposta, conn)

def getResponse(status, resposta, conn=None, cursorQuery=None):
    response = {
        "statusCode": status,
        "body": json.dumps(resposta)
    }

    return response

def dbConnectionII(event):
    stage_variables = event.get('stageVariables', {}) or {}

    db_name = stage_variables.get('DATABASE_NAME')
    db_username = stage_variables.get('DATABASE_USER')
    db_password = stage_variables.get('DATABASE_PASS')
    db_host = stage_variables.get('DATABASE_HOST')
    db_port = stage_variables.get('DATABASE_PORT')
    print('db_name: ', db_name)
    conn = None

    try:
        conn = pymysql.connect(host=db_host, user=db_username,
                               passwd=db_password, db=db_name, connect_timeout=5,
                               cursorclass=pymysql.cursors.DictCursor, port=db_port)
    except pymysql.MySQLError as e:
        print('Connection open error: (%s): %s', type(e), e)

    return conn

# old code, used without stageVariables
# def dbConnection():
#     conn = None
#
#     try:
#         conn = pymysql.connect(host=db_endpoint, user=db_username,
#                                passwd=db_password, db=db_name, connect_timeout=5,
#                                cursorclass=pymysql.cursors.DictCursor)
#     except pymysql.MySQLError as e:
#         logger.error('Erro abrindo a conexão' + str(e))
#
#     return conn

Shouldn’t this be something like db_name = os.environ["DATABASE_NAME"] or am I misunderstanding ?

Package os is another way to do that. Additionally, it is necessary to use the library python-dotenv to manage a env file. Thank you to show me this way.

However, the approach I am using has already been used in previous projects and appears as an example in several forums, including here.

Therefore, I would like to insist on this approach, asking colleagues if they can see anything wrong with the reported code.