Docs/Functions

Edge Functions

Deploy serverless functions globally with zero cold starts and automatic scaling.

Overview

Edge Functions run your code on the edge, close to your users. They support TypeScript and JavaScript with full npm package support.

<50ms Cold Start

Functions stay warm at the edge

Auto Scaling

Scale to millions of requests

TypeScript Native

Full TypeScript + npm support

Creating Functions

Function Structure

Functions are organized in the functions/ directory:

my-project/
├── functions/
│   ├── hello-world/
│   │   └── index.ts
│   ├── api/
│   │   ├── users/
│   │   │   └── index.ts
│   │   └── orders/
│   │       └── index.ts
│   └── webhooks/
│       └── stripe/
│           └── index.ts
└── lubes.json

Basic Function

functions/hello-world/index.ts
import { serve } from '@lubes/functions'

serve(async (req) => {
  const { name } = await req.json()

  return Response.json({
    message: `Hello, ${name || 'World'}!`,
    timestamp: new Date().toISOString(),
  })
})

Function with Database Access

functions/api/users/index.ts
import { serve } from '@lubes/functions'
import { db } from '@lubes/db'

serve(async (req) => {
  const url = new URL(req.url)
  const userId = url.searchParams.get('id')

  if (!userId) {
    return Response.json(
      { error: 'User ID required' },
      { status: 400 }
    )
  }

  const user = await db.queryOne`
    SELECT id, name, email, created_at
    FROM users
    WHERE id = ${userId}
  `

  if (!user) {
    return Response.json(
      { error: 'User not found' },
      { status: 404 }
    )
  }

  return Response.json({ user })
})

HTTP Methods

import { serve } from '@lubes/functions'
import { db } from '@lubes/db'

serve(async (req) => {
  const { method } = req

  switch (method) {
    case 'GET':
      const users = await db.query`SELECT * FROM users`
      return Response.json({ users })

    case 'POST':
      const body = await req.json()
      const newUser = await db.queryOne`
        INSERT INTO users (name, email)
        VALUES (${body.name}, ${body.email})
        RETURNING *
      `
      return Response.json({ user: newUser }, { status: 201 })

    case 'DELETE':
      const url = new URL(req.url)
      const id = url.searchParams.get('id')
      await db.query`DELETE FROM users WHERE id = ${id}`
      return Response.json({ success: true })

    default:
      return Response.json(
        { error: 'Method not allowed' },
        { status: 405 }
      )
  }
})

Deploying Functions

Deploy a Single Function

# Deploy a specific function
lubes functions deploy hello-world

# Output:
# Deploying function 'hello-world'...
# Building... done
# Uploading... done
# Function deployed successfully!
# URL: https://abc123.functions.lubes.dev/hello-world

Deploy All Functions

# Deploy all functions in the project
lubes functions deploy --all

# Deploy with verbose output
lubes functions deploy --all --verbose

Manage Functions

# List all deployed functions
lubes functions list

# Get function details
lubes functions info hello-world

# Delete a function
lubes functions delete hello-world

# View function versions
lubes functions versions hello-world

# Rollback to previous version
lubes functions rollback hello-world --version 3

Environment Variables

Securely store API keys, secrets, and configuration using environment variables. Variables are encrypted at rest and injected at runtime.

Setting Variables

# Set a single variable
lubes env set STRIPE_SECRET_KEY sk_live_xxx

# Set multiple variables from a file
lubes env set --file .env.production

# Set variables for a specific environment
lubes env set --env staging API_URL https://staging-api.example.com

Using Variables in Functions

import { serve } from '@lubes/functions'

serve(async (req) => {
  // Access environment variables
  const stripeKey = Deno.env.get('STRIPE_SECRET_KEY')
  const apiUrl = Deno.env.get('API_URL')

  // Use in your function logic
  const response = await fetch(`${apiUrl}/webhook`, {
    headers: {
      'Authorization': `Bearer ${stripeKey}`
    }
  })

  return Response.json({ success: true })
})

Managing Variables

# List all variables
lubes env list

# Get a specific variable (value hidden)
lubes env get STRIPE_SECRET_KEY

# Remove a variable
lubes env remove OLD_API_KEY

# Copy variables between environments
lubes env copy --from production --to staging

Logs & Debugging

Viewing Logs

# Stream logs in real-time
lubes functions logs hello-world --follow

# View last 100 log lines
lubes functions logs hello-world --lines 100

# Filter by time range
lubes functions logs hello-world --since 1h

# Filter by log level
lubes functions logs hello-world --level error

Logging in Functions

import { serve } from '@lubes/functions'

serve(async (req) => {
  // Standard console methods work
  console.log('Request received:', req.method, req.url)
  console.info('Processing request...')
  console.warn('Deprecated field used')
  console.error('Something went wrong:', error)

  // Structured logging
  console.log(JSON.stringify({
    event: 'user_action',
    userId: '123',
    action: 'login',
    metadata: { ip: req.headers.get('x-forwarded-for') }
  }))

  return Response.json({ success: true })
})

Local Development

# Start local development server
lubes functions serve

# Serve a specific function
lubes functions serve hello-world

# Serve with custom port
lubes functions serve --port 3001

# Serve with environment file
lubes functions serve --env-file .env.local

Advanced Topics

CORS Configuration

import { serve, cors } from '@lubes/functions'

serve(async (req) => {
  // Handle CORS preflight
  if (req.method === 'OPTIONS') {
    return cors(req, new Response(null, { status: 204 }))
  }

  const response = Response.json({ data: 'hello' })
  return cors(req, response, {
    origin: ['https://example.com', 'https://app.example.com'],
    methods: ['GET', 'POST'],
    headers: ['Content-Type', 'Authorization'],
  })
})

Authentication

import { serve } from '@lubes/functions'
import { verifyJwt } from '@lubes/auth'

serve(async (req) => {
  // Get token from header
  const token = req.headers.get('Authorization')?.replace('Bearer ', '')

  if (!token) {
    return Response.json(
      { error: 'Authentication required' },
      { status: 401 }
    )
  }

  // Verify JWT
  const { user, error } = await verifyJwt(token)

  if (error) {
    return Response.json(
      { error: 'Invalid token' },
      { status: 401 }
    )
  }

  // User is authenticated
  return Response.json({
    message: `Hello, ${user.email}!`,
    userId: user.id
  })
})

Next Steps