Serverless Functions — Browser IDE, Deploy & Test

Create serverless functions directly in the browser using a full-featured Monaco Editor. Test with custom payloads, publish when ready, and connect to API routes or cron jobs.

// Stripe Webhook Handler

Paste this into the function editor. Attach the shared-auth layer for signature verification. Connect to a POST route in the API Gateway.

stripe-webhook.jstypescript
// Requires layer: shared-auth (provides crypto helpers)
const crypto = require('crypto');

export const handler = async (event) => {
  const STRIPE_SECRET = process.env.STRIPE_WEBHOOK_SECRET || 'whsec_test_...';
  const sig = event.headers['stripe-signature'];
  const body = typeof event.body === 'string' ? event.body : JSON.stringify(event.body);

  // Verify Stripe signature
  const timestamp = sig?.split(',').find(s => s.startsWith('t='))?.split('=')[1];
  const v1 = sig?.split(',').find(s => s.startsWith('v1='))?.split('=')[1];
  const expected = crypto
    .createHmac('sha256', STRIPE_SECRET)
    .update(timestamp + '.' + body)
    .digest('hex');

  if (v1 !== expected) {
    return { statusCode: 401, body: { error: 'Invalid signature' } };
  }

  const { type, data } = JSON.parse(body);

  switch (type) {
    case 'checkout.session.completed':
      return {
        statusCode: 200,
        body: {
          action: 'fulfill_order',
          customer: data.object.customer_email,
          amount: data.object.amount_total / 100,
          currency: data.object.currency
        }
      };

    case 'invoice.payment_failed':
      return {
        statusCode: 200,
        body: {
          action: 'notify_customer',
          customer: data.object.customer_email,
          invoice_id: data.object.id
        }
      };

    case 'customer.subscription.deleted':
      return {
        statusCode: 200,
        body: {
          action: 'cancel_access',
          subscription_id: data.object.id,
          customer: data.object.customer
        }
      };

    default:
      return { statusCode: 200, body: { received: true, type } };
  }
};

Test payload you can paste into the Test Panel:

test-payload.jsonjson
{
  "headers": {
    "stripe-signature": "t=1234567890,v1=test_sig"
  },
  "body": {
    "type": "checkout.session.completed",
    "data": {
      "object": {
        "customer_email": "alice@example.com",
        "amount_total": 4999,
        "currency": "usd"
      }
    }
  }
}

// CSV Parser with Validation

Upload a CSV parsing layer, then use this function to accept raw CSV and return clean JSON. Handles quoted fields, trims whitespace, validates required columns.

csv-parser.jstypescript
// No layer needed — pure JS implementation
export const handler = async (event) => {
  const { csv, requiredColumns = [], delimiter = ',' } = event.body;

  if (!csv || typeof csv !== 'string') {
    return { statusCode: 400, body: { error: 'Missing "csv" field in body' } };
  }

  const lines = csv.trim().split('\n');
  if (lines.length < 2) {
    return { statusCode: 400, body: { error: 'CSV must have a header row and at least one data row' } };
  }

  // Parse header
  const headers = lines[0].split(delimiter).map(h => h.trim().replace(/^"|"$/g, ''));

  // Validate required columns
  const missing = requiredColumns.filter(col => !headers.includes(col));
  if (missing.length > 0) {
    return { statusCode: 400, body: { error: 'Missing columns: ' + missing.join(', ') } };
  }

  // Parse rows
  const data = [];
  const errors = [];
  for (let i = 1; i < lines.length; i++) {
    if (!lines[i].trim()) continue;
    const values = lines[i].split(delimiter).map(v => v.trim().replace(/^"|"$/g, ''));
    if (values.length !== headers.length) {
      errors.push({ row: i + 1, message: 'Column count mismatch: expected ' + headers.length + ', got ' + values.length });
      continue;
    }
    const row = {};
    headers.forEach((h, idx) => { row[h] = values[idx]; });
    data.push(row);
  }

  return {
    statusCode: 200,
    body: {
      total: data.length,
      columns: headers,
      errors: errors.length > 0 ? errors : undefined,
      data
    }
  };
};

Test payload:

test-csv-payload.jsonjson
{
  "body": {
    "csv": "name,email,role\nAlice,alice@example.com,admin\nBob,bob@corp.io,developer\nCarol,carol@test.com,viewer",
    "requiredColumns": ["name", "email"],
    "delimiter": ","
  }
}

