> 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-es/guias-para-colaboradores/components-conocidos/controllers.md).

# Controladores

Los controladores son responsables de manejar solicitudes HTTP y WebSocket en su aplicación. Sirven como punto de entrada para solicitudes externas y actúan como puente entre la capa de transporte y su lógica de negocio.

## Propósito

Los controladores SOLO DEBEN realizar estas tareas específicas:

1. **Validar la entrada de la capa de transporte** - Verificar autenticación, autorización y formato de entrada
2. **Llamar a componentes de lógica** - Delegar la lógica de negocio a los componentes de lógica apropiados
3. **Transformar la entrada** - Convertir los datos de la solicitud en parámetros para las llamadas a componentes de lógica
4. **Manejar errores** - Capturar y responder correctamente a los errores de los componentes de lógica

{% hint style="warning" %}
Los controladores NO deben contener lógica de negocio. Todas las reglas de negocio DEBEN implementarse en los componentes de lógica.
{% endhint %}

## Ubicación

Todos los controladores DEBEN colocarse bajo el `/src/controllers` directorio y nombrarse en referencia a la acción que realizan.

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

## Estructura del controlador

### Manejador 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 autenticación
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Authentication required'
        }
      }
    }

    // 2. Validar entrada
    if (!id || typeof id !== 'string') {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: 'Invalid user ID'
        }
      }
    }

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

    // 4. Devolver respuesta de éxito
    return {
      status: 200,
      body: {
        ok: true,
        data: user
      }
    }
  } catch (error) {
    // 5. Manejar errores
    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: 'An unexpected error occurred'
      }
    }
  }
}
```

### Manejador de solicitudes 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 autenticación
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Authentication required'
        }
      }
    }

    // 2. Analizar y validar el cuerpo de la solicitud
    const body = await request.json()
    
    if (!body.username || !body.email) {
      return {
        status: 400,
        body: {
          error: 'Bad Request',
          message: 'Username and email are required'
        }
      }
    }

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

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

    // 5. Devolver respuesta de éxito
    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: 'An unexpected error occurred'
      }
    }
  }
}
```

### Manejador de parámetros de consulta

```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 autenticación
    if (!verification?.auth) {
      return {
        status: 401,
        body: {
          error: 'Unauthorized',
          message: 'Authentication required'
        }
      }
    }

    // 2. Extraer y validar parámetros de consulta (¡en minúsculas!)
    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: 'Query parameter is required'
        }
      }
    }

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

    // 4. Devolver respuesta de éxito
    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: 'An unexpected error occurred'
      }
    }
  }
}
```

## Requisitos de entrada

Todas las claves de entrada recibidas a través de los controladores (cuerpo JSON, parámetros de consulta, parámetros de URL, encabezados, etc.) **DEBEN estar en minúsculas** para evitar problemas de mayúsculas/minúsculas al procesar datos.

### Parámetros con varias palabras

Los parámetros de varias palabras DEBEN definirse usando **snake\_case**.

{% hint style="danger" %}
**Nombres incorrectos:**

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

{% hint style="success" %}
**Nombres correctos:**

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

### Ejemplo

```tsx
// Cuerpo de la solicitud
{
  "user_name": "john_doe",        // ✅ Correcto
  "email_address": "john@example.com",  // ✅ Correcto
  "display_name": "John Doe",     // ✅ Correcto
  "max_items": 100                // ✅ Correcto
}

// Parámetros de consulta
?search_query=test&max_results=50&sort_by=created_at  // ✅ Correcto

// Parámetros de URL
/users/:user_id/posts/:post_id   // ✅ Correcto
```

## Manejo de errores

### Respuestas de error estándar

Los controladores deben devolver formatos de respuesta de error consistentes:

```tsx
// 400 Bad Request
{
  "error": "Bad Request",
  "message": "Detailed error message",
  "details": {} // Detalles adicionales opcionales
}

// 401 Unauthorized
{
  "error": "Unauthorized",
  "message": "Authentication required"
}

// 403 Forbidden
{
  "error": "Forbidden",
  "message": "You don't have permission to access this resource"
}

// 404 Not Found
{
  "error": "Not Found",
  "message": "Resource not found"
}

// 409 Conflict
{
  "error": "Conflict",
  "message": "Resource already exists"
}

// 500 Internal Server Error
{
  "error": "Internal Server Error",
  "message": "An unexpected error occurred"
}
```

### Mapeo de errores

Mapear los errores del dominio a los códigos de estado HTTP apropiados:

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

  // Errores específicos del dominio
  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 } }
  }

  // Error genérico
  return {
    status: 500,
    body: {
      error: 'Internal Server Error',
      message: 'An unexpected error occurred'
    }
  }
}
```

## Autenticación y autorización

### Comprobación de autenticación

```tsx
// Requerir autenticación
if (!verification?.auth) {
  return {
    status: 401,
    body: {
      error: 'Unauthorized',
      message: 'Authentication required'
    }
  }
}

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

### Comprobación de autorización

```tsx
// Comprobar permisos mediante componente de lógica
const canAccess = await permissionsLogic.canUserAccessResource(
  userAddress,
  resourceId
)

if (!canAccess) {
  return {
    status: 403,
    body: {
      error: 'Forbidden',
      message: 'You don\'t have permission to access this resource'
    }
  }
}
```

## Buenas prácticas

### 1. Mantener los controladores delgados

Los controladores deben ser envoltorios delgados alrededor de los componentes de lógica:

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

// ❌ Mal: lógica de negocio en el controlador
export async function handler(context) {
  const { database } = context.components
  const user = await database.query('SELECT * FROM users...')
  const processed = processUser(user) // ¡Lógica de negocio!
  const validated = validateUser(processed) // ¡Lógica de negocio!
  return { status: 200, body: { data: validated } }
}
```

### 2. Validar temprano

Validar y fallar rápido:

```tsx
// Validar autenticación primero
if (!verification?.auth) {
  return { status: 401, body: { error: 'Unauthorized' } }
}

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

// Luego proceder con la lógica de negocio
const result = await logic.doSomething(params.id)
```

### 3. Usar seguridad de tipos

Aprovechar TypeScript para seguridad de tipos:

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

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

### 4. Registrar apropiadamente

Registrar eventos y errores 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 })
```

## Pruebas de controladores

Vea la [Testing Services (WKC)](/contributor/contributor-es/guias-para-colaboradores/testing-standards/testing-services-wkc.md) documentación para orientación sobre pruebas de integración.


---

# 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:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/components-conocidos/controllers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
