> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Antony-Figueroa/my-evershop-app/llms.txt
> Use this file to discover all available pages before exploring further.

# GraphQL API

> Extend and query the EverShop GraphQL schema with custom types and resolvers

# GraphQL API

EverShop uses GraphQL for data fetching in React components. Extensions can add new types, extend existing types, and provide resolvers to fetch data from any source.

## What is GraphQL in EverShop?

GraphQL provides:

* Type-safe data fetching for React components
* Flexible queries that request exactly the data needed
* Extensible schema that can be augmented by any extension
* Automatic data loading for page components
* Resolver functions that can fetch from databases, APIs, or any data source

<Note>
  Every page component can export a `query` that automatically fetches data and passes it as props to the component.
</Note>

## Schema Files

GraphQL schemas are defined in `.graphql` files within extensions:

```plaintext theme={null}
extensions/[name]/src/graphql/types/[TypeName]/
├── TypeName.graphql        # Type definitions
└── TypeName.resolvers.js   # Resolver functions
```

## Creating GraphQL Types

<Steps>
  ### Define the GraphQL schema

  Create a `.graphql` file with your type definitions:

  ```graphql extensions/sample/src/graphql/types/Foo/Foo.graphql theme={null}
  type Foo {
    id: ID!
    name: String!
    description: String
  }

  type Query {
    foo(id: ID!): Foo
    foos: [Foo!]!
  }
  ```

  ### Create resolvers

  Implement the resolver functions:

  ```javascript extensions/sample/src/graphql/types/Foo/Foo.resolvers.js theme={null}
  const fooList = [
    { id: 1, name: 'Foo', description: 'This is a Foo object' },
    { id: 2, name: 'Bar', description: 'This is a Bar object' },
    { id: 3, name: 'Baz', description: 'This is a Baz object' }
  ];

  export default {
    Query: {
      foo: (root, { id }) => {
        return fooList.find((foo) => foo.id === id);
      },
      foos: () => {
        return fooList;
      }
    },
    Foo: {
      id: (foo) => foo.id,
      name: (foo) => foo.name,
      description: (foo) => foo.description
    }
  };
  ```

  ### Use in components

  Query the data in your page components:

  ```tsx extensions/sample/src/pages/frontStore/homepage/FooList.tsx theme={null}
  import React from 'react';

  type FooListProps = {
    foos?: {
      id: number;
      name: string;
      description: string;
    }[];
  };

  export default function FooList({ foos }: FooListProps) {
    return (
      <div className="foo-list container mx-auto px-4 py-8">
        <h2 className="font-bold text-center mb-8">Foo List</h2>
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          {foos?.map((foo) => (
            <div key={foo.id} className="bg-white rounded-lg shadow-md p-6">
              <h3 className="font-semibold mb-3">{foo.name}</h3>
              <p className="text-gray-600">{foo.description}</p>
            </div>
          ))}
        </div>
      </div>
    );
  }

  export const layout = {
    areaId: 'content',
    sortOrder: 30
  };

  export const query = `
    query Query {
      foos {
        id
        name
        description
      }
    }
  `;
  ```
</Steps>

## Extending Existing Types

You can add fields to types defined by other extensions or the core system:

```graphql extensions/productCatalog/src/graphql/types/ProductExtension/ProductExtension.graphql theme={null}
type Supplement {
  ingredients: String
  benefits: [String]
  presentation: String
  dosage: String
  warnings: String
  storage: String
}

type ProductExtension {
  supplement: Supplement
}

extend type Product {
  extension: ProductExtension
}
```

<Note>
  Use `extend type TypeName` to add fields to an existing type. The base type must be defined elsewhere (core or another extension).
</Note>

Resolvers for extended types:

```javascript extensions/productCatalog/src/graphql/types/ProductExtension/ProductExtension.resolvers.js theme={null}
export default {
  Product: {
    extension: (product) => {
      return {
        supplement: {
          ingredients: product.ingredients || null,
          benefits: product.benefits ? JSON.parse(product.benefits) : null,
          presentation: product.presentation || null,
          dosage: product.dosage || null,
          warnings: product.warnings || null,
          storage: product.storage || null
        }
      };
    }
  }
};
```

## Resolver Structure

Resolvers are organized by type and field:

