Skip to content
Back to skills

Graphql

ASecurity

| Atributo | Valor | |----------|-------| | **ID** | `flutter-graphql` | | **Nivel** | πŸ”΄ Avanzado | | **VersiΓ³n** | 1.0.0 | | **Keywords** | `graphql`, `apollo`, `graphql-client`, `subscriptions` | | **Referencia** | [GraphQL Flutter](https://pub.dev/packages/graphql_flutter) |

  • 4 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 8, 2026
developmentapibackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add chimeranext/flutter-boilerplate-monorepo-template --skill graphql --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Graphql?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Graphql
[![Security: A β€” Skills Directory](https://www.skillsdirectory.com/api/skills/chimeranext-graphql/badge)](https://www.skillsdirectory.com/skills/chimeranext-graphql)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# πŸ”— Skill: GraphQL Integration

## πŸ“‹ Metadata

| Atributo | Valor |
|----------|-------|
| **ID** | `flutter-graphql` |
| **Nivel** | πŸ”΄ Avanzado |
| **VersiΓ³n** | 1.0.0 |
| **Keywords** | `graphql`, `apollo`, `graphql-client`, `subscriptions` |
| **Referencia** | [GraphQL Flutter](https://pub.dev/packages/graphql_flutter) |

## πŸ”‘ Keywords para InvocaciΓ³n

- `graphql`
- `apollo`
- `graphql-client`
- `subscriptions`
- `mutations`
- `@skill:graphql`

### Ejemplos de Prompts

```
Integra GraphQL para consultas y mutaciones
```

```
Implementa GraphQL con subscriptions en tiempo real
```

```
@skill:graphql - Configura cliente GraphQL con cache
```

## πŸ“– DescripciΓ³n

GraphQL Integration permite consultar APIs GraphQL con queries, mutations y subscriptions en tiempo real. Incluye cache automΓ‘tico, optimistic updates, paginaciΓ³n, manejo de errores y code generation.

### βœ… CuΓ‘ndo Usar Este Skill

- Backend usa GraphQL
- Necesitas queries flexibles
- Datos en tiempo real (subscriptions)
- Optimistic UI updates
- Cache inteligente del lado del cliente
- Reducir over-fetching/under-fetching

### ❌ CuÑndo NO Usar Este Skill

- Backend solo REST
- App muy simple
- Team no familiarizado con GraphQL

## πŸ—οΈ Estructura del Proyecto

```
lib/
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ graphql/
β”‚   β”‚   β”œβ”€β”€ graphql_client.dart
β”‚   β”‚   β”œβ”€β”€ graphql_config.dart
β”‚   β”‚   β”œβ”€β”€ graphql_link.dart
β”‚   β”‚   └── cache_config.dart
β”‚   └── utils/
β”‚       └── graphql_helpers.dart
β”‚
β”œβ”€β”€ features/
β”‚   └── products/
β”‚       β”œβ”€β”€ data/
β”‚       β”‚   β”œβ”€β”€ datasources/
β”‚       β”‚   β”‚   └── products_graphql_datasource.dart
β”‚       β”‚   β”œβ”€β”€ models/
β”‚       β”‚   β”‚   └── product_model.dart
β”‚       β”‚   └── repositories/
β”‚       β”‚       └── products_repository_impl.dart
β”‚       β”œβ”€β”€ domain/
β”‚       β”‚   β”œβ”€β”€ entities/
β”‚       β”‚   β”‚   └── product.dart
β”‚       β”‚   └── repositories/
β”‚       β”‚       └── products_repository.dart
β”‚       └── presentation/
β”‚           β”œβ”€β”€ bloc/
β”‚           β”‚   └── products_bloc.dart
β”‚           └── screens/
β”‚               └── products_screen.dart
β”‚
└── graphql/
    β”œβ”€β”€ queries/
    β”‚   β”œβ”€β”€ get_products.graphql
    β”‚   └── get_product.graphql
    β”œβ”€β”€ mutations/
    β”‚   β”œβ”€β”€ create_product.graphql
    β”‚   β”œβ”€β”€ update_product.graphql
    β”‚   └── delete_product.graphql
    └── subscriptions/
        └── product_updated.graphql
```

## πŸ“¦ Dependencias Requeridas

```yaml
dependencies:
  flutter:
    sdk: flutter

  # GraphQL Client
  graphql_flutter: ^5.1.2

  # Code Generation (opcional)
  gql: ^1.0.0
  ferry: ^0.15.0
  ferry_flutter: ^0.9.0

  # Utils
  equatable: ^2.0.5
  dartz: ^0.10.1

  # State Management
  flutter_bloc: ^8.1.3

dev_dependencies:
  # Code Generation
  build_runner: ^2.4.6
  ferry_generator: ^0.10.0
  gql_code_builder: ^0.7.0
```

### graphql/schema.graphql

```graphql
type Query {
  products(limit: Int, offset: Int): [Product!]!
  product(id: ID!): Product
  searchProducts(query: String!): [Product!]!
}

type Mutation {
  createProduct(input: CreateProductInput!): Product!
  updateProduct(id: ID!, input: UpdateProductInput!): Product!
  deleteProduct(id: ID!): Boolean!
}

type Subscription {
  productUpdated(id: ID!): Product!
  productCreated: Product!
}

type Product {
  id: ID!
  name: String!
  description: String!
  price: Float!
  imageUrl: String!
  stock: Int!
  createdAt: String!
  updatedAt: String!
}

input CreateProductInput {
  name: String!
  description: String!
  price: Float!
  imageUrl: String!
  stock: Int!
}

input UpdateProductInput {
  name: String
  description: String
  price: Float
  imageUrl: String
  stock: Int
}
```

## πŸ’» ImplementaciΓ³n

### 1. GraphQL Client Configuration

```dart
// lib/core/graphql/graphql_config.dart
import 'package:graphql_flutter/graphql_flutter.dart';

class GraphQLConfig {
  static const String _endpoint = 'https://api.example.com/graphql';
  static const String _wsEndpoint = 'wss://api.example.com/graphql';

  static HttpLink get httpLink => HttpLink(_endpoint);

  static WebSocketLink get websocketLink => WebSocketLink(
        _wsEndpoint,
        config: const SocketClientConfig(
          autoReconnect: true,
          inactivityTimeout: Duration(seconds: 30),
        ),
      );

  static AuthLink get authLink => AuthLink(
        getToken: () async {
          // Obtener token de autenticaciΓ³n
          // final token = await SecureStorage.getToken();
          return 'Bearer YOUR_TOKEN';
        },
      );

  static Link get link {
    final Link httpLinkWithAuth = authLink.concat(httpLink);

    // Combinar HTTP y WebSocket links
    return Link.split(
      (request) => request.isSubscription,
      websocketLink,
      httpLinkWithAuth,
    );
  }

  static GraphQLClient getClient() {
    return GraphQLClient(
      link: link,
      cache: GraphQLCache(
        store: HiveStore(),
      ),
      defaultPolicies: DefaultPolicies(
        query: Policies(
          fetch: FetchPolicy.cacheFirst,
          error: ErrorPolicy.all,
          cacheReread: CacheRereadPolicy.mergeOptimistic,
        ),
        mutate: Policies(
          fetch: FetchPolicy.networkOnly,
          error: ErrorPolicy.all,
        ),
        subscribe: Policies(
          fetch: FetchPolicy.networkOnly,
          error: ErrorPolicy.all,
        ),
      ),
    );
  }
}
```

### 2. GraphQL Queries

```graphql
# graphql/queries/get_products.graphql
query GetProducts($limit: Int, $offset: Int) {
  products(limit: $limit, offset: $offset) {
    id
    name
    description
    price
    imageUrl
    stock
    createdAt
    updatedAt
  }
}
```

```graphql
# graphql/queries/get_product.graphql
query GetProduct($id: ID!) {
  product(id: $id) {
    id
    name
    description
    price
    imageUrl
    stock
    createdAt
    updatedAt
  }
}
```

```graphql
# graphql/queries/search_products.graphql
query SearchProducts($query: String!) {
  searchProducts(query: $query) {
    id
    name
    description
    price
    imageUrl
    stock
  }
}
```

### 3. GraphQL Mutations

```graphql
# graphql/mutations/create_product.graphql
mutation CreateProduct($input: CreateProductInput!) {
  createProduct(input: $input) {
    id
    name
    description
    price
    imageUrl
    stock
    createdAt
    updatedAt
  }
}
```

```graphql
# graphql/mutations/update_product.graphql
mutation UpdateProduct($id: ID!, $input: UpdateProductInput!) {
  updateProduct(id: $id, input: $input) {
    id
    name
    description
    price
    imageUrl
    stock
    updatedAt
  }
}
```

```graphql
# graphql/mutations/delete_product.graphql
mutation DeleteProduct($id: ID!) {
  deleteProduct(id: $id)
}
```

### 4. GraphQL Subscriptions

```graphql
# graphql/subscriptions/product_updated.graphql
subscription ProductUpdated($id: ID!) {
  productUpdated(id: $id) {
    id
    name
    description
    price
    imageUrl
    stock
    updatedAt
  }
}
```

```graphql
# graphql/subscriptions/product_created.graphql
subscription ProductCreated {
  productCreated {
    id
    name
    description
    price
    imageUrl
    stock
    createdAt
  }
}
```

### 5. DataSource Implementation

```dart
// lib/features/products/data/datasources/products_graphql_datasource.dart
import 'package:graphql_flutter/graphql_flutter.dart';
import '../models/product_model.dart';

abstract class ProductsGraphQLDataSource {
  Future<List<ProductModel>> getProducts({int? limit, int? offset});
  Future<ProductModel> getProduct(String id);
  Future<List<ProductModel>> searchProducts(String query);
  Future<ProductModel> createProduct(ProductModel product);
  Future<ProductModel> updateProduct(ProductModel product);
  Future<bool> deleteProduct(String id);
  Stream<ProductModel> subscribeToProductUpdates(String id);
}

class ProductsGraphQLDataSourceImpl implements ProductsGraphQLDataSource {
  final GraphQLClient client;

  ProductsGraphQLDataSourceImpl(this.client);

  static const String _getProductsQuery = r'''
    query GetProducts($limit: Int, $offset: Int) {
      products(limit: $limit, offset: $offset) {
        id
        name
        description
        price
        imageUrl
        stock
        createdAt
        updatedAt
      }
    }
  ''';

  static const String _getProductQuery = r'''
    query GetProduct($id: ID!) {
      product(id: $id) {
        id
        name
        description
        price
        imageUrl
        stock
        createdAt
        updatedAt
      }
    }
  ''';

  static const String _searchProductsQuery = r'''
    query SearchProducts($query: String!) {
      searchProducts(query: $query) {
        id
        name
        description
        price
        imageUrl
        stock
      }
    }
  ''';

  static const String _createProductMutation = r'''
    mutation CreateProduct($input: CreateProductInput!) {
      createProduct(input: $input) {
        id
        name
        description
        price
        imageUrl
        stock
        createdAt
        updatedAt
      }
    }
  ''';

  static const String _updateProductMutation = r'''
    mutation UpdateProduct($id: ID!, $input: UpdateProductInput!) {
      updateProduct(id: $id, input: $input) {
        id
        name
        description
        price
        imageUrl
        stock
        updatedAt
      }
    }
  ''';

  static const String _deleteProductMutation = r'''
    mutation DeleteProduct($id: ID!) {
      deleteProduct(id: $id)
    }
  ''';

  static const String _productUpdatedSubscription = r'''
    subscription ProductUpdated($id: ID!) {
      productUpdated(id: $id) {
        id
        name
        description
        price
        imageUrl
        stock
        updatedAt
      }
    }
  ''';

  @override
  Future<List<ProductModel>> getProducts({int? limit, int? offset}) async {
    final result = await client.query(
      QueryOptions(
        document: gql(_getProductsQuery),
        variables: {
          'limit': limit,
          'offset': offset,
        },
        fetchPolicy: FetchPolicy.cacheFirst,
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    final List<dynamic> productsJson = result.data?['products'] ?? [];
    return productsJson
        .map((json) => ProductModel.fromJson(json))
        .toList();
  }

  @override
  Future<ProductModel> getProduct(String id) async {
    final result = await client.query(
      QueryOptions(
        document: gql(_getProductQuery),
        variables: {'id': id},
        fetchPolicy: FetchPolicy.cacheFirst,
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    final productJson = result.data?['product'];
    if (productJson == null) {
      throw Exception('Product not found');
    }

    return ProductModel.fromJson(productJson);
  }

  @override
  Future<List<ProductModel>> searchProducts(String query) async {
    final result = await client.query(
      QueryOptions(
        document: gql(_searchProductsQuery),
        variables: {'query': query},
        fetchPolicy: FetchPolicy.networkOnly,
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    final List<dynamic> productsJson = result.data?['searchProducts'] ?? [];
    return productsJson
        .map((json) => ProductModel.fromJson(json))
        .toList();
  }

  @override
  Future<ProductModel> createProduct(ProductModel product) async {
    final result = await client.mutate(
      MutationOptions(
        document: gql(_createProductMutation),
        variables: {
          'input': {
            'name': product.name,
            'description': product.description,
            'price': product.price,
            'imageUrl': product.imageUrl,
            'stock': product.stock,
          },
        },
        // Optimistic response
        optimisticResult: {
          'createProduct': product.toJson(),
        },
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    return ProductModel.fromJson(result.data?['createProduct']);
  }

  @override
  Future<ProductModel> updateProduct(ProductModel product) async {
    final result = await client.mutate(
      MutationOptions(
        document: gql(_updateProductMutation),
        variables: {
          'id': product.id,
          'input': {
            'name': product.name,
            'description': product.description,
            'price': product.price,
            'imageUrl': product.imageUrl,
            'stock': product.stock,
          },
        },
        optimisticResult: {
          'updateProduct': product.toJson(),
        },
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    return ProductModel.fromJson(result.data?['updateProduct']);
  }

  @override
  Future<bool> deleteProduct(String id) async {
    final result = await client.mutate(
      MutationOptions(
        document: gql(_deleteProductMutation),
        variables: {'id': id},
      ),
    );

    if (result.hasException) {
      throw Exception(result.exception.toString());
    }

    return result.data?['deleteProduct'] ?? false;
  }

  @override
  Stream<ProductModel> subscribeToProductUpdates(String id) {
    final subscription = client.subscribe(
      SubscriptionOptions(
        document: gql(_productUpdatedSubscription),
        variables: {'id': id},
      ),
    );

    return subscription.map((result) {
      if (result.hasException) {
        throw Exception(result.exception.toString());
      }

      return ProductModel.fromJson(result.data?['productUpdated']);
    });
  }
}
```

### 6. Usage in Widgets

```dart
// lib/features/products/presentation/screens/products_screen.dart
import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class ProductsScreen extends StatelessWidget {
  const ProductsScreen({super.key});

  static const String _getProductsQuery = r'''
    query GetProducts($limit: Int, $offset: Int) {
      products(limit: $limit, offset: $offset) {
        id
        name
        description
        price
        imageUrl
        stock
      }
    }
  ''';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products'),
      ),
      body: Query(
        options: QueryOptions(
          document: gql(_getProductsQuery),
          variables: const {
            'limit': 20,
            'offset': 0,
          },
          fetchPolicy: FetchPolicy.cacheAndNetwork,
          pollInterval: const Duration(seconds: 30),
        ),
        builder: (result, {fetchMore, refetch}) {
          // Loading state
          if (result.isLoading && result.data == null) {
            return const Center(child: CircularProgressIndicator());
          }

          // Error state
          if (result.hasException) {
            return Center(
              child: Text('Error: ${result.exception.toString()}'),
            );
          }

          // Success state
          final products = result.data?['products'] as List<dynamic>? ?? [];

          return RefreshIndicator(
            onRefresh: () async {
              await refetch?.call();
            },
            child: ListView.builder(
              itemCount: products.length,
              itemBuilder: (context, index) {
                final product = products[index];
                return ProductCard(
                  id: product['id'],
                  name: product['name'],
                  price: product['price'],
                  imageUrl: product['imageUrl'],
                );
              },
            ),
          );
        },
      ),
    );
  }
}
```

```dart
// Mutation Example
class CreateProductButton extends StatelessWidget {
  final ProductModel product;

  const CreateProductButton({required this.product});

  static const String _createProductMutation = r'''
    mutation CreateProduct($input: CreateProductInput!) {
      createProduct(input: $input) {
        id
        name
        price
      }
    }
  ''';

  @override
  Widget build(BuildContext context) {
    return Mutation(
      options: MutationOptions(
        document: gql(_createProductMutation),
        onCompleted: (dynamic resultData) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Product created!')),
          );
        },
        onError: (error) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('Error: $error')),
          );
        },
      ),
      builder: (runMutation, result) {
        return ElevatedButton(
          onPressed: result?.isLoading ?? false
              ? null
              : () {
                  runMutation({
                    'input': {
                      'name': product.name,
                      'description': product.description,
                      'price': product.price,
                      'imageUrl': product.imageUrl,
                      'stock': product.stock,
                    },
                  });
                },
          child: result?.isLoading ?? false
              ? const CircularProgressIndicator()
              : const Text('Create Product'),
        );
      },
    );
  }
}
```

```dart
// Subscription Example
class ProductDetailScreen extends StatelessWidget {
  final String productId;

  const ProductDetailScreen({required this.productId});

  static const String _productUpdatedSubscription = r'''
    subscription ProductUpdated($id: ID!) {
      productUpdated(id: $id) {
        id
        name
        price
        stock
        updatedAt
      }
    }
  ''';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Product Detail')),
      body: Subscription(
        options: SubscriptionOptions(
          document: gql(_productUpdatedSubscription),
          variables: {'id': productId},
        ),
        builder: (result) {
          if (result.isLoading) {
            return const Center(child: CircularProgressIndicator());
          }

          if (result.hasException) {
            return Center(child: Text('Error: ${result.exception}'));
          }

          final product = result.data?['productUpdated'];
          if (product == null) {
            return const Center(child: Text('No data'));
          }

          return Column(
            children: [
              Text('Name: ${product['name']}'),
              Text('Price: \$${product['price']}'),
              Text('Stock: ${product['stock']}'),
              Text('Updated: ${product['updatedAt']}'),
            ],
          );
        },
      ),
    );
  }
}
```

## 🎯 Mejores PrÑcticas

### 1. Fragment Reusability

```graphql
fragment ProductFields on Product {
  id
  name
  description
  price
  imageUrl
  stock
}

query GetProducts {
  products {
    ...ProductFields
  }
}
```

### 2. Optimistic Updates

```dart
optimisticResult: {
  'createProduct': {
    '__typename': 'Product',
    'id': 'temp-id',
    ...product.toJson(),
  },
}
```

### 3. Error Handling

```dart
if (result.hasException) {
  final exception = result.exception;
  if (exception?.graphqlErrors != null) {
    // Handle GraphQL errors
  }
  if (exception?.linkException != null) {
    // Handle network errors
  }
}
```

## πŸ“š Recursos Adicionales

- [GraphQL Flutter](https://pub.dev/packages/graphql_flutter)
- [GraphQL Official](https://graphql.org/)
- [Ferry Code Generator](https://ferrygraphql.com/)

## πŸ”— Skills Relacionados

- [Clean Architecture](../clean-architecture/SKILL.md)
- [Offline-First](../offline-first/SKILL.md)

---

**VersiΓ³n:** 1.0.0
**Última actualización:** Diciembre 2025

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in development β†’

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.