// API Proxy & Aggregator

Combine multiple external API calls into one response. Uses built-in fetch.

api-aggregator.jstypescript
export const handler = async (event) => {
  const userId = event.body?.userId || event.queryStringParameters?.userId;
  if (!userId) {
    return { statusCode: 400, body: { error: 'userId is required' } };
  }

  // Fetch from multiple APIs in parallel
  const [userRes, postsRes, todosRes] = await Promise.allSettled([
    fetch('https://jsonplaceholder.typicode.com/users/' + userId),
    fetch('https://jsonplaceholder.typicode.com/posts?userId=' + userId),
    fetch('https://jsonplaceholder.typicode.com/todos?userId=' + userId + '&_limit=5'),
  ]);

  const parse = async (res) => {
    if (res.status === 'fulfilled' && res.value.ok) return res.value.json();
    return null;
  };

  const user = await parse(userRes);
  const posts = await parse(postsRes);
  const todos = await parse(todosRes);

  if (!user) {
    return { statusCode: 404, body: { error: 'User not found' } };
  }

  return {
    statusCode: 200,
    body: {
      user: { id: user.id, name: user.name, email: user.email, company: user.company?.name },
      stats: { totalPosts: posts?.length || 0, totalTodos: todos?.length || 0 },
      recentPosts: (posts || []).slice(0, 3).map(p => ({ id: p.id, title: p.title })),
      pendingTodos: (todos || []).filter(t => !t.completed).map(t => ({ id: t.id, title: t.title })),
    }
  };
};

Test payload:

test-proxy-payload.jsonjson
{
  "body": { "userId": 1 }
}

// Using Layers in Functions

Attach a layer to your function, then require() it by the layer's module name. Layers are loaded into the runtime path before your function executes.

function-with-layers.jstypescript
// Requires layers: db-pool, http-utils
// Attach these in the function editor's Layers panel

const db = require('db-pool');         // -> from db-pool layer ZIP
const http = require('http-utils');    // -> from http-utils layer ZIP

export const handler = async (event) => {
  try {
    const { id } = event.body;
    if (!id) return http.error(400, 'Missing id');

    const result = await db.query('SELECT * FROM users WHERE id = $1', [id]);
    if (result.rows.length === 0) return http.notFound('User not found');

    return http.json(result.rows[0]);
  } catch (err) {
    return http.error(500, err.message);
  }
};
email-notification.jstypescript
// Requires layer: email-sender
const mailer = require('email-sender');

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

  if (!to || !subject) {
    return { statusCode: 400, body: { error: 'Missing "to" or "subject"' } };
  }

  const html = '<h1>Hello ' + (name || 'there') + '</h1>'
    + '<p>This email was sent from a moqapi serverless function.</p>'
    + '<p>Time: ' + new Date().toISOString() + '</p>';

  const result = await mailer.send({
    to,
    subject,
    html,
    from: process.env.SMTP_FROM || 'noreply@moqapi.dev'
  });

  return {
    statusCode: 200,
    body: { sent: true, messageId: result.messageId }
  };
};

// Draft → Publish Workflow

01

Write Draft Code

Edit in the Monaco IDE. Changes are saved as draft automatically.

02

Test with Payloads

Invoke your function in the Test Panel with the JSON payloads shown above.

03

Publish → Connect to API Route

Click Publish, then bind it to an API Gateway route (e.g. POST /webhooks/stripe).

// Invoke via curl

terminalbash
# Invoke a function directly
curl -X POST https://moqapi.dev/api/functions/<function-id>/invoke \
  -H "Content-Type: application/json" \
  -d '{"body": {"csv": "name,email\nAlice,alice@test.com"}}'

# Invoke via project URL (project ID + function name path)
curl -X POST https://moqapi.dev/api/invoke/{projectId}/fn/webhooks/stripe \
  -H "Content-Type: application/json" \
  -H "stripe-signature: t=123,v1=abc" \
  -d '{"type":"checkout.session.completed","data":{"object":{"customer_email":"a@b.com","amount_total":999,"currency":"usd"}}}'

// API Reference

GET/api/functionsList all functions
POST/api/functionsCreate function
PATCH/api/functions/:idUpdate function code
POST/api/functions/:id/invokeExecute function