> ## 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 Mutations

> Learn how to create and use GraphQL mutations in EverShop

## Introduction

GraphQL mutations allow you to modify data in your EverShop application. Unlike queries which are read-only, mutations perform write operations such as creating, updating, or deleting data.

## Mutation Structure

Mutations in EverShop follow the standard GraphQL mutation syntax:

```graphql theme={null}
mutation MutationName {
  mutationField(argument: value) {
    field1
    field2
  }
}
```

## Defining Mutations

Mutations are defined in your GraphQL schema files alongside queries:

<CodeGroup>
  ```graphql Schema Definition theme={null}
  type Foo {
    id: ID!
    name: String!
    description: String
  }

  type Mutation {
    createFoo(name: String!, description: String): Foo
    updateFoo(id: ID!, name: String, description: String): Foo
    deleteFoo(id: ID!): Boolean
  }
  ```

  ```javascript Resolver Implementation theme={null}
  let fooList = [
    { id: 1, name: 'Foo', description: 'This is a Foo object' },
    { id: 2, name: 'Bar', description: 'This is a Bar object' }
  ];

  let nextId = 3;

  export default {
    Mutation: {
      createFoo: (root, { name, description }, context) => {
        const newFoo = {
          id: nextId++,
          name,
          description: description || null
        };
        fooList.push(newFoo);
        return newFoo;
      },
      
      updateFoo: (root, { id, name, description }, context) => {
        const foo = fooList.find(f => f.id === parseInt(id));
        if (!foo) {
          throw new Error(`Foo with id ${id} not found`);
        }
        
        if (name !== undefined) foo.name = name;
        if (description !== undefined) foo.description = description;
        
        return foo;
      },
      
      deleteFoo: (root, { id }, context) => {
        const index = fooList.findIndex(f => f.id === parseInt(id));
        if (index === -1) {
          throw new Error(`Foo with id ${id} not found`);
        }
        
        fooList.splice(index, 1);
        return true;
      }
    }
  };
  ```
</CodeGroup>

## Mutation Examples

### Create Mutation

Create a new resource in the system.

<ParamField body="name" type="String!" required>
  The name of the new Foo object
</ParamField>

<ParamField body="description" type="String">
  Optional description for the Foo object
</ParamField>

<ResponseField name="createFoo" type="Foo">
  Returns the newly created Foo object

  <Expandable title="Foo fields">
    <ResponseField name="id" type="ID!">
      Auto-generated unique identifier
    </ResponseField>

    <ResponseField name="name" type="String!">
      Name of the created object
    </ResponseField>

    <ResponseField name="description" type="String">
      Description if provided
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```graphql Mutation theme={null}
  mutation CreateNewFoo {
    createFoo(name: "New Foo", description: "A newly created object") {
      id
      name
      description
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "createFoo": {
        "id": "4",
        "name": "New Foo",
        "description": "A newly created object"
      }
    }
  }
  ```

  ```graphql With Variables theme={null}
  mutation CreateNewFoo($name: String!, $description: String) {
    createFoo(name: $name, description: $description) {
      id
      name
      description
    }
  }
  ```

  ```json Variables theme={null}
  {
    "name": "New Foo",
    "description": "A newly created object"
  }
  ```
</CodeGroup>

### Update Mutation

Update an existing resource.

<ParamField body="id" type="ID!" required>
  The ID of the Foo object to update
</ParamField>

<ParamField body="name" type="String">
  New name for the object (optional)
</ParamField>

<ParamField body="description" type="String">
  New description for the object (optional)
</ParamField>

<ResponseField name="updateFoo" type="Foo">
  Returns the updated Foo object
</ResponseField>

<CodeGroup>
  ```graphql Mutation theme={null}
  mutation UpdateFoo {
    updateFoo(id: "1", name: "Updated Foo") {
      id
      name
      description
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "updateFoo": {
        "id": "1",
        "name": "Updated Foo",
        "description": "This is a Foo object"
      }
    }
  }
  ```

  ```graphql Partial Update theme={null}
  mutation UpdateDescription {
    updateFoo(id: "1", description: "New description only") {
      id
      name
      description
    }
  }
  ```
</CodeGroup>

### Delete Mutation

Remove a resource from the system.

<ParamField body="id" type="ID!" required>
  The ID of the Foo object to delete
</ParamField>

<ResponseField name="deleteFoo" type="Boolean">
  Returns true if the deletion was successful
</ResponseField>

<CodeGroup>
  ```graphql Mutation theme={null}
  mutation DeleteFoo {
    deleteFoo(id: "3")
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "deleteFoo": true
    }
  }
  ```

  ```json Error Response theme={null}
  {
    "data": {
      "deleteFoo": null
    },
    "errors": [
      {
        "message": "Foo with id 999 not found",
        "path": ["deleteFoo"]
      }
    ]
  }
  ```
</CodeGroup>

## Input Types

For complex mutations, use input types to group related fields:

<CodeGroup>
  ```graphql Schema with Input Type theme={null}
  input CreateFooInput {
    name: String!
    description: String
    category: String
    tags: [String!]
  }

  type Mutation {
    createFoo(input: CreateFooInput!): Foo
  }
  ```

  ```graphql Usage theme={null}
  mutation CreateFoo($input: CreateFooInput!) {
    createFoo(input: $input) {
      id
      name
      description
    }
  }
  ```

  ```json Variables theme={null}
  {
    "input": {
      "name": "New Foo",
      "description": "Description here",
      "category": "example",
      "tags": ["tag1", "tag2"]
    }
  }
  ```
