1.2.1 • Published 5 months ago

aws-serverless-utils v1.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

aws-serverless-utils

Utility functions for AWS serverless services like Lambda, EventBridge, and DynamoDB.

Build Status

codecov

Maintainability

Serverless is awesome but what happens when you have a ton of microservices and small APIs? Well, you replicate the same boring boilerplate everywhere, that's what happens! This packages resolves some of those issues.

  • Saves you time when building AWS serverless solutions
  • Sensible helpers for common boring boilerplate stuff
  • Lightweight
  • Zero dependencies, unless you use end(), endWithError(), or startLogger() which use MikroLog
  • Has 100% test coverage

Usage

Basic importing and usage

The basic pattern is to import each function you want to use, respectively, and then use them.

// ES5 format
const { end } = require('aws-serverless-utils');
// ES6 format
import { end } from 'aws-serverless-utils';

// One example could be...
return await end(); // Returns a 200 with CORS headers but no message

Functions

end()

Utility function to create a valid AWS Lambda response object. Note that headers will be completely replaced if you supply custom headers.

return await end(); // Returns a 200 with default CORS headers but no message
return await end({ statusCode: 204 }); // Returns a 204 with default CORS headers but no message
return await end({ statusCode: 200, message: { timeCreated: '2024-10-27', itemId: 'abc123' }}); // Returns a 200 with an object message
return await end({ statusCode: 200, headers: { 'X-Custom-Header': 'custom-value' }}); // Replaces the headers with a custom set of headers

async function flushFn() {
  await sendLogsToService(MikroLog.logBuffer)
};

return await end({ statusCode: 200, flushFn }); // Runs a "flush" function before ending

endWithError()

Utility function to create a valid AWS Lambda response object when encountering an error.

MikroLog will be initialized to log the error; because it's static, it will reuse any previous configuration.

The resulting status code will be the value of error.cause.statusCode or it'll use the default value if not found (falling back to status 400).

Any provided headers will be passed to the end() function. Please see the documentation for that function for more information.

This supports the optional flushFn argument as the regular end() function does.

getAuthContext()

Get the authorization data coming from the Lambda authorizer.

export async function handler(event, context) {
  const authData = getAuthContext(event); // Get any data from the Lambda authorizer.
}

getBearerToken()

Extract the Bearer token from the Authorization header. The value must start with Bearer.

const token = getBearerToken({ Authorization: 'Bearer some-very-long-value-123-abc...' }); // Result: 'some-very-long-value-123-abc...'

getCleanedDynamoItems()

Clean up and return DynamoDB items in a normalized format. Accepts one or multiple items and returns an array of cleaned objects. Returns a cleaned and parsed array of objects.

The optional includePkSk (set to false by default) will enable getting the pk and sk keys.

const items = getCleanedDynamoItems(
  [
    {
      pk: { S: 'item#123' },
      sk: { S: 'meta#456' },
      name: { S: 'Test Item' },
      age: { N: '25' },
      isActive: { BOOL: true },
      tags: { SS: ['tag1', 'tag2'] },
      metadata: { M: { key1: { S: 'value1' }, key2: { N: '100' } } },
      list: { L: [{ S: 'item1' }, { S: 'item2' }] }
    }
  ],
  true // This will return the pk and sk values, else they are omitted
);

// This is what items will look like
const result = {
  pk: 'item#123',
  sk: 'meta#456',
  name: 'Test Item',
  age: 25,
  isActive: true,
  tags: ['tag1', 'tag2'],
  metadata: { key1: 'value1', key2: 100 },
  list: ['item1', 'item2']
};

getCorrelationId()

Get correlation ID from:

  1. Environment, via process.env.CORRELATION_ID
  2. Event headers (event.headers.['x-correlation-id'] or event.headers.['X-Correlation-Id'])
  3. Event metadata (event.detail.metadata.correlationId or event.detail.metadata.correlation_id)
  4. Context request ID (awsRequestId)
  5. Empty string, if nothing is found

getInputBody()

Get the main input body from EventBridge or API Gateway input, in which case it also handles if the input is stringified JSON.

If you follow the convention with data + metadata in EventBridge, then the data object will be returned, otherwise the whole detail object will be returned. It will also handle Base64-encoded bodies.

export async function handler(event, context) {
  const body = getInputBody(event);
  const result = {
    myThingHere: true // You'll get a ready-to-use JSON object
  };
}

getMetadata()

Get the contents of an EventBridge metadata object, as per the data + metadata convention.

export async function handler(event, context) {
  const metadata = getMetadata(event); // EventBridge event
  const result = {
    tenantId: 'qwerty' // You'll get the `event.detail.metadata` data here
  };
}

getPathParameters()

Get the pathParameters object if it exists, else an empty object.

export async function handler(event, context) {
  const body = getPathParameters(event);
  const result = {
    recordId: 'abc123' // You'll get a ready-to-use JSON object
  };
}

getQueryStringParameters()

Get query string parameters if they exist, else an empty object.

export async function handler(event, context) {
  const body = getQueryStringParameters(event);
  const result = {
    isSpecialFeature: true // You'll get a ready-to-use JSON object
  };
}

handleCors()

Return a CORS response.

export async function handler(event, context) {
  if (event?.requestContext?.http?.method === 'OPTIONS') return handleCors();
}
{
    statusCode: 200,
    headers: {
      'Content-Type': 'text/plain',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      'Access-Control-Allow-Credentials': true
    },
    body: JSON.stringify('OK')
  }

isJsonString()

Check if input is stringified JSON.

isJsonString('asdf'); // false
isJsonString('{"abc": 123}'); // true

mapToDynamoAttribute()

Map out values as DynamoDB attributes. Use this to convert JSON to a shape ready to use with DynamoDB.

Supports mapping to:

  • NULL
  • BOOL
  • S
  • N
  • L
  • M
mapToDynamoAttribute({
  title: 'Complex Item',
  count: 5,
  isActive: true,
  tags: ['tag1', 'tag2'],
  metadata: {
    createdBy: 'user123',
    scores: [1, 2, 3],
    settings: { darkMode: false }
  }
});

// You'll get a result like this
const result = {
  M: {
    title: { S: 'Complex Item' },
    count: { N: '5' },
    isActive: { BOOL: true },
    tags: { L: [{ S: 'tag1' }, { S: 'tag2' }] },
    metadata: {
      M: {
        createdBy: { S: 'user123' },
        scores: { L: [{ N: '1' }, { N: '2' }, { N: '3' }] },
        settings: {
          M: {
            darkMode: { BOOL: false }
          }
        }
      }
    }
  }
};

startLogger()

Starts an instance of MikroLog with correlation ID already set up and returns it.

See the documentation for getCorrelationId() to understand how the ID is fetched.

License

MIT. See LICENSE file.

1.2.1

5 months ago

1.2.0

6 months ago

1.1.0

6 months ago

1.0.11

6 months ago

1.0.10

6 months ago

1.0.14

6 months ago

1.0.13

6 months ago

1.0.12

6 months ago

1.0.9

6 months ago

1.0.8

7 months ago

1.0.7

8 months ago

1.0.6

8 months ago

1.0.5

8 months ago

1.0.4

8 months ago

1.0.2

8 months ago

1.0.1

8 months ago

1.0.3

8 months ago

1.0.0

8 months ago

0.0.1

8 months ago