> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiveku.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Serverless Hosting Guide

> Everything you need to know about deploying on Hiveku's serverless infrastructure — Lambda limits, API handlers, hiveku.json config, and code patterns

Complete reference for building and deploying applications on Hiveku's serverless infrastructure.

<Info>
  **Platform:** AWS Lambda + CloudFront + S3 | **Runtime:** Node.js 20.x on ARM64 | **Not supported:** PHP, WebSockets, native modules without layers, persistent processes
</Info>

## Architecture

* Static assets are built and uploaded to **S3**, served via **CloudFront CDN** (global edge locations)
* Automatic **HTTPS** with wildcard certificate
* URL rewriting for clean routes (`/about` → `/about/index.html`)
* Cache: HTML 1 min, CSS/JS 5 min
* Each API route becomes a **Lambda function** with its own URL
* SSR pages are bundled with the server runtime

## Resource Limits

| Resource                 | Default    | Maximum              | Notes                            |
| ------------------------ | ---------- | -------------------- | -------------------------------- |
| Memory                   | 256 MB     | 10,240 MB            | Higher memory = faster CPU       |
| Timeout                  | 30 seconds | 900 seconds (15 min) | SSR uses 60s default             |
| Bundle Size (zipped)     | -          | 50 MB                | Larger uses S3 upload            |
| Bundle Size (unzipped)   | -          | 250 MB               | AWS Lambda hard limit            |
| Request Payload          | -          | 6 MB                 | Use S3 presigned URLs for larger |
| Response Payload         | -          | 6 MB                 | Stream large responses           |
| Ephemeral Storage (/tmp) | 512 MB     | 10,240 MB            | Cleared between invocations      |
| Environment Variables    | -          | 4 KB total           | Use Secrets Manager for large    |
| Concurrent Executions    | 1,000      | Depends on quota     | Per-account AWS limit            |
| ZIP Import (upload)      | -          | 1 GB                 | Max 10,000 files                 |

## Do's and Don'ts

<Tabs>
  <Tab title="Do">
    * **Use pure JavaScript packages** — `bcryptjs`, `pg` (pure mode), `jose` for JWT
    * **Keep functions small** — split large APIs into separate files for faster cold starts
    * **Use environment variables** — store secrets in Hiveku's env vars, not in code
    * **Optimize bundle size** — use dynamic imports, tree-shaking, avoid bundling dev deps
    * **Use S3 for large files** — generate presigned URLs for uploads over 6MB
    * **Return proper status codes** — use 200, 201, 400, 401, 404, 500 appropriately
    * **Set cache headers** — use Cache-Control for static responses
    * **Handle cold starts** — initialize DB connections outside the handler
  </Tab>

  <Tab title="Don't">
    * **Manually bundle native modules** — `bcrypt`, `sharp`, `canvas` won't work without layers
    * **Rely on filesystem persistence** — Lambda `/tmp` is ephemeral (cleared between invocations)
    * **Use setTimeout for cron jobs** — functions terminate after response, timers don't persist
    * **Run long background jobs** — max timeout is 15 minutes, no background threads
    * **Bundle large files in functions** — ML models, large datasets increase cold start
    * **Hardcode credentials** — secrets in code are exposed in logs and bundles
  </Tab>
</Tabs>

## API Routes

### Where to Place API Files

Hiveku auto-detects API routes from these directories:

```
# Standard locations
api/
src/api/
functions/
lambdas/
serverless/

# Next.js App Router
app/api/**/route.ts

# Next.js Pages Router
pages/api/**/*.ts
```

### File to URL Mapping

| File Path                | Deployed URL               |
| ------------------------ | -------------------------- |
| `api/users.ts`           | `/api/users`               |
| `api/users/[id].ts`      | `/api/users/:id`           |
| `api/files/[...path].ts` | `/api/files/*` (catch-all) |
| `app/api/auth/route.ts`  | `/api/auth`                |

### API Handler Formats

Hiveku supports two function formats — use whichever you prefer:

