> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-pt/guias-do-contribuidor/componentes-bem-conhecidos/controllers.md).

# Controladores

Controllers são responsáveis por lidar com requisições HTTP e WebSocket na sua aplicação. Eles servem como ponto de entrada para requisições externas e atuam como ponte entre a camada de transporte e sua lógica de negócio.

## Propósito

Controllers DEVEM realizar apenas estas tarefas específicas:

1. **Validar a entrada da camada de transporte** - Verificar autenticação, autorização e formato de entrada
2. **Chamar componentes de lógica** - Delegar a lógica de negócio para os componentes de lógica apropriados
3. **Transformar a entrada** - Converter dados da requisição em parâmetros para chamadas dos componentes de lógica
4. **Lidar com erros** - Capturar e responder adequadamente a erros originados pelos componentes de lógica

{% hint style="warning" %}
Controllers NÃO devem conter lógica de negócio. Todas as regras de negócio DEVEM ser implementadas em componentes de lógica.
{% endhint %}

## Localização

Todos os controllers DEVEM ser colocados sob o `/src/controllers` diretório e serem nomeados em referência à ação que executam.

```
src/
└── controllers/
    ├── users/
    │   ├── get-user.ts
    │   ├── create-user.ts
    │   └── update-user.ts
    ├── content/
    │   ├── publish-content.ts
    │   └── delete-content.ts
    └── health.ts
```

## Estrutura do Controller

### Manipulador HTTP Básico

```tsx
import { HandlerContextWithPath } from '@dcl/platform-server-commons'

export async function getUserHandler(
  context: HandlerContextWithPath<'userLogic' | 'logs', '/users/:id'>
) {
  const { 
    components: { userLogic, logs },
    params: { id },
    verification 
  } = context

  const logger = logs.getLogger('get-user-handler')

  try {
    // 1. Validar autenticação
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Autenticação requerida'
        }
      }
    }

    // 2. Validar entrada
    if (!id || typeof id !== 'string') {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: 'ID de usuário inválido'
        }
      }
    }

    // 3. Chamar componente de lógica
    const user = await userLogic.getUser(id)

    // 4. Retornar resposta de sucesso
    return {
      status: 200,
      body: {
        ok: true,
        data: user
      }
    }
  } catch (error) {
    // 5. Lidar com erros
    logger.error('Error fetching user', error)

    if (error instanceof UserNotFoundError) {
      return {
        status: 404,
        body: {
          error: 'Not Found',
          message: error.message
        }
      }
    }

    return {
      status: 500,
      body: {
        error: 'Internal Server Error',
        message: 'Ocorreu um erro inesperado'
      }
    }
  }
}
```

### Manipulador de Requisição POST

```tsx
export async function createUserHandler(
  context: HandlerContextWithPath<'userLogic' | 'logs', '/users'>
) {
  const {
    components: { userLogic, logs },
    request,
    verification
  } = context

  const logger = logs.getLogger('create-user-handler')

  try {
    // 1. Validar autenticação
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Autenticação requerida'
        }
      }
    }

    // 2. Fazer o parse e validar o corpo da requisição
    const body = await request.json()
    
    if (!body.username || !body.email) {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: 'Nome de usuário e email são obrigatórios'
        }
      }
    }

    // 3. Transformar entrada para o componente de lógica
    const userData = {
      username: body.username.toLowerCase(),
      email: body.email.toLowerCase(),
      display_name: body.display_name || body.username
    }

    // 4. Chamar componente de lógica
    const user = await userLogic.createUser(userData)

    // 5. Retornar resposta de sucesso
    return {
      status: 201,
      body: {
        ok: true,
        data: user
      }
    }
  } catch (error) {
    logger.error('Error creating user', error)

    if (error instanceof UserAlreadyExistsError) {
      return {
        status: 409,
        body: {
          error: 'Conflict',
          message: error.message
        }
      }
    }

    if (error instanceof InvalidEmailError) {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: error.message
        }
      }
    }

    return {
      status: 500,
      body: {
        error: 'Internal Server Error',
        message: 'Ocorreu um erro inesperado'
      }
    }
  }
}
```

### Manipulador de Parâmetros de Query

```tsx
export async function searchUsersHandler(
  context: HandlerContextWithPath<'userLogic' | 'logs', '/users/search'>
) {
  const {
    components: { userLogic, logs },
    url,
    verification
  } = context

  const logger = logs.getLogger('search-users-handler')

  try {
    // 1. Validar autenticação
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Autenticação requerida'
        }
      }
    }

    // 2. Extrair e validar parâmetros de query (lowercase!)
    const searchParams = new URLSearchParams(url.search)
    const query = searchParams.get('query')?.toLowerCase()
    const limit = parseInt(searchParams.get('limit') || '10', 10)
    const offset = parseInt(searchParams.get('offset') || '0', 10)

    if (!query) {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: 'Parâmetro query é obrigatório'
        }
      }
    }

    // 3. Chamar componente de lógica
    const results = await userLogic.searchUsers({
      query,
      limit,
      offset
    })

    // 4. Retornar resposta de sucesso
    return {
      status: 200,
      body: {
        ok: true,
        data: results
      }
    }
  } catch (error) {
    logger.error('Error searching users', error)

    return {
      status: 500,
      body: {
        error: 'Internal Server Error',
        message: 'Ocorreu um erro inesperado'
      }
    }
  }
}
```

## Requisitos de Entrada

