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.
// 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):
nodejs/
└── node_modules/
└── csv-parser/
├── index.js
└── package.jsonUse it in a function (attach the csv-parser layer first):
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:
{
"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.
// 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:
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
// 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:
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.
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.
Shared Auth
↓ .zipJWT validation, token signing, role-based access control.
deps: jsonwebtoken
Database Pool
↓ .zipPostgreSQL connection pool with query helpers.
deps: pg
HTTP Utils
↓ .zipResponse helpers (json, error, 404, 401), CORS, body parsing.
deps: none
Structured Logger
↓ .zipJSON logging with levels, child loggers, timing.
deps: none
Email Sender
↓ .zipSMTP email via Nodemailer with template support.
deps: nodemailer