<Tabs>
  <Tab title="Web API Format (Recommended)">
    Modern format, similar to Next.js/Vercel Edge Functions:

    ```typescript theme={null}
    // api/users.ts
    export default async function handler(req: Request) {
      // req.method - GET, POST, PUT, DELETE, PATCH
      // req.url - Full URL with query params
      // req.headers - Headers object (lowercase keys)
      // req.body - Parsed JSON body
      // req.query - Query parameters object
      // req.params - URL path parameters (e.g., [id])
      
      if (req.method === 'GET') {
        return Response.json({ users: [] });
      }
      
      if (req.method === 'POST') {
        const body = await req.json();
        return Response.json({ created: body }, { status: 201 });
      }
      
      return Response.json({ error: 'Method not allowed' }, { status: 405 });
    }
    ```
  </Tab>

  <Tab title="AWS Lambda Format">
    Traditional format, compatible with Netlify Functions, Vercel (legacy), AWS SAM:

    ```typescript theme={null}
    // api/users.ts
    export async function handler(event: any) {
      const headers = {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
      };

      if (event.httpMethod === 'OPTIONS') {
        return { statusCode: 200, headers, body: '' };
      }

      if (event.httpMethod === 'GET') {
        return {
          statusCode: 200,
          headers,
          body: JSON.stringify({ users: [] })
        };
      }

      return {
        statusCode: 405,
        headers,
        body: JSON.stringify({ error: 'Method not allowed' })
      };
    }
    ```
  </Tab>
</Tabs>

| Feature      | Web API Format            | AWS Lambda Format                 |
| ------------ | ------------------------- | --------------------------------- |
| Export       | `export default function` | `export async function handler`   |
| HTTP Method  | `req.method`              | `event.httpMethod`                |
| Query Params | `req.query.id`            | `event.queryStringParameters?.id` |
| Request Body | `req.body` (auto-parsed)  | `JSON.parse(event.body)`          |
| Response     | `Response.json(data)`     | `{ statusCode, body, headers }`   |

## hiveku.json Configuration

Create a `hiveku.json` in your project root to customize deployment:

```json theme={null}
{
  "functions": {
    "directories": ["api/", "src/lambdas/"],
    "include": ["lib/scheduled-job.ts"],
    "exclude": ["api/internal/**", "**/*.test.ts"],
    "routes": {
      "api/legacy-auth.ts": "/api/v1/auth",
      "api/new-auth.ts": "/api/v2/auth"
    },
    "config": {
      "api/image-processor.ts": {
        "memory": 1024,
        "timeout": 120
      },
      "api/heavy-compute.ts": {
        "memory": 2048,
        "timeout": 300
      },
      "**": {
        "memory": 256,
        "timeout": 30
      }
    }
  },
  "build": {
    "outputDirectory": "dist",
    "env": {
      "NEXT_PUBLIC_API_URL": "https://api.example.com"
    }
  }
}
```

| Field                   | Description                                                 |
| ----------------------- | ----------------------------------------------------------- |
| `functions.directories` | Additional directories to scan for API routes               |
| `functions.include`     | Specific files to include as functions                      |
| `functions.exclude`     | Glob patterns to exclude                                    |
| `functions.routes`      | Override auto-generated URL paths                           |
| `functions.config`      | Per-function memory/timeout settings. Use `**` for defaults |
| `build.outputDirectory` | Custom build output directory                               |
| `build.env`             | Build-time environment variables (`NEXT_PUBLIC_*`)          |

## Advanced Features

### Lambda Layers

Auto-provisioned layers for native dependencies. These packages just work when you add the layer:

`sharp` `bcrypt` `argon2` `canvas` `prisma` `ffmpeg` `puppeteer`

### Scheduled Functions (Cron)

Add a `@schedule` comment or put files in a `cron/` directory:

```typescript theme={null}
// api/cron/daily-report.ts
// @schedule cron(0 9 * * ? *)

export default async function() {
  const report = await generateReport();
  await sendEmail(report);
  return { success: true };
}
```

**Cron format:** `cron(min hour day-of-month month day-of-week year)`

| Expression                | Description   |
| ------------------------- | ------------- |
| `cron(0 9 * * ? *)`       | 9 AM daily    |
| `cron(0 */2 * * ? *)`     | Every 2 hours |
| `cron(0 9 ? * MON-FRI *)` | 9 AM weekdays |
| `cron(0 0 1 * ? *)`       | 1st of month  |

**Rate format:** `rate(5 minutes)`, `rate(1 hour)`, `rate(1 day)`, `rate(7 days)`

### Streaming Responses

SSE and chunked responses for AI apps. Auto-detected from `text/event-stream` content type:

```typescript theme={null}
// api/stream/ai-chat.ts
// @streaming

export default async function(req: Request) {
  const { prompt } = await req.json();
  
  return new Response(
    new ReadableStream({
      async start(controller) {
        for await (const chunk of streamAI(prompt)) {
          controller.enqueue(`data: ${JSON.stringify(chunk)}\n\n`);
        }
        controller.close();
      }
    }),
    { headers: { 'Content-Type': 'text/event-stream' } }
  );
}
```

