Function Bay is a serverless function deployment and orchestration service that wraps cloud function providers (like AWS Lambda) to provide a unified API for deploying, versioning, invoking, and managing serverless functions.
- Multi-Provider Support: Currently supports AWS Lambda with extensible architecture for additional providers
- Function Management: Define, version, and manage serverless functions with configurations
- Deployment Orchestration: Automated deployment workflows with build tracking and logging via Forge
- Runtime Support: Multiple runtime environments including Node.js, Python, Ruby, and Java
- Function Invocation: Direct function invocation with payload support and detailed logging
- Version Control: Track and manage different versions of your functions
- Instance Isolation: Multi-tenant support with instance-based resource isolation
- Configuration Management: Memory, timeout, and environment variable configuration per deployment
Create a docker-compose.yml file:
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: function-bay
POSTGRES_PASSWORD: function-bay
POSTGRES_DB: function-bay
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- function-bay-network
redis:
image: redis:7-alpine
networks:
- function-bay-network
object-storage:
image: ghcr.io/metorial/object-storage:latest
ports:
- "52010:52010"
volumes:
- object-store-data:/app/data
environment:
RUST_LOG: info
OBJECT_STORE__SERVER__HOST: 0.0.0.0
OBJECT_STORE__SERVER__PORT: 52010
OBJECT_STORE__BACKEND__TYPE: local
networks:
- function-bay-network
forge:
image: ghcr.io/metorial/forge:latest
ports:
- "52020:52020"
environment:
DATABASE_URL: postgresql://function-bay:function-bay@postgres:5432/forge
REDIS_URL: redis://redis:6379/0
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
DEFAULT_PROVIDER: aws.code-build
OBJECT_STORAGE_URL: http://object-storage:52010
LOG_BUCKET_NAME: logs
ARTIFACT_BUCKET_NAME: artifacts
CODE_BUILD_AWS_REGION: ${CODE_BUILD_AWS_REGION}
CODE_BUILD_AWS_ACCESS_KEY_ID: ${CODE_BUILD_AWS_ACCESS_KEY_ID}
CODE_BUILD_AWS_SECRET_ACCESS_KEY: ${CODE_BUILD_AWS_SECRET_ACCESS_KEY}
CODE_BUILD_PROJECT_NAME: ${CODE_BUILD_PROJECT_NAME}
CODE_BUILD_ROLE_ARN: ${CODE_BUILD_ROLE_ARN}
depends_on:
- postgres
- redis
- object-storage
networks:
- function-bay-network
function-bay:
image: ghcr.io/metorial/function-bay:latest
ports:
- "52030:52030"
environment:
DATABASE_URL: postgresql://function-bay:function-bay@postgres:5432/function-bay
REDIS_URL: redis://redis:6379/0
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
DEFAULT_PROVIDER: ${DEFAULT_PROVIDER}
OBJECT_STORAGE_URL: http://object-storage:52010
BUNDLE_BUCKET_NAME: bundles
FORGE_API_URL: http://forge:52020/metorial-forge
LAMBDA_AWS_REGION: ${LAMBDA_AWS_REGION}
LAMBDA_AWS_ACCESS_KEY_ID: ${LAMBDA_AWS_ACCESS_KEY_ID}
LAMBDA_AWS_SECRET_ACCESS_KEY: ${LAMBDA_AWS_SECRET_ACCESS_KEY}
LAMBDA_ROLE_ARN: ${LAMBDA_ROLE_ARN}
depends_on:
- postgres
- redis
- object-storage
- forge
networks:
- function-bay-network
volumes:
postgres_data:
object-store-data:
networks:
function-bay-network:
driver: bridgeCreate a .env file:
# Generate a random 32-character encryption key
ENCRYPTION_KEY=your-32-character-encryption-key
# Default provider (aws.lambda or gcp.cloud-functions or azure.functions)
DEFAULT_PROVIDER=aws.lambda
# AWS Lambda Configuration
LAMBDA_AWS_REGION=us-east-1
LAMBDA_AWS_ACCESS_KEY_ID=your-access-key
LAMBDA_AWS_SECRET_ACCESS_KEY=your-secret-key
LAMBDA_ROLE_ARN=arn:aws:iam::account:role/your-lambda-role
# AWS CodeBuild Configuration (for Forge)
CODE_BUILD_AWS_REGION=us-east-1
CODE_BUILD_AWS_ACCESS_KEY_ID=your-access-key
CODE_BUILD_AWS_SECRET_ACCESS_KEY=your-secret-key
CODE_BUILD_PROJECT_NAME=your-project-name
CODE_BUILD_ROLE_ARN=arn:aws:iam::account:role/your-codebuild-roleStart the services:
docker-compose up -dThe Function Bay service will be available at http://localhost:52030
npm install @metorial-services/function-bay-client
yarn add @metorial-services/function-bay-client
bun add @metorial-services/function-bay-clientimport { createFunctionBayClient } from '@metorial-services/function-bay-client';
let client = createFunctionBayClient({
endpoint: 'http://localhost:52030',
});Instances represent isolated tenants or projects:
// Create/update an instance
let instance = await client.instance.upsert({
name: 'My Project',
identifier: 'my-project',
});
// Get an instance
let retrievedInstance = await client.instance.get({
instanceId: instance.id,
});List available runtimes for your functions:
// List available runtimes
let runtimes = await client.runtime.list({
instanceId: instance.id,
limit: 10,
order: 'desc',
});
// Get a specific runtime
let runtime = await client.runtime.get({
runtimeId: runtimes.items[0].id,
});
console.log('Runtime:', runtime.identifier);
console.log('Specification:', runtime.specification);Functions define serverless function resources:
// Create/update a function
let func = await client.function.upsert({
instanceId: instance.id,
name: 'My API Handler',
identifier: 'api-handler',
});
// List functions
let functions = await client.function.list({
instanceId: instance.id,
limit: 10,
order: 'desc',
});
// Get a specific function
let functionDetails = await client.function.get({
instanceId: instance.id,
functionId: func.id,
});
// Update a function
let updated = await client.function.update({
instanceId: instance.id,
functionId: func.id,
name: 'Updated API Handler',
});Deploy function code with specific runtime and configuration:
// Create a deployment
let deployment = await client.functionDeployment.create({
instanceId: instance.id,
functionId: func.id,
name: 'v1.0.0',
runtime: {
identifier: 'nodejs',
version: '24.x',
},
config: {
memorySizeMb: 512,
timeoutSeconds: 30,
},
env: {
NODE_ENV: 'production',
API_KEY: 'your-api-key',
},
files: [
{
filename: 'index.js',
content: `
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from Function Bay!' }),
};
};
`,
encoding: 'utf-8',
},
{
filename: 'package.json',
content: JSON.stringify({
name: 'my-function',
type: 'module',
}),
encoding: 'utf-8',
},
],
});
console.log('Deployment ID:', deployment.id);
console.log('Status:', deployment.status);
// List deployments
let deployments = await client.functionDeployment.list({
instanceId: instance.id,
functionId: func.id,
limit: 20,
order: 'desc',
});
// Get deployment details
let deploymentDetails = await client.functionDeployment.get({
instanceId: instance.id,
functionId: func.id,
functionDeploymentId: deployment.id,
});
console.log('Deployment:', deploymentDetails);
console.log('Runtime:', deploymentDetails.runtime);
console.log('Version:', deploymentDetails.version);Retrieve build and deployment logs:
// Get all deployment step outputs
let outputs = await client.functionDeployment.getOutput({
instanceId: instance.id,
functionId: func.id,
functionDeploymentId: deployment.id,
});
for (let output of outputs) {
console.log(`Step: ${output.name}`);
console.log(`Status: ${output.status}`);
console.log(`Type: ${output.type}`);
console.log('Logs:');
for (let log of output.logs) {
console.log(` [${new Date(log.timestamp)}] ${log.message}`);
}
console.log('---');
}List and manage deployed function versions:
// List function versions
let versions = await client.functionVersion.list({
instanceId: instance.id,
functionId: func.id,
limit: 10,
order: 'desc',
});
for (let version of versions.items) {
console.log('Version:', version.name);
console.log('Is Current:', version.isCurrent);
console.log('Runtime:', version.runtime.specification);
console.log('Configuration:', version.configuration);
}
// Get a specific version
let version = await client.functionVersion.get({
instanceId: instance.id,
functionId: func.id,
functionVersionId: versions.items[0].id,
});Execute deployed functions with custom payloads:
// Invoke a function
let invocation = await client.function.invoke({
instanceId: instance.id,
functionId: func.id,
payload: {
action: 'process',
data: { userId: '123', operation: 'update' },
},
});
if (invocation.type === 'success') {
console.log('Result:', invocation.result);
} else {
console.error('Error:', invocation.error);
}View invocation logs and metrics:
// List function invocations
let invocations = await client.functionInvocation.list({
instanceId: instance.id,
functionId: func.id,
limit: 20,
order: 'desc',
});
for (let inv of invocations.items) {
console.log('Invocation ID:', inv.id);
console.log('Status:', inv.status);
console.log('Compute Time:', inv.computeTimeMs, 'ms');
console.log('Billed Time:', inv.billedTimeMs, 'ms');
console.log('Logs:', inv.logs);
if (inv.error) {
console.log('Error:', inv.error);
}
}
// Get detailed invocation information
let invocationDetails = await client.functionInvocation.get({
instanceId: instance.id,
functionId: func.id,
functionInvocationId: invocations.items[0].id,
});
console.log('Full invocation details:', invocationDetails);
console.log('Function Version:', invocationDetails.functionVersionId);// Get the default provider configuration
let provider = await client.provider.getDefault();
console.log('Provider:', provider.identifier);
console.log('Provider ID:', provider.id);This project is licensed under the Apache License 2.0.