npm package: @jamx-framework/graphql
Description: JAMX Framework â€” GraphQL adapter
Author: Stiven-21
Latest version: 2.0.0

# @jamx-framework/graphql

## Descripción
Módulo de GraphQL para JAMX Framework. Proporciona una API completa para construir, ejecutar y gestionar esquemas GraphQL, incluyendo soporte para DataLoader (carga por lotes), paginación, y registro de resolvers. Permite crear servidores GraphQL type-safe que se integran perfectamente con el ecosistema JAMX.

## Cómo funciona
El módulo implementa las capacidades principales de GraphQL:

1. **Schema Builder**: Construye esquemas GraphQL a partir de clases y decoradores de JAMX
2. **Resolver Registry**: Registra y gestiona resolvers para campos y tipos
3. **Executor**: Ejecuta queries y mutations contra el schema
4. **DataLoader**: Carga por lotes para evitar el problema N+1
5. **Pagination**: Utilidades para paginación cursor-based y offset-based

## Componentes principales
- **src/schema-builder.ts**: Clase `SchemaBuilder` que construye esquemas GraphQL desde tipos TypeScript
- **src/executor.ts**: Clase `Executor` que ejecuta queries y mutations
- **src/resolver-registry.ts**: `ResolverRegistry` que gestiona resolvers de campos
- **src/dataloader.ts**: Implementación de DataLoader para batch loading
- **src/pagination.ts**: Utilidades de paginación (Connection, Edge, PageInfo)
- **src/types.ts**: Tipos compartidos (GraphQLType, Resolver, etc.)
- **src/index.ts**: Punto de exportación

## Uso básico
```typescript
import { SchemaBuilder, Executor, ResolverRegistry } from '@jamx-framework/graphql';

// 1. Definir tipos GraphQL
const builder = new SchemaBuilder();

const UserType = builder.objectType('User', {
  fields: (t) => ({
    id: t.field({ type: 'ID' }),
    name: t.field({ type: 'String' }),
    email: t.field({ type: 'String' }),
  }),
});

const QueryType = builder.objectType('Query', {
  fields: (t) => ({
    user: t.field({
      type: UserType,
      args: { id: t.arg({ type: 'ID' }) },
      resolve: async (parent, args, ctx) => {
        return await ctx.db.users.findById(args.id);
      },
    }),
  }),
});

// 2. Construir schema
const schema = builder.build();

// 3. Registrar resolvers
const registry = new ResolverRegistry();
registry.register('Query.user', async (parent, args, ctx) => {
  return await ctx.db.users.findById(args.id);
});

// 4. Ejecutar query
const executor = new Executor(schema, registry);
const result = await executor.execute(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`, { id: '123' }, { db });

console.log('Resultado GraphQL:', result);
```

## Ejemplos
### Schema completo con mutations
```typescript
import { SchemaBuilder } from '@jamx-framework/graphql';

const builder = new SchemaBuilder();

// Input types
const CreateUserInput = builder.input('CreateUserInput', {
  fields: (t) => ({
    name: t.field({ type: 'String' }),
    email: t.field({ type: 'String' }),
    password: t.field({ type: 'String' }),
  }),
});

// User type
const UserType = builder.objectType('User', {
  fields: (t) => ({
    id: t.field({ type: 'ID' }),
    name: t.field({ type: 'String' }),
    email: t.field({ type: 'String' }),
    createdAt: t.field({ type: 'String' }),
  }),
});

// Query
builder.queryType({
  fields: (t) => ({
    users: t.field({
      type: ['User'],
      resolve: async (_, __, ctx) => await ctx.db.users.findAll(),
    }),
    user: t.field({
      type: UserType,
      args: { id: t.arg({ type: 'ID' }) },
      resolve: async (_, args, ctx) => await ctx.db.users.findById(args.id),
    }),
  }),
});

// Mutation
builder.mutationType({
  fields: (t) => ({
    createUser: t.field({
      type: UserType,
      args: { input: t.arg({ type: CreateUserInput }) },
      resolve: async (_, { input }, ctx) => {
        const user = await ctx.db.users.insert(input);
        return user;
      },
    }),
    deleteUser: t.field({
      type: 'Boolean',
      args: { id: t.arg({ type: 'ID' }) },
      resolve: async (_, { id }, ctx) => {
        await ctx.db.users.delete(id);
        return true;
      },
    }),
  }),
});

const schema = builder.build();
```

### DataLoader para evitar N+1
```typescript
import { DataLoader } from '@jamx-framework/graphql';

// Crear DataLoader para cargar usuarios por IDs en batch
const userLoader = new DataLoader<string, User>(async (ids) => {
  // Esta función se llama una vez con todos los IDs necesarios
  const users = await db.users.findAll({ where: { id: ids } });
  const userMap = new Map(users.map(u => [u.id, u]));
  return ids.map(id => userMap.get(id) ?? null);
});

// En el resolver:
const userType = builder.objectType('User', {
  fields: (t) => ({
    id: t.field({ type: 'ID' }),
    name: t.field({ type: 'String' }),
    // Relación que usa DataLoader
    posts: t.field({
      type: ['Post'],
      resolve: async (user, _, ctx) => {
        // Esto se cachea automáticamente por usuario
        return await ctx.postLoader.load(user.id);
      },
    }),
  }),
});
```

### Paginación con cursor
```typescript
import { paginate, Connection, Edge } from '@jamx-framework/graphql';

