Dependency Layers — Reusable Shared Packages for Functions

Layers are reusable code packages (ZIP archives) shared across functions. Upload once, attach to any function, and require() in your code.

// CSV Parser Layer

Create this file, ZIP it with the correct folder structure, then upload.

csv-parser/index.jstypescript
// Layer: csv-parser — handles CSV parsing, validation, and transformation

exports.parse = function(csvString, options = {}) {
  const { delimiter = ',', skipEmpty = true, trimValues = true } = options;
  const lines = csvString.trim().split('\n');
  if (lines.length < 2) throw new Error('CSV must have header + data rows');

  const headers = lines[0].split(delimiter).map(h => {
    let val = h.trim();
    if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
    return val;
  });

  const data = [];
  const errors = [];

  for (let i = 1; i < lines.length; i++) {
    const line = lines[i];
    if (skipEmpty && !line.trim()) continue;
    const values = line.split(delimiter).map(v => {
      let val = trimValues ? v.trim() : v;
      if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
      return val;
    });
    if (values.length !== headers.length) {
      errors.push({ row: i + 1, expected: headers.length, got: values.length });
      continue;
    }
    const row = {};
    headers.forEach((h, idx) => { row[h] = values[idx]; });
    data.push(row);
  }

  return { headers, data, errors, total: data.length };
};

exports.toCSV = function(jsonArray) {
  if (!Array.isArray(jsonArray) || jsonArray.length === 0) return '';
  const headers = Object.keys(jsonArray[0]);
  const rows = jsonArray.map(obj =>
    headers.map(h => {
      const val = String(obj[h] ?? '');
      return val.includes(',') ? '"' + val + '"' : val;
    }).join(',')
  );
  return [headers.join(','), ...rows].join('\n');
};

ZIP structure (drag into the Layers upload):

csv-parser.zipplaintext
nodejs/
└── node_modules/
    └── csv-parser/
        ├── index.js
        └── package.json

Use it in a function (attach the csv-parser layer first):

import-csv-function.jstypescript
const csv = require('csv-parser');

export const handler = async (event) => {
  const { csvData, delimiter } = event.body;

  if (!csvData) {
    return { statusCode: 400, body: { error: 'Missing csvData field' } };
  }

  try {
    const result = csv.parse(csvData, { delimiter: delimiter || ',' });
    return {
      statusCode: 200,
      body: {
        columns: result.headers,
        rows: result.total,
        errors: result.errors,
        data: result.data
      }
    };
  } catch (err) {
    return { statusCode: 400, body: { error: err.message } };
  }
};

Test payload:

test.jsonjson
{
  "body": {
    "csvData": "name,email,department\nAlice,alice@corp.io,Engineering\nBob,bob@corp.io,Marketing\nCarol,carol@corp.io,Design"
  }
}

// Stripe Payment Layer

Bundle Stripe helpers into a layer. Functions stay lightweight while accessing full payment processing.

stripe-helpers/index.jstypescript
// Layer: stripe-helpers — wraps Stripe API calls

const STRIPE_KEY = process.env.STRIPE_SECRET_KEY || 'sk_test_...';
const BASE = 'https://api.stripe.com/v1';

async function stripeRequest(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'Authorization': 'Bearer ' + STRIPE_KEY,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: body ? new URLSearchParams(body).toString() : undefined,
  });
  return res.json();
}

exports.createCustomer = (email, name) =>
  stripeRequest('POST', '/customers', { email, name });

exports.createPaymentIntent = (amount, currency = 'usd') =>
  stripeRequest('POST', '/payment_intents', {
    amount: String(amount),
    currency,
    automatic_payment_methods: { enabled: 'true' }
  });

exports.listCharges = (limit = 10) =>
  stripeRequest('GET', '/charges?limit=' + limit);

exports.getBalance = () =>
  stripeRequest('GET', '/balance');

Use in a function:

create-payment.jstypescript
const stripe = require('stripe-helpers');

export const handler = async (event) => {
  const { email, name, amount } = event.body;

  if (!email || !amount) {
    return { statusCode: 400, body: { error: 'email and amount required' } };
  }

  // Create customer
  const customer = await stripe.createCustomer(email, name || email);

  // Create payment intent (amount in cents)
  const payment = await stripe.createPaymentIntent(
    Math.round(amount * 100)
  );

  return {
    statusCode: 200,
    body: {
      customer_id: customer.id,
      payment_intent: payment.id,
      client_secret: payment.client_secret,
      amount: amount
    }
  };
};

// Database Pool Layer

db-pool/index.jstypescript
// Layer: db-pool — PostgreSQL connection pool
// Requires pg in node_modules alongside this file

const { Pool } = require('pg');

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 5,
  idleTimeoutMillis: 30000,
});

exports.query = (text, params) => pool.query(text, params);

exports.getOne = async (text, params) => {
  const result = await pool.query(text, params);
  return result.rows[0] || null;
};

exports.getMany = async (text, params) => {
  const result = await pool.query(text, params);
  return result.rows;
};

exports.insert = async (table, data) => {
  const keys = Object.keys(data);
  const values = Object.values(data);
  const placeholders = keys.map((_, i) => '$' + (i + 1)).join(', ');
  const result = await pool.query(
    'INSERT INTO ' + table + ' (' + keys.join(', ') + ') VALUES (' + placeholders + ') RETURNING *',
    values
  );
  return result.rows[0];
};

Use in a function:

user-lookup.jstypescript
const db = require('db-pool');

export const handler = async (event) => {
  const { email } = event.body;

  if (!email) {
    return { statusCode: 400, body: { error: 'email is required' } };
  }

  const user = await db.getOne('SELECT * FROM users WHERE email = $1', [email]);

  if (!user) {
    // Create the user
    const newUser = await db.insert('users', {
      email,
      name: email.split('@')[0],
      created_at: new Date().toISOString()
    });
    return { statusCode: 201, body: newUser };
  }

  return { statusCode: 200, body: user };
};

// Layer ZIP Structure

Every layer ZIP must follow this structure. The folder name inside node_modules is what you require() in your function.

my-layer.zipplaintext
nodejs/
└── node_modules/
    └── my-layer/              ← require('my-layer')
        ├── index.js           ← entry point
        ├── package.json
        └── node_modules/      ← sub-dependencies (optional)
            └── some-dep/

// Downloadable Layer ZIPs

Download ready-to-use layer ZIPs. Upload directly through the Layers dashboard.

// API Reference

GET/api/layersList all layers
POST/api/layersUpload new layer (multipart/form-data)
DELETE/api/layers/:idDelete a layer