```javascript theme={null}
export default {
  // Root Query resolvers
  Query: {
    foo: (root, args, context) => {
      // Fetch single foo by ID
    },
    foos: (root, args, context) => {
      // Fetch list of foos
    }
  },
  
  // Root Mutation resolvers
  Mutation: {
    createFoo: (root, args, context) => {
      // Create new foo
    }
  },
  
  // Type field resolvers
  Foo: {
    id: (parent) => parent.id,
    name: (parent) => parent.name,
    // Computed field
    fullDescription: (parent) => {
      return `${parent.name}: ${parent.description}`;
    }
  }
};
```

### Resolver Arguments

Every resolver receives four arguments:

```javascript theme={null}
(parent, args, context, info) => {
  // parent:  The result from the parent resolver
  // args:    Arguments passed to the field
  // context: Shared context (request, database, etc.)
  // info:    Query metadata (rarely used)
}
```

<CodeGroup>
  ```javascript Query Resolver theme={null}
  Query: {
    foo: (root, { id }, context) => {
      // root: Usually null for Query
      // id: Argument from query foo(id: "123")
      // context: { request, db, ... }
      return context.db.findFooById(id);
    }
  }
  ```

  ```javascript Field Resolver theme={null}
  Foo: {
    name: (parent) => {
      // parent: The Foo object from Query.foo
      return parent.name;
    }
  }
  ```

  ```javascript Nested Resolver theme={null}
  Foo: {
    author: (parent, args, context) => {
      // parent: The Foo object
      // Fetch related author
      return context.db.findUserById(parent.authorId);
    }
  }
  ```
</CodeGroup>

## Query Export in Components

Page components can export a GraphQL query that runs automatically:

```tsx theme={null}
export const query = `
  query Query {
    product(id: getContextValue("productId")) {
      productId
      name
      price
      description
    }
  }
`;
```

### Context Values

Use `getContextValue()` to access runtime values:

```graphql theme={null}
query Query {
  product(id: getContextValue("productId")) {
    name
  }
  
  user(id: getContextValue("userId")) {
    email
  }
}
```

### URL Generation

Get URLs for routes:

```graphql theme={null}
query Query {
  loginUrl: url(routeId: "login")
  productUrl: url(routeId: "product", params: { id: "123" })
}
```

## Real-World Example: Product Reviews

From the productReviews extension:

<CodeGroup>
  ```tsx Component theme={null}
  import React from 'react';

  interface Review {
    id: string;
    author: string;
    rating: number;
    comment: string;
    createdAt: string;
  }

  type ProductReviewsProps = {
    product: {
      productId: string;
      name: string;
    };
    reviews?: Review[];
    action?: string;
  };

  export default function ProductReviews({ 
    product, 
    reviews = [], 
    action 
  }: ProductReviewsProps) {
    const averageRating = reviews.length > 0
      ? reviews.reduce((sum, r) => sum + r.rating, 0) / reviews.length
      : 0;

    return (
      <div className="bg-[#F8FAF9] border border-[#E8F5E9] rounded-lg p-6 mt-6">
        <h3 className="text-xl font-bold text-[#2D5A3D] mb-6">
          Reseñas de Clientes
        </h3>
        
        {reviews.length > 0 && (
          <div className="mb-6">
            <span className="text-[#4A5568]">
              {averageRating.toFixed(1)} de 5 ({reviews.length} reseñas)
            </span>
          </div>
        )}
        
        <div className="space-y-4">
          {reviews.map((review) => (
            <div key={review.id} className="border-b border-[#E8F5E9] pb-4">
              <span className="font-medium text-[#2D5A3D]">{review.author}</span>
              <p className="text-[#4A5568] text-sm">{review.comment}</p>
            </div>
          ))}
        </div>
      </div>
    );
  }

  export const layout = {
    areaId: 'productPageBottom',
    sortOrder: 10
  };

  export const query = `
    query Query {
      product(id: getContextValue("productId")) {
        productId
        name
        reviews {
          id
          author
          rating
          comment
          createdAt
        }
      }
      action: url(routeId: "productReviews")
    }
  `;
  ```

  ```graphql Schema theme={null}
  type Review {
    id: ID!
    author: String!
    rating: Int!
    comment: String!
    createdAt: String!
  }

  extend type Product {
    reviews: [Review!]!
  }
  ```

  ```javascript Resolvers theme={null}
  export default {
    Product: {
      reviews: async (product, args, context) => {
        const { db } = context;
        
        const reviews = await db.query(
          'SELECT * FROM reviews WHERE product_id = $1 ORDER BY created_at DESC',
          [product.productId]
        );
        
        return reviews.rows.map(row => ({
          id: row.id,
          author: row.author_name,
          rating: row.rating,
          comment: row.comment,
          createdAt: row.created_at.toISOString()
        }));
      }
    }
  };
  ```