## Package Compatibility

<Tabs>
  <Tab title="Works">
    | Package                 | Use For                                                  |
    | ----------------------- | -------------------------------------------------------- |
    | `prisma`                | ORM (use connection pooling + keep-alive for serverless) |
    | `pg` / `mysql2`         | Database drivers (pure JS mode)                          |
    | `bcryptjs`              | Password hashing (pure JS)                               |
    | `jose`                  | JWT signing/verification                                 |
    | `zod`                   | Schema validation                                        |
    | `axios` / `node-fetch`  | HTTP clients                                             |
    | `@aws-sdk/*`            | AWS services (S3, SES, etc.)                             |
    | `stripe`                | Payment processing                                       |
    | `resend` / `nodemailer` | Email sending                                            |
    | `openai`                | AI/LLM integration                                       |
  </Tab>

  <Tab title="Needs Replacement">
    | Package        | Replace With                 |
    | -------------- | ---------------------------- |
    | `bcrypt`       | `bcryptjs`                   |
    | `jsonwebtoken` | `jose` (pure JS)             |
    | `sharp`        | `jimp` or Lambda Layer       |
    | `canvas`       | Lambda Layer                 |
    | `sqlite3`      | Supabase, PlanetScale, Turso |
    | `pg-native`    | `pg` (pure JS mode)          |
    | `puppeteer`    | `@sparticuz/chromium` Layer  |
    | `grpc`         | `connect-web` (gRPC-Web)     |
  </Tab>

  <Tab title="Next.js Only">
    These packages only work in Next.js and need alternatives in Vite/React:

    | Package       | Alternative                                        |
    | ------------- | -------------------------------------------------- |
    | `next-themes` | CSS `prefers-color-scheme` or custom ThemeProvider |
    | `next/image`  | Standard `<img>` tags or a Vite image plugin       |
    | `next/font`   | `@fontsource` packages or Google Fonts CDN         |
    | `next/link`   | `react-router-dom` Link                            |
  </Tab>
</Tabs>

## Code Patterns

### Database Connection (Prisma)

Initialize the Prisma client outside the handler so connections are reused across Lambda invocations. Use connection pooling and keep-alive pings to avoid cold reconnect latency.

```typescript theme={null}
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
  connectionWarmed: boolean;
  keepAliveInterval: NodeJS.Timeout | undefined;
};

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
    datasources: {
      db: { url: process.env.DATABASE_URL },
    },
  });

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

// Pre-warm the connection on cold start (reduces first query latency)
if (!globalForPrisma.connectionWarmed) {
  globalForPrisma.connectionWarmed = true;
  prisma.$queryRaw`SELECT 1`.catch(() => {});
}

// Keep-alive: ping every 20s to prevent connection pool from closing
// Supabase session pooler closes idle connections after ~30 seconds
if (process.env.NODE_ENV === 'production' && !globalForPrisma.keepAliveInterval) {
  globalForPrisma.keepAliveInterval = setInterval(async () => {
    try { await prisma.$queryRaw`SELECT 1`; } catch {}
  }, 20000);
  globalForPrisma.keepAliveInterval.unref?.();
}

export default prisma;
```

<Tip>
  Set `connection_limit` and `pool_timeout` in your `DATABASE_URL` query string. A limit of 10-15 connections with a 30s timeout works well for serverless.
</Tip>

### JWT Authentication

<Warning>
  Use the `jose` library for JWT -- it's pure JavaScript and works on Lambda. Avoid `jsonwebtoken` as it has native dependencies.
</Warning>

```typescript theme={null}
// lib/auth.ts
import { SignJWT, jwtVerify } from 'jose';

const secret = new TextEncoder().encode(process.env.JWT_SECRET);

export async function signToken(payload: { userId: string; email: string }) {
  return await new SignJWT(payload)
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('7d')
    .sign(secret);
}

export async function verifyToken(token: string) {
  try {
    const { payload } = await jwtVerify(token, secret);
    return payload as { userId: string; email: string };
  } catch {
    return null;
  }
}
```