</CodeGroup>

## Mutation Context

Mutations have access to the same context object as queries:

```javascript theme={null}
export default {
  Mutation: {
    createFoo: (root, { name, description }, context) => {
      // Access authenticated user
      const userId = context.user?.id;
      
      // Access database connection
      const db = context.db;
      
      // Access request object
      const headers = context.request.headers;
      
      // Implementation
      return newFoo;
    }
  }
};
```

## Authorization

Implement authorization checks in your mutation resolvers:

```javascript theme={null}
export default {
  Mutation: {
    createFoo: (root, { name, description }, context) => {
      // Check if user is authenticated
      if (!context.user) {
        throw new Error('Authentication required');
      }
      
      // Check if user has permission
      if (!context.user.hasPermission('create_foo')) {
        throw new Error('Insufficient permissions');
      }
      
      // Proceed with mutation
      return createNewFoo(name, description);
    }
  }
};
```

## Validation

Validate input data before processing:

```javascript theme={null}
export default {
  Mutation: {
    createFoo: (root, { name, description }, context) => {
      // Validate required fields
      if (!name || name.trim().length === 0) {
        throw new Error('Name is required and cannot be empty');
      }
      
      // Validate length
      if (name.length > 100) {
        throw new Error('Name must be 100 characters or less');
      }
      
      // Validate format
      if (!/^[a-zA-Z0-9\s]+$/.test(name)) {
        throw new Error('Name can only contain letters, numbers, and spaces');
      }
      
      // Create the resource
      return createNewFoo(name, description);
    }
  }
};
```

## Database Integration

Integrate mutations with database operations:

```javascript theme={null}
export default {
  Mutation: {
    createFoo: async (root, { name, description }, context) => {
      const db = context.db;
      
      try {
        // Insert into database
        const result = await db.query(
          'INSERT INTO foos (name, description) VALUES ($1, $2) RETURNING *',
          [name, description]
        );
        
        return result.rows[0];
      } catch (error) {
        throw new Error(`Failed to create Foo: ${error.message}`);
      }
    },
    
    updateFoo: async (root, { id, name, description }, context) => {
      const db = context.db;
      
      const updates = [];
      const values = [];
      let paramIndex = 1;
      
      if (name !== undefined) {
        updates.push(`name = $${paramIndex++}`);
        values.push(name);
      }
      
      if (description !== undefined) {
        updates.push(`description = $${paramIndex++}`);
        values.push(description);
      }
      
      values.push(id);
      
      const query = `
        UPDATE foos 
        SET ${updates.join(', ')} 
        WHERE id = $${paramIndex}
        RETURNING *
      `;
      
      const result = await db.query(query, values);
      
      if (result.rows.length === 0) {
        throw new Error(`Foo with id ${id} not found`);
      }
      
      return result.rows[0];
    }
  }
};
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Descriptive Names" icon="signature">
    Name your mutations clearly: `createProduct`, `updateUser`, `deleteOrder`
  </Card>

  <Card title="Return Created Objects" icon="arrow-turn-down-left">
    Return the full object after creation/update for immediate use
  </Card>

  <Card title="Validate Input" icon="shield-check">
    Always validate input data before processing mutations
  </Card>

  <Card title="Handle Errors" icon="triangle-exclamation">
    Provide clear, actionable error messages
  </Card>

  <Card title="Use Transactions" icon="database">
    Wrap related database operations in transactions
  </Card>

  <Card title="Check Authorization" icon="lock">
    Verify user permissions before allowing mutations
  </Card>
</CardGroup>

## Error Handling

Mutations should provide clear error messages:

```javascript theme={null}
export default {
  Mutation: {
    createFoo: (root, { name, description }, context) => {
      try {
        // Validation
        if (!name) {
          throw new Error('Name is required');
        }
        
        // Business logic validation
        if (isDuplicateName(name)) {
          throw new Error(`A Foo with name "${name}" already exists`);
        }
        
        // Create the resource
        return createNewFoo(name, description);
      } catch (error) {
        // Log error for debugging
        console.error('Error creating Foo:', error);
        
        // Re-throw with user-friendly message
        throw error;
      }
    }
  }
};
```

## Testing Mutations

Test your mutations thoroughly:

```javascript theme={null}
// Example test
describe('createFoo mutation', () => {
  it('should create a new Foo', async () => {
    const result = await graphql({
      schema,
      source: `
        mutation {
          createFoo(name: "Test Foo", description: "Test description") {
            id
            name
            description
          }
        }
      `,
      contextValue: { user: mockUser }
    });
    
    expect(result.data.createFoo).toBeDefined();
    expect(result.data.createFoo.name).toBe('Test Foo');
  });
  
  it('should reject creation without authentication', async () => {
    const result = await graphql({
      schema,
      source: `
        mutation {
          createFoo(name: "Test Foo") {
            id
          }
        }
      `,
      contextValue: {}
    });
    
    expect(result.errors).toBeDefined();
    expect(result.errors[0].message).toContain('Authentication required');
  });
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="GraphQL Queries" icon="magnifying-glass" href="/api/graphql/queries">
    Learn about querying data with GraphQL
  </Card>

  <Card title="GraphQL Overview" icon="book" href="/api/graphql/overview">
    Review GraphQL architecture and concepts
  </Card>
</CardGroup>