// En el resolver:
const usersConnection: Connection<User> = await paginate(
  await db.users.findAll({ where: { active: true } }),
  { first: 10, after: cursor },
  (user) => user.id // cursor field
);

return {
  edges: usersConnection.edges.map(edge => ({
    cursor: edge.cursor,
    node: edge.node,
  })),
  pageInfo: {
    hasNextPage: usersConnection.hasNext,
    hasPrevPage: usersConnection.hasPrev,
    startCursor: usersConnection.edges[0]?.cursor,
    endCursor: usersConnection.edges[usersConnection.edges.length - 1]?.cursor,
  },
  totalCount: usersConnection.total,
};
```

### Integración con JAMX Container
```typescript
import { Container } from '@jamx-framework/core';
import { Executor } from '@jamx-framework/graphql';

// Registrar en contenedor
Container.registerSingleton('graphqlExecutor', () => {
  const schema = buildSchema(); // tu función que construye el schema
  const registry = new ResolverRegistry();
  // registrar resolvers...
  return new Executor(schema, registry);
});

// Usar en controlador
const executor = Container.resolve<Executor>('graphqlExecutor');
const result = await executor.execute(query, variables, context);
res.json(result);
```

## Flujo interno
1. **Schema Building**: `SchemaBuilder` procesa definiciones de tipos, queries y mutations
2. **Type Resolution**: Convierte tipos TypeScript a tipos GraphQL (String, Int, ID, Object, etc.)
3. **Resolver Registration**: `ResolverRegistry` asocia funciones a campos específicos
4. **Execution**: `Executor` recibe una query, la parsea, resuelve campos y ejecuta resolvers
5. **DataLoader**: Cachea requests de batch para evitar llamadas repetidas a BD
6. **Pagination**: Utilidades para convertir resultados a formato Connection de GraphQL
7. **Error Handling**: Los errores en resolvers se capturan y formatean según GraphQL spec

## API Reference (Resumen)
### SchemaBuilder
- `objectType(name, config)`: Define un tipo objeto
- `inputType(name, config)`: Define un tipo de input
- `interfaceType(name, config)`: Define una interfaz
- `enumType(name, values)`: Define un enum
- `unionType(name, types)`: Define una unión
- `queryType(config)`: Define el tipo Query raíz
- `mutationType(config)`: Define el tipo Mutation raíz
- `build()`: Construye el schema final

### Executor
- `constructor(schema, registry, context?)`
- `execute(query, variables?, context?)`: Ejecuta una query
- `executeSubscription(...)`: Ejecuta una suscripción (si está soportado)

### ResolverRegistry
- `register(fieldPath, resolver)`: Registra un resolver para un campo
- `get(fieldPath)`: Obtiene el resolver para un campo
- `clear()`: Limpia todos los resolvers

### DataLoader
- `load(key)`: Carga un dato por clave (batch automático)
- `loadMany(keys)`: Carga múltiples datos
- `clear(key)`: Limpia cache para una clave
- `clearAll()`: Limpia todo el cache

### Pagination
- `paginate<T>(data, options, cursorField)`: Pagina resultados
- `Connection<T>`, `Edge<T>`, `PageInfo`: Tipos de paginación

## Performance Considerations
- **DataLoader**: Reduce queries N+1 a 1 query batch
- **Resolver caching**: Los resultados de DataLoader se cachean por request
- **Schema validation**: El schema se valida una vez al construir, no en cada query
- **Lazy loading**: Los campos se resuelven solo cuando se solicitan
- **Batching**: DataLoader agrupa requests en un solo batch por tick de event loop

## Configuration Options
```typescript
const executor = new Executor(schema, registry, {
  // Contexto global para todos los resolvers
  db,
  cache,
  auth,
  
  // Límites
  maxDepth: 10,        // profundidad máxima de query
  maxComplexity: 1000, // complejidad máxima
  fieldLimit: 100,     // máximo campos por query
  
  // Formato de errores
  formatError: (err) => ({
    message: err.message,
    locations: err.locations,
    path: err.path,
  }),
});
```

## Testing
Tests en `packages/graphql/tests/unit/`:

```bash
pnpm test
```

Cubre:
- Construcción de schemas
- Resolución de campos
- DataLoader batch loading
- Paginación
- Manejo de errores
- Variables y fragments

## Compatibility
- Compatible con GraphQL SDL (Specification)
- Funciona con Node.js 18+
- Integra con cualquier base de datos a través de JAMX db
- Soporta subscriptions (parcialmente, requiere WebSocket)

## CLI Integration
- `jamx graphql:playground`: Abre GraphQL Playground en dev
- `jamx graphql:introspect`: Genera schema SDL
- `jamx graphql:validate <query>`: Valida una query contra el schema

## Best Practices
1. **Usar DataLoader** para relaciones y queries N+1
2. **Limitar profundidad** de queries para evitar DoS
3. **Validar queries** en producción (no ejecutar queries arbitrarias)
4. **Cachear resultados** de queries costosas
5. **Documentar schema** con descripciones en campos
6. **Usar paginación** para listas grandes
7. **Manejar errores** consistentemente en resolvers

This GraphQL module provides a powerful, type-safe way to build GraphQL APIs in JAMX applications, with built-in support for batching, pagination, and seamless integration with the rest of the framework.