AWS Amplify Gen 2 — Cap.2: Anatomía de un Proyecto Amplify Gen 2

aws tutorial typescript web-dev

Video próximamente

Fecha esperada:

Creando tu Primer Proyecto

Antes de examinar la estructura de archivos, crea un proyecto para tener algo concreto que inspeccionar. Comienza con una app Next.js 15 y agrega Amplify Gen 2:

# Crea un nuevo proyecto Next.js 15
npx create-next-app@latest mi-app-amplify \
  --typescript \
  --app \
  --tailwind \
  --eslint \
  --src-dir

cd mi-app-amplify

# Inicializa Amplify Gen 2
npm create amplify@latest

# Instala la librería frontend de Amplify
npm install aws-amplify

El comando npm create amplify@latest scaffoldea el directorio amplify/ y un conjunto de archivos de inicio. Vamos a recorrer cada archivo que crea y entender qué hace cada uno.

¿Qué comando inicializa un backend Amplify Gen 2 en un proyecto existente?

La Estructura del Directorio amplify/

Después de la inicialización, tu proyecto tiene esta estructura:

mi-app-amplify/
├── amplify/
│   ├── auth/
│   │   └── resource.ts        # Configuración de Auth (Cognito)
│   ├── data/
│   │   └── resource.ts        # Schema de Data (AppSync + DynamoDB)
│   ├── backend.ts             # Orquestador — importa y conecta todo
│   ├── package.json           # Dependencias del backend de Amplify
│   └── tsconfig.json          # Config TypeScript para el backend
├── src/
│   ├── app/
│   │   └── layout.tsx         # Layout raíz (donde agregarás Amplify.configure)
│   └── ...
├── amplifyconfiguration.json  # Auto-generado — NO editar manualmente
└── package.json               # Dependencias del frontend

Observa que hay dos archivos package.json. El raíz maneja las dependencias del frontend de Next.js. El amplify/package.json maneja las dependencias del backend solamente — los paquetes que corren en AWS Lambda y en el entorno de síntesis de CDK, no en el browser.

El Orquestador: backend.ts

amplify/backend.ts es la única fuente de verdad para todo tu backend. Importa definiciones de recursos desde subdirectorios y los pasa a defineBackend():

// amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';

/**
 * @see https://docs.amplify.aws/react/build-a-backend/
 */
defineBackend({
  auth,
  data,
});

defineBackend() es la fábrica de constructs CDK. Cuando ejecutas npx ampx sandbox o triggeras un build en la nube, este archivo es el punto de entrada. El toolchain de Amplify lo importa, recolecta todos los constructs CDK, los sintetiza en una plantilla de CloudFormation, y la despliega.

También puedes acceder directamente a los stacks CDK subyacentes desde backend.ts usando backend.createStack() para agregar recursos AWS personalizados. Así es como escapas de las abstracciones de Amplify cuando es necesario.

Archivos de Recursos: El Patrón resource.ts

Cada recurso de Amplify vive en su propio directorio con un archivo resource.ts. El amplify/auth/resource.ts por defecto se ve así:

// amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
    email: true,
  },
});

Y amplify/data/resource.ts comienza con un ejemplo de Todo:

// amplify/data/resource.ts
import { a, defineData, type ClientSchema } from '@aws-amplify/backend';