```typescript theme={null}
// api/protected.ts - Protected route example
import { verifyToken } from '../lib/auth';

export default async function handler(req: Request) {
  // Headers are lowercase on Lambda!
  const authHeader = req.headers.get('authorization');
  if (!authHeader?.startsWith('Bearer ')) {
    return Response.json({ error: 'Missing token' }, { status: 401 });
  }
  
  const payload = await verifyToken(authHeader.slice(7));
  if (!payload) {
    return Response.json({ error: 'Invalid token' }, { status: 401 });
  }
  
  return Response.json({ message: 'Authenticated!', user: payload });
}
```

### Large File Upload (S3 Presigned URL)

```typescript theme={null}
// api/upload-url.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const s3 = new S3Client({ region: process.env.AWS_REGION });

export default async function handler(req: Request) {
  const { filename, contentType } = await req.json();
  
  const command = new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: `uploads/${Date.now()}-${filename}`,
    ContentType: contentType,
  });
  
  const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
  return Response.json({ uploadUrl });
}
```

### Custom CORS Headers

```typescript theme={null}
// api/data.ts
export default async function handler(req: Request) {
  const headers = {
    'Access-Control-Allow-Origin': 'https://myapp.com',
    'Access-Control-Allow-Methods': 'GET, POST',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization',
  };
  
  if (req.method === 'OPTIONS') {
    return new Response(null, { status: 204, headers });
  }
  
  return Response.json({ data: 'hello' }, { headers });
}
```

### Reusable Auth Middleware

```typescript theme={null}
// lib/withAuth.ts
import { verifyToken } from './auth';

type AuthenticatedHandler = (
  req: Request,
  user: { userId: string; email: string }
) => Promise<Response>;

export function withAuth(handler: AuthenticatedHandler) {
  return async (req: Request): Promise<Response> => {
    const authHeader = req.headers.get('authorization');
    if (!authHeader?.startsWith('Bearer ')) {
      return Response.json({ error: 'Missing Authorization header' }, { status: 401 });
    }
    
    const user = await verifyToken(authHeader.slice(7));
    if (!user) {
      return Response.json({ error: 'Invalid or expired token' }, { status: 401 });
    }
    
    return handler(req, user);
  };
}

// api/profile.ts - Using the middleware
import { withAuth } from '../lib/withAuth';

export default withAuth(async (req, user) => {
  const profile = await prisma.user.findUnique({
    where: { id: user.userId },
    select: { id: true, email: true, name: true }
  });
  return Response.json(profile);
});
```

## Environment Variables

### Auto-Injected Variables

These are available in every Lambda function automatically:

```
NODE_ENV=production
AWS_REGION=us-east-1
HIVEKU_PROJECT_ID=your-project-id
DATABASE_URL=... (if provisioned)
```

### Build-Time vs Runtime

* `NEXT_PUBLIC_*` variables are bundled at build time and exposed to the browser
* All other variables are available only at runtime, server-side only

<Warning>
  `NEXT_PUBLIC_*` variables require a redeploy to take effect after changes.
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Function timeout (30s limit)">
    Check if you're hitting the 30s default timeout. Increase in `hiveku.json`, or optimize your code. Database queries without connection pooling are a common cause.
  </Accordion>

  <Accordion title="Bundle too large">
    Use dynamic imports, check for accidentally bundled devDependencies, exclude large assets. Consider splitting into multiple functions.
  </Accordion>

  <Accordion title="Native module error">
    You're using a package with native bindings (`bcrypt`, `sharp`, etc.). Switch to a pure JS alternative or request a Lambda Layer.
  </Accordion>

  <Accordion title="CORS errors">
    Hiveku adds CORS headers automatically. If you still see errors, check your frontend is calling the correct URL and your API returns proper response format.
  </Accordion>

  <Accordion title="Environment variable undefined">
    Make sure the variable is set in Hiveku dashboard. `NEXT_PUBLIC_*` vars need a redeploy to take effect. Server-side vars are only available in Lambda, not the browser.
  </Accordion>

  <Accordion title="Slow cold starts">
    Reduce bundle size, increase memory (more memory = faster CPU), minimize top-level imports. Consider keeping functions warm with scheduled pings.
  </Accordion>

  <Accordion title="Too many database connections">
    Lambda can scale to many instances. Use a connection pooler (Supabase, PlanetScale) or set your pool size to 1-2 connections per function.
  </Accordion>

  <Accordion title="Files disappear from /tmp">
    Lambda `/tmp` is ephemeral. Files may persist between invocations on the same instance but will be cleared. Use S3 for persistent storage.
  </Accordion>
</AccordionGroup>
