Subidas e integración frontend

# Subidas seguras de archivos en Next.js con Uppy y Templates firmados de Transloadit

Crea una subida segura en Next.js App Router con Uppy: firma en servidor, Template bloqueado, validación autoritativa, transferencia reanudable y fin asíncrono.

Publicado el 24 de agosto de 2026

## Conclusiones clave

* Mantén el Auth Secret de Transloadit en un módulo `server-only` y devuelve únicamente opciones firmadas y de corta duración para la Assembly.
* Autoriza el Route Handler de firma y limita su tasa de solicitudes; tener la URL de una aplicación no concede permiso para subir archivos.
* Bloquea el Template guardado, exige firmas y vuelve a aplicar las restricciones de archivos del cliente en un límite de procesamiento confiable.

Una subida segura desde el navegador exige más que trasladar el Auth Secret a una ruta de API. El servidor debe autorizar cada solicitud de firma, la carga útil firmada debe indicar un Template con restricciones, el receptor debe aplicar la política de archivos y la aplicación debe conciliar el procesamiento después de que el usuario abandone la página. Este diseño con App Router explicita cada responsabilidad.

## En esta guía

1. [Separa la autorización de la aplicación de la ejecución de la subida](#secure-file-uploads-nextjs-uppy-section-1)
2. [Bloquea el Template antes de exponer el formulario de subida](#secure-file-uploads-nextjs-uppy-section-2)
3. [Firma una solicitud de corta duración en un módulo server-only](#secure-file-uploads-nextjs-uppy-section-3)
4. [Protege el endpoint de firma del App Router](#secure-file-uploads-nextjs-uppy-section-4)
5. [Monta Uppy una sola vez dentro de un Client Component](#secure-file-uploads-nextjs-uppy-section-5)
6. [Concilia el progreso, los reintentos y la finalización](#secure-file-uploads-nextjs-uppy-section-6)
7. [Prueba los controles como atacante y como usuario cuya operación fue interrumpida](#secure-file-uploads-nextjs-uppy-section-7)

## Lo más importante

* Crea una sola instancia de Uppy para el componente cliente y deja que el plugin de Transloadit con mantenimiento activo coordine la transferencia reanudable.
* Trata el progreso del navegador como progreso de la transferencia y usa notificaciones verificadas para registrar de forma persistente la finalización del procesamiento.
* Guarda el acceso al almacenamiento de terceros en credenciales de Template con privilegios mínimos, en lugar de hacerlo en campos de solicitud o variables de entorno del cliente.

## Separa la autorización de la aplicación de la ejecución de la subida

El navegador es un solicitante no confiable, incluso cuando la interfaz forma parte de tu aplicación Next.js. Un Client Component puede contener la Auth Key pública y un ID de Template, pero nunca debe contener el Auth Secret del Workspace, credenciales de almacenamiento sin procesar ni autorización para elegir Steps de procesamiento arbitrarios. Mantén el Auth Secret detrás de un Route Handler y haz que ese endpoint decida si el usuario actual de la aplicación puede iniciar esta subida específica.

Esa decisión es independiente de la integridad de la solicitud de Transloadit. Una firma válida demuestra que tu servidor aprobó los parámetros serializados de la Assembly durante un periodo limitado; no demuestra que la persona que solicita una firma a tu servidor sea propietaria de un proyecto, se mantenga dentro del cupo, haya superado una verificación de CSRF o pueda publicar el resultado. El adaptador de autorización de la aplicación debe aplicar esas reglas antes de firmar. El Template guardado limita entonces lo que Transloadit ejecutará.

### Next.js

El código de la aplicación autentica la sesión, verifica la propiedad del recurso y la política de CSRF, aplica límites de tasa de solicitudes o de cupo y emite opciones firmadas para fines específicos.

### Uppy

Se encarga de la selección de archivos, la interfaz accesible de subida, las restricciones del navegador, el progreso, los reintentos y la coordinación de transferencias reanudables.

### Transloadit

Crea la Assembly, aplica la solicitud firmada y el Template guardado, inspecciona los archivos, ejecuta el procesamiento y exporta los resultados configurados.

## Bloquea el Template antes de exponer el formulario de subida

Crea el flujo de trabajo de procesamiento como un Template guardado. Establece `allow_steps_override` en `false` para que un navegador no pueda enviar Steps de reemplazo junto con su `template_id`. Activa «Exigir una firma válida» en este Template o exige firmas correctas para todo el Workspace. Son controles distintos: el Template bloqueado fija el grafo de procesamiento, mientras que la aplicación obligatoria de firmas rechaza los parámetros firmados que se hayan alterado, hayan vencido o no sean válidos por cualquier otro motivo. Tu aplicación sigue decidiendo qué usuario puede recibir esos parámetros.

Repite en el límite de procesamiento las comprobaciones de cliente de bajo costo aplicadas en la interfaz. El ejemplo limita cada archivo a 50 MiB, limita cada Assembly a cinco archivos y usa `/file/filter` con los metadatos MIME detectados, en lugar de confiar en el nombre del archivo o en la declaración del navegador. Adapta la lista exacta de elementos permitidos al producto. Añade verificación, análisis de malware, cuarentena o revisión humana cuando el riesgo del contenido lo requiera; ninguna comprobación MIME por sí sola hace seguro el contenido arbitrario de los usuarios.

El valor `user_uploads` identifica las credenciales de Template almacenadas en el Workspace. Concede a esa entidad externa únicamente las operaciones de almacenamiento, el bucket y las rutas que requiere este flujo de trabajo. El navegador no recibe ni firma la clave de acceso subyacente. Así, la rotación de credenciales puede realizarse independientemente del paquete de Next.js, pero debe coordinarse porque afecta a todos los Templates que hacen referencia al registro.

Definir un Template bloqueado con límites autoritativos y acceso limitado al almacenamiento

```
{
  "allow_steps_override": false,
  "auth": {
    "max_number_of_files": 5,
    "max_size": 52428800
  },
  "notify_url": "https://app.example.com/api/transloadit-notifications",
  "steps": {
    ":original": {
      "robot": "/upload/handle"
    },
    "accepted_images": {
      "use": ":original",
      "robot": "/file/filter",
      "accepts": [
        ["${file.mime}", "regex", "^(image/jpeg|image/png|image/webp)$"]
      ],
      "error_on_decline": true,
      "error_msg": "Only JPEG, PNG, and WebP images are accepted"
    },
    "stored": {
      "use": "accepted_images",
      "robot": "/s3/store",
      "credentials": "user_uploads"
    }
  }
}
```

## Firma una solicitud de corta duración en un módulo `server-only`

Mantén la configuración del servidor en variables de entorno sin el prefijo `NEXT_PUBLIC_` e importa `server-only` al principio del módulo de firma. Next.js hará que la compilación falle si el código del cliente importa ese módulo. El marcador es una medida de protección útil, no un gestor de secretos: las políticas de acceso de producción, el enmascaramiento de datos sensibles en los registros, el aislamiento del entorno de vista previa y la rotación de la credencial siguen siendo importantes.

El SDK de Node añade la Auth Key configurada, serializa los parámetros y devuelve exactamente esa cadena `params` con su firma. Devuelve ambos valores sin cambios. Analizar la cadena, añadir un campo o volver a serializarla en un orden diferente después de firmarla genera un payload distinto que debe rechazarse. El ejemplo establece un vencimiento de cinco minutos porque Uppy solicita las opciones inmediatamente antes de crear la Assembly y añade un nonce nuevo para cada autorización.

Selecciona el Template en el servidor. No aceptes `template_id`, `steps`, `notify_url`, credenciales de exportación ni valores de transformación sin límites del cuerpo de la solicitud para firmarlos sin más. Si el producto realmente ofrece varios flujos de trabajo de subida, asigna una operación pequeña del nivel de la aplicación, como `avatar` o `product-gallery`, a un Template y a límites incluidos en una lista de permitidos después de comprobar el permiso del usuario.

Firmar exactamente un Template fijo con una función auxiliar exclusiva del servidor

```
import 'server-only'

import { randomUUID } from 'node:crypto'

import { Transloadit } from 'transloadit'

export interface AssemblyOptions {
  params: string
  signature: string
}

type TransloaditEnvironmentName =
  | 'TRANSLOADIT_KEY'
  | 'TRANSLOADIT_SECRET'
  | 'TRANSLOADIT_TEMPLATE_ID'

function readServerEnvironment(name: TransloaditEnvironmentName): string {
  const value = process.env[name]
  if (value == null || value === '') {
    throw new Error('Missing Transloadit server configuration')
  }

  return value
}

const templateId = readServerEnvironment('TRANSLOADIT_TEMPLATE_ID')
const transloadit = new Transloadit({
  authKey: readServerEnvironment('TRANSLOADIT_KEY'),
  authSecret: readServerEnvironment('TRANSLOADIT_SECRET'),
})

export function createAssemblyOptions(): AssemblyOptions {
  const requestParameters = {
    auth: {
      expires: new Date(Date.now() + 5 * 60 * 1000).toISOString(),
      nonce: randomUUID(),
    },
    template_id: templateId,
  }

  return transloadit.calcSignature(requestParameters)
}
```

## Protege el endpoint de firma del App Router

Se puede acceder a un Route Handler como a cualquier otro endpoint HTTP. La función `authorizeUpload` del ejemplo es deliberadamente específica de la aplicación: conéctala con la biblioteca de sesiones existente del proyecto, las comprobaciones de propiedad de los recursos, la estrategia de CSRF y el limitador de tasa de solicitudes compartido. Devuelve la denegación antes de generar la firma. En aplicaciones multiinquilino, aplica los límites tanto por inquilino como por usuario y comprueba qué inquilino será propietario del recurso resultante.

La ruta no acepta parámetros arbitrarios de Assembly y marca la respuesta correcta como `no-store`. Su mensaje público de fallo es genérico de manera intencional. Registra en el servidor un identificador interno de solicitud o de traza, la categoría de la decisión y el ID del actor, pero no devuelvas al navegador trazas de pila, identificadores de cuenta, cuerpos de respuestas de terceros ni detalles de la credencial. Deja que los fallos inesperados utilicen la gestión centralizada de errores saneados de la aplicación, en lugar de envolver cada llamada en un bloque catch que genere ruido.

Limitar la tasa de solicitudes de la ruta de firma controla la creación de Assemblies, pero no sustituye los límites de facturación de la cuenta, los límites de archivos del Template ni las cuotas de la aplicación. Aplica los tres. Una firma es una capacidad de corta duración: cualquiera que obtenga el payload firmado completo puede intentar enviarlo mientras siga siendo válido. Por eso, envíalo solo mediante HTTPS y evita que se conserve en datos analíticos, almacenamiento del navegador, URL y registros.

Crear un endpoint de firma autenticado en App Router

```
import type { NextRequest } from 'next/server'

import { NextResponse } from 'next/server'

import { authorizeUpload } from '../../../server/upload-authorization'
import {
  type AssemblyOptions,
  createAssemblyOptions,
} from '../../../server/transloadit-options'

interface ErrorResponse {
  error: string
}

export async function POST(
  request: NextRequest,
): Promise<NextResponse<AssemblyOptions | ErrorResponse>> {
  const permission = await authorizeUpload(request)
  if (!permission.allowed) {
    return NextResponse.json({ error: 'Upload not allowed' }, { status: 403 })
  }

  return NextResponse.json(createAssemblyOptions(), {
    headers: { 'Cache-Control': 'no-store' },
  })
}
```

### Autenticar

Obtén una sesión nueva del lado del servidor en lugar de confiar en un identificador de usuario o de inquilino proporcionado por el cliente.

### Autorizar

Comprueba que el actor tenga permiso para subir archivos para el recurso y la operación de destino antes de crear la capacidad firmada.

### Limitar

Aplica límites por usuario y por inquilino a la tasa de solicitudes, el trabajo simultáneo, la política de almacenamiento y las cuotas comerciales, además de los límites del Template.

## Monta Uppy una sola vez dentro de un Client Component

Uppy necesita API del navegador, por lo que el componente de subida se encuentra detrás de un límite `use client`. Crea una sola vez la instancia de Uppy mediante un inicializador de estado diferido. Volver a crearla durante el renderizado descarta los archivos seleccionados y rompe el control del ciclo de vida. Este componente destruye la instancia cuando controla todo el ciclo de vida de la subida. Si las subidas deben continuar al navegar entre rutas, eleva la instancia a un proveedor del cliente con un ciclo de vida más largo y destrúyela cuando finalice ese proveedor.

La función asíncrona `assemblyOptions` llama a la ruta protegida mientras Uppy prepara la subida. Comprueba `response.ok`, gestiona de forma segura el JSON malformado, valida la estructura de la respuesta y solo muestra un error de autorización estable para el usuario. El Dashboard de Uppy proporciona la interfaz de selección, progreso, cancelación y errores, mientras que el plugin de Transloadit crea la Assembly y envía los archivos mediante una infraestructura de subidas reanudables. La secuencia acotada de tiempos de reintento ayuda ante fallos temporales sin reintentar indefinidamente.

Esos reintentos funcionan mientras esta instancia de Uppy siga activa. No permiten que el componente mostrado recupere los archivos seleccionados ni el estado de la Assembly después de recargar la página. Si la recuperación tras una recarga es un requisito del producto, configura y prueba Golden Retriever u otro diseño de persistencia documentado con el plugin de Transloadit; no deduzcas esa capacidad únicamente del protocolo tus ni de `retryDelays`.

Los límites del navegador reflejan los del Template para ofrecer una respuesta rápida, pero no constituyen el límite de seguridad. Quien realiza la llamada puede omitir el componente, y los metadatos de los archivos pueden ser falsos. Mantén en el Template bloqueado las comprobaciones autoritativas de cantidad de archivos, tamaño en bytes y contenido detectado, así como la política de exportación. Decide también si cancelar en el navegador debe cancelar solo la transferencia, la Assembly o el registro del recurso en la aplicación; después, prueba esa decisión en lugar de suponer que los tres estados son idénticos.

Crear un Client Component con restricciones alineadas y reintentos limitados

```
'use client'

import Uppy from '@uppy/core'
import Dashboard from '@uppy/react/dashboard'
import Transloadit from '@uppy/transloadit'
import { type ReactNode, useEffect, useState } from 'react'
import { z } from 'zod'

import '@uppy/core/css/style.min.css'
import '@uppy/dashboard/css/style.min.css'

const assemblyOptionsSchema = z.object({
  params: z.string().min(1),
  signature: z.string().regex(/^(sha1|sha256|sha384):[0-9a-f]+$/),
})

async function fetchAssemblyOptions(): Promise<z.infer<typeof assemblyOptionsSchema>> {
  const response = await fetch('/api/transloadit-params', {
    method: 'POST',
    headers: { Accept: 'application/json' },
  })

  if (!response.ok) {
    throw new Error('Could not authorize this upload. Try again.')
  }

  const responseBody: unknown = await response.json().catch(() => null)
  const parsedOptions = assemblyOptionsSchema.safeParse(responseBody)
  if (!parsedOptions.success) {
    throw new Error('Could not authorize this upload. Try again.')
  }

  return parsedOptions.data
}

function createUppy(): Uppy {
  return new Uppy({
    restrictions: {
      allowedFileTypes: ['image/jpeg', 'image/png', 'image/webp'],
      maxFileSize: 50 * 1024 * 1024,
      maxNumberOfFiles: 5,
    },
  }).use(Transloadit, {
    assemblyOptions: fetchAssemblyOptions,
    retryDelays: [0, 1_000, 3_000, 5_000],
    waitForEncoding: false,
  })
}

export function UploadForm(): ReactNode {
  const [uppy] = useState(createUppy)

  useEffect(() => {
    return () => uppy.destroy()
  }, [uppy])

  return <Dashboard height={420} proudlyDisplayPoweredByUppy={false} uppy={uppy} />
}
```

## Concilia el progreso, los reintentos y la finalización

El progreso de la transferencia y el progreso del procesamiento responden preguntas diferentes. Con `waitForEncoding: false`, la interfaz puede finalizar después de que los bytes lleguen a Transloadit mientras continúan la validación, la transformación y la exportación. Escucha `transloadit:assembly-created` y asocia el ID de la Assembly con un registro pendiente de la aplicación. Así, una navegación puede interrumpir la observación en el navegador sin perder la identidad necesaria para la conciliación.

Incluye un `notify_url` fijo en el Template controlado por el servidor para gestionar la finalización asíncrona. El controlador de notificaciones debe verificar la firma mediante el Auth Secret asociado con la Auth Key de la Assembly, validar la carga útil, vincular el ID de la Assembly con el registro pendiente esperado y aplicar los resultados de forma idempotente antes de devolver una respuesta correcta. Las notificaciones pueden reintentarse, por lo que un duplicado debe confirmar el estado terminal existente en lugar de crear otro recurso o evento de publicación.

Usa `waitForEncoding: true` solo para flujos de trabajo cortos en los que el usuario deba permanecer en la página y el código del navegador necesite realmente los resultados finales. Incluso entonces, conserva una ruta de recuperación del lado del servidor porque las pestañas se cierran y las conexiones desaparecen. Una tarea programada de conciliación puede consultar las Assemblies no terminales cuyas notificaciones no se recibieron. Separa la política de reintentos según el fallo: reanuda la transferencia tras una interrupción de red, solicita nuevas opciones firmadas cuando venzan antes de crear la Assembly y detente ante un rechazo de la política hasta que el usuario cambie el archivo.

### Seleccionado

El navegador tiene un archivo candidato; ningún sistema de confianza lo ha aceptado todavía.

### Subido

El receptor tiene los bytes, pero la validación autoritativa, el procesamiento o la exportación aún pueden fallar.

### Listo

Se ha guardado de forma persistente un resultado terminal verificado y se ha autorizado para el uso previsto en la aplicación.

## Prueba los controles como atacante y como usuario cuya operación fue interrumpida

Prueba el endpoint de firma sin una sesión, con el inquilino incorrecto, con un token CSRF ausente o no válido cuando corresponda, por encima de su límite de tasa de solicitudes y después de revocar el permiso. Confirma que ninguna respuesta ni ningún registro contenga el Auth Secret, credenciales de Template, una traza de pila o el error sin procesar de una dependencia. Intenta reemplazar el `template_id`, añadir `steps`, extender `auth.expires` y enviar opciones firmadas después de que hayan vencido; los controles de firma o de Template deberían rechazar la solicitud no válida.

Prueba un JPEG permitido, un tipo MIME no permitido con una extensión de imagen, un archivo demasiado grande, demasiados archivos, un archivo de cero bytes, una pérdida de conexión en varios desplazamientos, la recarga del navegador, la cancelación, una autorización vencida, una notificación duplicada, la denegación del almacenamiento y un fallo de procesamiento posterior a la subida. Confirma que el comportamiento al recargar coincida con el diseño de persistencia, en lugar de suponer que la recuperación es automática. Verifica con el teclado y las tecnologías de asistencia que los anuncios de progreso y errores sean accesibles; después, inspecciona el almacenamiento temporal y los registros de la aplicación para detectar filtraciones o estados pendientes permanentes.

Supervisa las denegaciones de firmas, las decisiones de limitación de la tasa de solicitudes, las tasas de creación y fallo de Assemblies, la recuperación de subidas, la latencia del procesamiento, la antigüedad de las notificaciones, los errores de almacenamiento y la antigüedad de los registros pendientes sin registrar payloads protegidos. Genera alertas ante cambios sostenidos, no ante equivocaciones individuales de los usuarios. Conserva el ID del Template y una versión del flujo de trabajo administrada por la aplicación en los registros operativos, porque un Template guardado puede cambiar con el tiempo.

## Detalles técnicos que conviene conocer

* Un archivo `route.ts` de App Router es un endpoint HTTP, por lo que debe aplicar su propia autenticación, autorización, controles contra abusos y validación de entradas antes de devolver una firma.
* El marcador del paquete `server-only` provoca un error durante la compilación si se importa un módulo protegido en un Client Component, pero los secretos del despliegue aún requieren una configuración correcta de la plataforma y controles de acceso.
* El método `calcSignature` del SDK de Node de Transloadit añade la Auth Key cuando está configurada, serializa los parámetros de la solicitud y devuelve exactamente esa cadena `params` con su firma HMAC.
* Signature Authentication abarca `auth.expires` y el resto de la carga útil serializada de la solicitud; cambiar un valor protegido después de firmarla invalida la firma.
* Un Template puede aceptar de forma predeterminada anulaciones de Steps durante la ejecución, por lo que los flujos de trabajo controlados por el navegador deben establecer `allow_steps_override` en `false`, salvo que se pretenda permitir una anulación específica sometida a una revisión rigurosa.
* Las restricciones de Uppy proporcionan información inmediata en el navegador, mientras que `auth.max_size`, `auth.max_number_of_files` del Template y los Steps de procesamiento de archivos aplican la política cuando ya no se puede confiar en el código del navegador.
* El plugin de Transloadit acepta una función `assemblyOptions` asíncrona, crea una Assembly y configura subidas reanudables al endpoint del protocolo tus de la Assembly.
* Con `waitForEncoding: false`, Uppy finaliza después de la transferencia y no después del procesamiento; la aplicación debe conservar el ID de la Assembly y usar una notificación cuya firma se haya verificado o una consulta posterior del estado para obtener el resultado terminal.
* El ejemplo con valores limitados de `retryDelays` reanuda las transferencias tras fallos transitorios mientras su instancia de Uppy siga activa; la recuperación después de una recarga requiere que el estado de Uppy y Transloadit sea persistente, por ejemplo, mediante una integración con Golden Retriever configurada expresamente.
* Las credenciales de Template son registros del Workspace referenciados por nombre, por lo que los secretos de almacenamiento sin procesar no aparecen en el JSON del Template guardado, el paquete del cliente ni los parámetros firmados de la Assembly; el Template contiene únicamente el nombre del registro de la credencial.

## Un enfoque práctico

1. 1\
   Crea un Template bloqueado que requiera firma, con límites de subida, verificaciones del contenido detectado y credenciales de Template con alcance limitado.
2. 2\
   Agrega una función auxiliar de firma exclusiva del servidor y un endpoint de App Router autenticado, con límite de tasa de solicitudes y sin almacenamiento en caché.
3. 3\
   Monta una sola instancia de Uppy en un Client Component con restricciones, reintentos y fallos de autorización saneados y coherentes.
4. 4\
   Guarda de forma persistente el ID de la Assembly, verifica las notificaciones de finalización y prueba la denegación, la interrupción, el vencimiento, la reejecución y la entrega duplicada.

Un flujo de trabajo multimedia de cuatro etapas

## Cuándo resulta útil Transloadit

Usa el plugin de Transloadit para Uppy, que recibe mantenimiento activo, cuando un flujo de navegador de Next.js necesite subidas reanudables seguidas de validación, transformación y exportación gestionadas. Un Template guardado fija el flujo de trabajo permitido, Signature Authentication protege los parámetros aprobados de la solicitud y su vencimiento, y las credenciales de Template mantienen los secretos del almacenamiento sin procesar fuera de los paquetes de cliente de Next.js y de los parámetros de Assembly.

## Límite de la arquitectura

Tu aplicación Next.js autentica al usuario y decide si emite una autorización de subida de corta duración. Uppy se encarga de la selección en el navegador, el progreso y la coordinación de la transferencia. Transloadit recibe los bytes, aplica el Template configurado de validación y procesamiento, e informa los resultados. Ninguna de esas capas sustituye el registro persistente de los recursos ni la política de publicación de la aplicación.

## Preguntas frecuentes

### ¿Puede un Client Component de Next.js contener la Auth Key de Transloadit?

La Auth Key identifica el Workspace y puede aparecer en una solicitud firmada, pero el Auth Secret debe permanecer en el servidor. Aun así, exige firmas y restringe el Template, porque una Auth Key expuesta sin esos controles puede permitir solicitudes no autorizadas.

### ¿Por qué usar un Route Handler en lugar de firmar en un Server Component?

Uppy solicita nuevas opciones de Assembly desde el código del navegador justo antes de la subida. Un Route Handler proporciona ese límite HTTP, pero debe autenticar y autorizar la solicitud como cualquier otro endpoint de mutación. Una Server Function podría implementar un límite similar si la integración la invoca de forma segura.

### ¿Una solicitud firmada para un Template bloqueado evita todo tipo de abuso en las subidas?

No. Protege la integridad de la solicitud y restringe la receta de procesamiento. Aún necesitas autorización de la aplicación, límites de tasa de solicitudes y de facturación, límites de cantidad de archivos y bytes, validación del contenido detectado, almacenamiento con privilegios mínimos y cualquier análisis o revisión que exija el modelo de amenazas del producto.

### ¿`waitForEncoding` debe ser true en Next.js?

Por lo general, no para trabajos largos. Con false, el usuario espera la transferencia y la aplicación completa el procesamiento mediante una notificación verificada. Establécelo en true solo cuando el flujo de trabajo sea corto y el código del navegador necesite los resultados finales, sin dejar de realizar la conciliación del lado del servidor para las pestañas cerradas y las conexiones perdidas.

### ¿Dónde deben almacenarse las credenciales de S3 u otros servicios de almacenamiento?

Guárdalas como credenciales de Template de Transloadit con privilegios mínimos y, desde el Template guardado, referencia la credencial por su nombre. No incluyas credenciales sin procesar del proveedor en variables de entorno públicas de Next.js, código del cliente, campos de solicitudes firmadas, registros ni metadatos de resultados.

### ¿Son suficientes las restricciones de archivos de Uppy para validar las subidas?

No. Mejoran la información que reciben los usuarios que siguen las indicaciones. Repite los límites de bytes y cantidad en el Template e inspecciona las propiedades detectadas de los archivos mediante Steps de procesamiento confiables, porque quienes hacen las solicitudes pueden eludir el JavaScript del navegador y las declaraciones pueden ser engañosas.

## Crea el flujo de trabajo

Pasa del concepto a una Assembly probada con documentación de Robots y demos funcionales.

* [Lee la documentación de la API](/es/docs.md)
* [Explora demos funcionales EN (English)](/demos.md)
* [Crea un Workspace gratuito](/c/signup/)

Subidas e integración frontend

## Continúa con guías relacionadas

* [Subidas de archivos con React y Uppy: reanudación, vistas previas, validación y procesamiento](/es/guides/react-file-uploads-with-uppy.md)\
  Crea un componente de subida en React con Uppy: vistas previas, validación, progreso accesible, cancelación, reanudación y procesamiento con Transloadit.
* [Mejores bibliotecas JavaScript para subir archivos: Uppy vs FilePond vs Dropzone](/es/guides/best-javascript-file-upload-libraries.md)\
  Compara Uppy, FilePond y Dropzone según el protocolo de transferencia, el modelo de interfaz, la recuperación, la integración y el mantenimiento a largo plazo.
* [Cómo aceptar subidas grandes pese a una conexión interrumpida](/es/guides/resumable-uploads-for-large-files.md)\
  Acepta subidas de varios gigabytes mediante el protocolo tus, reanúdalas tras perder la conexión y mantén viva la Assembly hasta terminarlas.
* [Guía de API de subida de archivos: arquitectura, seguridad y selección de proveedores](/es/guides/file-upload-api-guide.md)\
  Elige e implementa una API de subida comparando arquitectura, reanudación, transferencia directa a la nube, seguridad, límites de almacenamiento y proveedores.