</CodeGroup>

## Scalar Types

GraphQL supports several scalar types:

| Type      | Description       | Example              |
| --------- | ----------------- | -------------------- |
| `ID`      | Unique identifier | `"123"`, `"abc-def"` |
| `String`  | Text value        | `"Hello World"`      |
| `Int`     | Integer number    | `42`, `-10`          |
| `Float`   | Decimal number    | `3.14`, `-0.5`       |
| `Boolean` | True or false     | `true`, `false`      |

### Non-null Types

Use `!` to mark fields as required:

```graphql theme={null}
type Product {
  id: ID!              # Required ID
  name: String!        # Required String
  description: String  # Optional String
  price: Float!        # Required Float
}
```

### Lists

Use `[]` for arrays:

```graphql theme={null}
type Product {
  tags: [String]        # Optional array of optional strings
  tags: [String]!       # Required array of optional strings
  tags: [String!]!      # Required array of required strings
}
```

## Mutations

Mutations modify data:

```graphql theme={null}
type Mutation {
  createReview(input: ReviewInput!): Review!
  updateReview(id: ID!, input: ReviewInput!): Review!
  deleteReview(id: ID!): Boolean!
}

input ReviewInput {
  author: String!
  rating: Int!
  comment: String!
}
```

With resolvers:

```javascript theme={null}
export default {
  Mutation: {
    createReview: async (root, { input }, context) => {
      const { db } = context;
      
      const result = await db.query(
        'INSERT INTO reviews (author_name, rating, comment) VALUES ($1, $2, $3) RETURNING *',
        [input.author, input.rating, input.comment]
      );
      
      return {
        id: result.rows[0].id,
        author: result.rows[0].author_name,
        rating: result.rows[0].rating,
        comment: result.rows[0].comment,
        createdAt: result.rows[0].created_at.toISOString()
      };
    }
  }
};
```

## Best Practices

<Accordion title="Keep resolvers focused">
  Each resolver should do one thing well:

  ```javascript theme={null}
  // Good: Simple, focused resolver
  Query: {
    products: (root, args, context) => {
      return context.db.getAllProducts();
    }
  }

  // Avoid: Resolver doing too much
  Query: {
    products: async (root, args, context) => {
      const products = await context.db.getAllProducts();
      const reviews = await context.db.getAllReviews();
      const categories = await context.db.getAllCategories();
      // ... complex data manipulation
      return complexResult;
    }
  }
  ```
</Accordion>

<Accordion title="Use TypeScript for type safety">
  Define types for your GraphQL data:

  ```typescript theme={null}
  interface Foo {
    id: string;
    name: string;
    description?: string;
  }

  const fooList: Foo[] = [
    { id: '1', name: 'Foo', description: 'A foo object' }
  ];
  ```
</Accordion>

<Accordion title="Avoid N+1 queries">
  Use DataLoader or batch queries to prevent performance issues:

  ```javascript theme={null}
  // Bad: N+1 query problem
  Product: {
    reviews: (product, args, context) => {
      // This runs once PER product!
      return context.db.getReviewsForProduct(product.id);
    }
  }

  // Better: Use DataLoader
  Product: {
    reviews: (product, args, context) => {
      return context.reviewLoader.load(product.id);
    }
  }
  ```
</Accordion>

<Accordion title="Document your schema">
  Add descriptions to types and fields:

  ```graphql theme={null}
  """A foo represents..."""
  type Foo {
    """Unique identifier for the foo"""
    id: ID!
    
    """Display name of the foo"""
    name: String!
    
    """Optional description providing more details"""
    description: String
  }
  ```
</Accordion>

## Testing GraphQL Queries

### Using GraphQL Playground

EverShop includes GraphQL Playground (usually at `http://localhost:3000/graphql`):

```graphql theme={null}
query GetFoos {
  foos {
    id
    name
    description
  }
}

query GetFoo {
  foo(id: "1") {
    id
    name
    description
  }
}

mutation CreateReview {
  createReview(input: {
    author: "John Doe"
    rating: 5
    comment: "Great product!"
  }) {
    id
    author
    rating
  }
}
```

### Using curl

```bash theme={null}
curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ foos { id name } }"
  }'
```

## Next Steps

* Learn about [Extensions](/concepts/extensions) for organizing your GraphQL code
* Understand [Routing](/concepts/routing) for REST API alternatives
* Explore [Themes](/concepts/themes) for using GraphQL in page components