Todas as chaves de entrada recebidas através dos controllers (corpo JSON, parâmetros de query, parâmetros de URL, headers, etc.) **DEVEM estar em lowercase** para evitar problemas de casing ao processar dados.

### Parâmetros com Múltiplas Palavras

Parâmetros com múltiplas palavras DEVEM ser definidos usando **snake\_case**.

{% hint style="danger" %}
**Nomeação incorreta:**

* `minPrice`
* `Car`
* `somethingURL`
* `userId`
  {% endhint %}

{% hint style="success" %}
**Nomeação correta:**

* `min_price`
* `car`
* `something_url`
* `user_id`
  {% endhint %}

### Exemplo

```tsx
// Corpo da requisição
{
  "user_name": "john_doe",        // ✅ Correto
  "email_address": "john@example.com",  // ✅ Correto
  "display_name": "John Doe",     // ✅ Correto
  "max_items": 100                // ✅ Correto
}

// Parâmetros de query
?search_query=test&max_results=50&sort_by=created_at  // ✅ Correto

// Parâmetros de URL
/users/:user_id/posts/:post_id   // ✅ Correto
```

## Tratamento de Erros

### Respostas de Erro Padrão

Controllers devem retornar formatos de resposta de erro consistentes:

```tsx
// 400 Bad Request
{
  "error": "Bad Request",
  "message": "Mensagem de erro detalhada",
  "details": {} // Detalhes adicionais opcionais
}

// 401 Unauthorized
{
  "error": "Unauthorized",
  "message": "Autenticação requerida"
}

// 403 Forbidden
{
  "error": "Forbidden",
  "message": "Você não tem permissão para acessar este recurso"
}

// 404 Not Found
{
  "error": "Not Found",
  "message": "Recurso não encontrado"
}

// 409 Conflict
{
  "error": "Conflict",
  "message": "Recurso já existe"
}

// 500 Internal Server Error
{
  "error": "Internal Server Error",
  "message": "Ocorreu um erro inesperado"
}
```

### Mapeamento de Erros

Mapear erros de domínio para códigos HTTP apropriados:

```tsx
function mapErrorToResponse(error: Error, logger: ILogger) {
  logger.error('Handler error', error)

  // Erros específicos de domínio
  if (error instanceof UserNotFoundError) {
    return { status: 404, body: { error: 'Not Found', message: error.message } }
  }
  
  if (error instanceof UserAlreadyExistsError) {
    return { status: 409, body: { error: 'Conflict', message: error.message } }
  }
  
  if (error instanceof UnauthorizedError) {
    return { status: 403, body: { error: 'Forbidden', message: error.message } }
  }
  
  if (error instanceof InvalidInputError) {
    return { status: 400, body: { error: 'Bad Request', message: error.message } }
  }

  // Erro genérico
  return {
    status: 500,
    body: {
      error: 'Internal Server Error',
      message: 'Ocorreu um erro inesperado'
    }
  }
}
```

## Autenticação e Autorização

### Verificação de Autenticação

```tsx
// Exigir autenticação
if (!verification?.auth) {
  return {
    status: 401,
    body: {
      error: 'Unauthorized',
      message: 'Autenticação requerida'
    }
  }
}

const userAddress = verification.auth.toLowerCase()
```

### Verificação de Autorização

```tsx
// Verificar permissões através do componente de lógica
const canAccess = await permissionsLogic.canUserAccessResource(
  userAddress,
  resourceId
)

if (!canAccess) {
  return {
    status: 403,
    body: {
      error: 'Forbidden',
      message: 'Você não tem permissão para acessar este recurso'
    }
  }
}
```

## Boas Práticas

### 1. Manter Controllers Enxutos

Controllers devem ser invólucros enxutos ao redor dos componentes de lógica:

```tsx
// ✅ Bom: Controller enxuto
export async function handler(context) {
  const { userLogic } = context.components
  const user = await userLogic.getUser(context.params.id)
  return { status: 200, body: { data: user } }
}

// ❌ Ruim: Lógica de negócio no controller
export async function handler(context) {
  const { database } = context.components
  const user = await database.query('SELECT * FROM users...')
  const processed = processUser(user) // Lógica de negócio!
  const validated = validateUser(processed) // Lógica de negócio!
  return { status: 200, body: { data: validated } }
}
```

### 2. Validar Cedo

Validar e falhar rapidamente:

```tsx
// Validar autenticação primeiro
if (!verification?.auth) {
  return { status: 401, body: { error: 'Unauthorized' } }
}

// Depois validar a entrada
if (!params.id) {
  return { status: 400, body: { error: 'Bad Request' } }
}

// Então prosseguir com a lógica de negócio
const result = await logic.doSomething(params.id)
```

### 3. Usar Segurança de Tipos

Aproveite o TypeScript para segurança de tipos:

```tsx
interface CreateUserRequest {
  user_name: string
  email_address: string
  display_name?: string
}

const body: CreateUserRequest = await request.json()
```

### 4. Fazer logs adequados

Registrar eventos e erros importantes:

```tsx
logger.info('User created', { userId: user.id, userAddress })
logger.warn('Rate limit approaching', { userAddress, requests })
logger.error('Failed to create user', { error, userAddress })
```

## Testando Controllers

Veja a [Testing Services (WKC)](/contributor/contributor-pt/guias-do-contribuidor/testing-standards/testing-services-wkc.md) documentação para orientação sobre testes de integração.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-pt/guias-do-contribuidor/componentes-bem-conhecidos/controllers.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