const schema = a.schema({
  Todo: a.model({
    content: a.string(),
    isDone: a.boolean(),
  }).authorization(allow => [allow.owner()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});

La exportación de tipo ClientSchema<typeof schema> es crucial. Deriva tipos TypeScript de tu definición de schema en tiempo de compilación. Cuando usas generateClient<Schema>() en el frontend, TypeScript conoce la forma exacta de cada modelo — nombres de campos, tipos, y si son requeridos u opcionales.

¿Cuál es el propósito de la exportación de tipo `ClientSchema<typeof schema>` en el archivo de recurso de data?

La Configuración Generada: amplifyconfiguration.json

Después de ejecutar npx ampx sandbox, Amplify genera amplifyconfiguration.json en la raíz de tu proyecto. Este archivo es el puente entre tu backend y tu frontend. Contiene todos los endpoints, IDs, y configuración que la librería frontend de Amplify necesita:

{
  "version": "1.3",
  "auth": {
    "user_pool_id": "us-east-1_XXXXXXXXX",
    "aws_region": "us-east-1",
    "user_pool_client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXX",
    "identity_pool_id": "us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "mfa_methods": [],
    "standard_required_attributes": ["email"],
    "username_attributes": ["email"],
    "user_verification_types": ["email"],
    "mfa_configuration": "NONE",
    "password_policy": {
      "min_length": 8,
      "require_lowercase": true,
      "require_numbers": true,
      "require_symbols": true,
      "require_uppercase": true
    },
    "unauthenticated_identities_enabled": true
  },
  "data": {
    "url": "https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.us-east-1.amazonaws.com/graphql",
    "aws_region": "us-east-1",
    "default_authorization_type": "AMAZON_COGNITO_USER_POOLS",
    "authorization_types": [],
    "model_introspection": { "...": "..." }
  }
}

Importante: No edites este archivo manualmente. Se regenera cada vez que haces deploy. Agrégalo a .gitignore si trabajas en equipo (cada developer tiene su propio sandbox con IDs diferentes). Sin embargo, el archivo generado para producción debe ser commiteado para los builds de CI/CD.

Configurando el Frontend

En el frontend, Amplify.configure() lee amplifyconfiguration.json e inicializa todos los clientes. En un proyecto Next.js 15 con App Router, el mejor lugar para esto es un componente cliente que envuelve tu layout:

// src/components/ConfigureAmplify.tsx
"use client";

import { Amplify } from "aws-amplify";
import config from "../../amplifyconfiguration.json";

Amplify.configure(config, { ssr: true });

export default function ConfigureAmplify() {
  return null;
}
// src/app/layout.tsx
import ConfigureAmplify from "@/components/ConfigureAmplify";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <ConfigureAmplify />
        {children}
      </body>
    </html>
  );
}

La opción { ssr: true } en Amplify.configure() es requerida para Next.js App Router. Le indica a Amplify que use cookies en lugar de localStorage para el almacenamiento de tokens, permitiendo que el server-side rendering lea el estado de auth del usuario.

El Cliente de Datos Tipado

Con Amplify.configure() en su lugar, puedes crear un cliente de datos tipado en cualquier parte de tu app:

// src/lib/amplify-client.ts
import { generateClient } from "aws-amplify/data";
import type { Schema } from "../../amplify/data/resource";

export const client = generateClient<Schema>();
// Usando el cliente en un componente
import { client } from "@/lib/amplify-client";

// TypeScript sabe: content es string | null, isDone es boolean | null
const { data: todos } = await client.models.Todo.list();

// Crear con type safety completo
const { data: newTodo } = await client.models.Todo.create({
  content: "Aprender Amplify Gen 2",
  isDone: false,
});

TypeScript detectará errores en tiempo de compilación si pasas nombres de campo incorrectos o tipos equivocados. Este es el beneficio de “zero guesswork” de generateClient<Schema>() — el sistema de tipos conoce la estructura de tu modelo de datos sin ninguna definición de tipo manual.

Dependencias: Qué Instala Amplify y Por Qué

Amplify usa un modelo de dependencias dividido. El backend (amplify/package.json) contiene:

{
  "dependencies": {
    "@aws-amplify/backend": "^1.0.0",
    "@aws-amplify/backend-cli": "^1.0.0",
    "aws-cdk-lib": "^2.0.0",
    "constructs": "^10.0.0",
    "typescript": "^5.0.0"
  }
}

El frontend (package.json) contiene:

{
  "dependencies": {
    "aws-amplify": "^6.0.0",
    "@aws-amplify/ui-react": "^6.0.0"
  }
}

@aws-amplify/backend es para escribir definiciones de recursos del backend y corre solo durante la síntesis/despliegue. aws-amplify es la librería compatible con el browser que corre en tu frontend, proporcionando APIs de Auth, Data y Storage.

¿Por qué un proyecto Next.js con Amplify tiene dos archivos package.json separados?

Pon a Prueba tu Conocimiento

Conclusiones

  • amplify/backend.ts es el único orquestador — importa y conecta todas las definiciones de recursos vía defineBackend()
  • Cada recurso vive en su propio directorio con un archivo resource.ts siguiendo la convención defineXxx()
  • El pipeline TypeScript-a-CloudFormation: tu fuente → CDK synth → CloudFormation → recursos AWS desplegados
  • amplifyconfiguration.json es auto-generado después de cada deploy — nunca lo edites manualmente, agrégalo a .gitignore para sandboxes de equipo
  • Amplify.configure(config, { ssr: true }) debe llamarse una vez en la raíz de tu app Next.js
  • generateClient<Schema>() crea un cliente de datos completamente tipado — TypeScript conoce cada nombre de campo y tipo de tu schema
  • Dos archivos package.json sirven propósitos diferentes: raíz para frontend (browser), amplify/ para backend (síntesis CDK)