---
title: "Protect backend functions"
description: "Declare each Convex function at the access tier it needs: signed in, a workspace member, an administrator, or a role of your own."
canonical_url: "https://nuxt-backend.dev/docs/auth/access/functions"
---
# Protect backend functions

> Declare each Convex function at the access tier it needs: signed in, a workspace member, an administrator, or a role of your own.

Every Convex function you export is callable by anyone who can reach the deployment. The scaffolded `backend/functions.ts` gives you builders that check the caller before your handler runs, so a function's access tier is part of how it is declared.

## The builders

`createFunctions({ query, mutation, action }, authorization, options?)` returns drop-in replacements for your `_generated/server` builders, grouped by tier:

```ts [backend/projects.ts]
import { v } from 'convex/values'
import { admin, authed, org, withRole } from './functions'

export const me = authed.query({            // ctx.user — signed in, not banned
  args: {},
  handler: async ctx => ctx.user,
})

export const createProject = org.mutation({  // + ctx.organization { id, role } — fresh membership check
  args: { name: v.string() },
  handler: async (ctx, { name }) =>
    ctx.db.insert('projects', { name, organizationId: ctx.organization.id }),
})

export const purge = admin.action({          // requireRole(adminRoles) — default ['admin']
  args: {},
  handler: async (ctx) => { /* … */ },
})

export const review = withRole('editor').query({ // custom app-wide role tier
  args: {},
  handler: async (ctx) => { /* … */ },
})
```

<table>
<thead>
  <tr>
    <th>
      Tier
    </th>
    
    <th>
      Check
    </th>
    
    <th>
      Injects
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        authed
      </code>
    </td>
    
    <td>
      <code>
        requireUser
      </code>
      
       (claims)
    </td>
    
    <td>
      <code>
        ctx.user
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        org
      </code>
    </td>
    
    <td>
      <code>
        requireMember
      </code>
      
       — fresh member-table read on <strong>
        every
      </strong>
      
       call, so a removed member is locked out immediately, not at JWT expiry
    </td>
    
    <td>
      <code>
        ctx.user
      </code>
      
      , <code>
        ctx.organization
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        admin
      </code>
    </td>
    
    <td>
      <code>
        requireRole(adminRoles)
      </code>
      
       (claims)
    </td>
    
    <td>
      <code>
        ctx.user
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        withRole(role)
      </code>
    </td>
    
    <td>
      <code>
        requireRole(role)
      </code>
      
       (claims)
    </td>
    
    <td>
      <code>
        ctx.user
      </code>
    </td>
  </tr>
</tbody>
</table>

Options: `adminRoles` (default `['admin']`), and per-tier write throttling — pass your `setupRateLimiter(...)` as `rateLimiter` and name a limit per tier in `limits` (`{ authed: 'apiWrites' }`); it is consumed on **mutations and actions only**, keyed by user id. Pass your `withTriggers`-wrapped `mutation` to keep [aggregate](https://nuxt-backend.dev/docs/data/aggregates) triggers composing.

## Which one to reach for

- Data a person owns: `authed`, and read `ctx.user.id`.
- Data a workspace owns: `org`, and scope every read and write by `ctx.organization.id`. Its membership check is a fresh read, so removing someone takes effect on their next call.
- Operations on other people's accounts, or anything that moves money or sends mail to typed addresses: `admin`.
- A role of your own across the app: `withRole('editor')`.
- A check the tiers do not express, such as a permission or a workspace role: call the helpers `setupAuthorization` returns inside the handler (`requirePermission`, `requireMember(ctx, { role })`). [Authorization](https://nuxt-backend.dev/docs/auth/access/roles) lists them.

A function nobody outside the deployment should call is an `internalQuery`, `internalMutation` or `internalAction` instead: the scaffold ships its email-sending and discount-minting functions that way.


## Sitemap

See the full [sitemap](https://nuxt-backend.dev/sitemap.md) for all pages.
