# Function Integration

Access authenticated user context in your functions.

Access authenticated user context in your functions.

## Receiving User Context

When a function is invoked with an auth user token, the user context is injected into the event:

```javascript
exports.handler = async (event) => {
  // Check if user is authenticated
  if (!event.__volcano_auth) {
    return {
      statusCode: 401,
      body: JSON.stringify({ error: 'Unauthorized' }),
    };
  }

  // Access user information
  const { user_id, email, project_id, role } = event.__volcano_auth;

  console.log('User ID:', user_id);
  console.log('Email:', email);
  console.log('Project:', project_id);
  console.log('Role:', role); // "authenticated" or "anonymous"
};
```

## User Context Fields

```javascript
event.__volcano_auth = {
  user_id: 'uuid', // Auth user's unique ID
  email: 'user@example.com', // User's email
  project_id: 'uuid', // Project the user belongs to
  role: 'authenticated', // "authenticated" or "anonymous"
  access_token: 'eyJhbGci...', // The caller's access token
};
```

**Role values:**

- `"authenticated"` - User signed up with email/password or OAuth
- `"anonymous"` - Guest user (created via `signUpAnonymous()`)

**Note:** Both user types have a `user_id`. When an anonymous user converts to authenticated, their `user_id` remains the same - preserving all their data.

## With service key

If invoked with a service key (not auth user token), `__volcano_auth` is `undefined`:

```javascript
if (!event.__volcano_auth) {
  // Invoked with service key (service-to-service)
  // Or no authentication
}
```

## Handling Different User Types

```javascript
exports.handler = async (event) => {
  const auth = event.__volcano_auth;

  if (!auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  // Handle based on user type
  if (auth.role === 'anonymous') {
    // Guest user - maybe limit features
    return {
      statusCode: 200,
      body: JSON.stringify({
        data: limitedData,
        suggestion: 'Create an account to unlock all features',
      }),
    };
  }

  // Full authenticated user
  return {
    statusCode: 200,
    body: JSON.stringify({ data: fullData }),
  };
};
```

## Database Integration

`DATABASE_URL` defaults to `application_name=volcano_full_access` (admin, bypasses RLS). Rewrite
`application_name` to `volcano_user_access:{user_id}` before connecting, and pgproxy sets the
session variables Row-Level Security policies read automatically:

Save the [bounded per-user pool helper](https://github.com/Kong/volcano-docs/blob/main/content/platform/examples/database-pool-example/user-db-pool.js) as `user-db-pool.js` alongside your function. It retains at most four user pools with one connection each, reclaims idle pools for new users, removes empty pools after failed startup, and preserves active work. Handle its capacity error with backpressure and size all instances within the [shared connection limits](https://docs.staging.volcano.dev/platform/databases/direct-connection.md#keep-connection-pools-within-capacity).

```javascript
const { connectForUser } = require('./user-db-pool');

exports.handler = async (event) => {
  if (!event.__volcano_auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  const { user_id } = event.__volcano_auth;
  let client;
  try {
    client = await connectForUser(user_id);
  } catch (error) {
    // Log diagnostic codes, never connection strings or error messages.
    console.error('Database connection failed', { name: error?.name, code: error?.code ?? 'UNKNOWN' });
    return {
      statusCode: 503,
      headers: { 'Retry-After': '1' },
      body: JSON.stringify({ error: 'Database temporarily unavailable' }),
    };
  }

  try {
    // RLS policies work automatically — works for both authenticated and anonymous users
    const result = await client.query('SELECT * FROM posts');

    return {
      statusCode: 200,
      body: JSON.stringify(result.rows),
    };
  } finally {
    client.release();
  }
};
```

## Complete Example

```javascript
const { connectForUser } = require('./user-db-pool');

exports.handler = async (event) => {
  // 1. Verify authentication
  if (!event.__volcano_auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  const { user_id } = event.__volcano_auth;

  // 2. Get a connection from this user's pool — RLS is already in effect
  let client;
  try {
    client = await connectForUser(user_id);
  } catch (error) {
    // Log diagnostic codes, never connection strings or error messages.
    console.error('Database connection failed', { name: error?.name, code: error?.code ?? 'UNKNOWN' });
    return {
      statusCode: 503,
      headers: { 'Retry-After': '1' },
      body: JSON.stringify({ error: 'Database temporarily unavailable' }),
    };
  }

  try {
    // 3. Handle different actions
    const { action } = event;

    switch (action) {
      case 'create_post':
        await client.query(
          'INSERT INTO posts (title, content) VALUES ($1, $2)',
          [event.title, event.content],
          // user_id automatically set by trigger
        );
        return { statusCode: 201, body: 'Post created' };

      case 'get_posts':
        // RLS automatically filters to user's posts
        // Works for both anonymous and authenticated users
        const result = await client.query('SELECT * FROM posts');
        return {
          statusCode: 200,
          body: JSON.stringify(result.rows),
        };

      default:
        return { statusCode: 400, body: 'Unknown action' };
    }
  } finally {
    client.release();
  }
};
```

## Environment Variables

Volcano doesn't set `DATABASE_URL` for you. Set it yourself as a project variable, from the connection string in [Connection strings](https://docs.staging.volcano.dev/platform/databases/connection-strings.md#using-in-functions):

```javascript
process.env.DATABASE_URL; // The connection string you set as a project variable
```

## See Also

- [Row-Level Security](https://docs.staging.volcano.dev/platform/databases/row-level-security.md) - Secure data at database level
- [Auth Helpers](https://docs.staging.volcano.dev/platform/databases/auth-helpers.md) - Using auth.uid(), auth.email()
- [Examples](https://docs.staging.volcano.dev/platform/examples/function-sdk-example/README.md) - Complete working examples
