npm package: @sparklink-pro/apant-codegen
Description: Apant graphql code generator based on graphql-code-generator
Author: Sparklink
Latest version: 2.0.1

# Apant Codegen

Opinionated GraphQL code generator for Apant - generates TypeScript types and TypedDocumentNode from your GraphQL schema.

## Install

```bash
npm install @sparklink-pro/apant-codegen
# or
yarn add @sparklink-pro/apant-codegen
```

## Migration from v1 to v2

**Breaking Change:** Version 2.0 removes generated hooks (e.g., `useBookListQuery`, `useActionCreateMutation`) in favor of Apollo Client's native hooks with TypedDocumentNode.

### Migration Steps

**1. First, run the Apollo Client codemod** (if upgrading from Apollo v3):

```bash
npx @apollo/client-codemod-migrate-3-to-4 --parser tsx --extensions tsx src
npx @apollo/client-codemod-migrate-3-to-4 --parser ts --extensions ts src
```

This updates Apollo Client imports from `@apollo/client` to `@apollo/client/react`.

**2. Then, run the apant migration tool:**

```bash
# Preview changes (dry-run)
npx apant-migrate --dry-run ./src

# Apply migration
npx apant-migrate ./src
```

### What the migration tool does

- Replaces generated hooks with native Apollo hooks:
  - `useBookListQuery()` → `useQuery(BookListDocument,`
  - `useActionCreateMutation()` → `useMutation(ActionCreateDocument,`
  - `useProjectLazyQuery()` → `useQuery(ProjectDocument,` (lazy queries become regular queries)
- Updates imports in your `gql` files:
  - Removes hook imports (e.g., `useBookListQuery`)
  - Adds Document imports (e.g., `BookListDocument`)

**Important:** If files had no existing `gql` import, the script adds `import { ... } from './gql'`. You may need to adjust the path (e.g., change `./gql` to `gap/gql` or `../gql`) depending on your project structure.

**Note:** The Apollo codemod handles updating Apollo imports to `@apollo/client/react`. Our tool focuses on replacing generated hooks with Documents.### Manual Migration

If you prefer to migrate manually, here's the pattern:

**Before (v1):**

```typescript
import { useBookListQuery, useBookCreateMutation } from './gql';

function BookList() {
    const { data, loading } = useBookListQuery();
    const [createBook] = useBookCreateMutation();
    // ...
}
```

**After (v2):**

```typescript
import { useQuery, useMutation } from '@apollo/client/react';
import { BookListDocument, BookCreateDocument } from './gql';

function BookList() {
    const { data, loading } = useQuery(BookListDocument);
    const [createBook] = useMutation(BookCreateDocument);
    // ...
}
```

### Why this change?

Following [Apollo Client v4 recommendations](https://www.apollographql.com/docs/react/data/queries/#usequery-api):

- Better type inference with TypedDocumentNode
- Reduced bundle size (no duplicate hook code)
- More flexible - use any Apollo hook feature directly
- Consistent with Apollo's best practices

## Usage

### Setup

Create `apant.config.js` at your project root:

```javascript
module.exports = {
    basePath: './gql', // Directory containing your GraphQL files
};
```

### Commands

```bash
# Generate code
npx apant-codegen

# Watch mode (auto-regenerate on changes)
npx apant-codegen --watch

# Migrate from v1 to v2
npx apant-migrate ./src
```

## Description

This plugin generate operations code and default type fragments in an opiniated way from a graphql schema and a document containing fragments.  
We identify two kinds of Object type: `Regular Object types` and `Operation payload types` (ie. The output type of an operation usually used only for the said operation).

A Type is considered as a Operation payload type, if it ends by `Payload`.

- The type `User` would be considered as a regular type.
- The type `BooksPayload` would be considered as an operation payload type.

The default fragment for a given type is `_<Type name>`.

- The default fragment for a type `User` would be `_User`.
- The default fragment for a type `BooksPayload` would be `_BooksPayload`.

The alias of the result of an operation is always `res`.

The generation consists of two steps:

- First, the generator generates default fragments if they don't exist for every regular object types.
- Second, the generator generates operations code for every defined operation in the schema.

### Step 1 : Fragments generation

For every type non identified as a regular type (ie. That doesn't end with `Payload`), the generator will generated a default fragment for that type.  
The generated fragment will include all scalar fields of the type but not the fields of the type's subtypes.  
If there is no scalar fields in the fragment, the fragment is skipped.  
The fragment who doesn't match a real type are removed.

### Step 2 : Operations code generation

For every operation defined in the schema, the generator will generate a query or mutation code based on if a fragment for the operation or the payload exists or not.  
If a fragment exists with the name of the operation, it will be used. If a fragment exists for the operation payload, it is used as resultset.
Otherwise, the resultset is the list of fields of the operation payload type.  
If the resultset is a list of fields, the generator will go one level deep to generate the fields selection.

## Examples

Given the following schema:

```graphql
# schema.graphql

type Book {
    id: ID!
    name: string
    rank: Int
}

type BooksPayload {
    books: [Books!]!
    count: Int!
}

query {
    TopBooks: BooksPayload
}
```

### Case 1 : The operation fragment exists

```graphql
# fragments.graphql

fragment TopBooks on BooksPayload {
  count
  books {
    id
    title
    rank
  }
}
...
```

The resulting query code will be:

```graphql
# operations.graphql

query TopBooks {
    res: TopBooks {
        ...TopBooks
    }
}
```

### Case 2 : The payload fragment exists

```graphql
# fragments.graphql

fragment _BooksPayload on BooksPayload {
  count
  books {
    id
    title
  }
}
...
```

The resulting query code will be:

```graphql
# operations.graphql

query TopBooks {
    res: TopBooks {
        ..._BooksPayload
    }
}
```

### Case 3 : Neither the operation fragment, nor the payload fragment exist

```graphql
# fragments.graphql
...
```

The resulting query code will be:

```graphql
# operations.graphql

query TopBooks {
    res: TopBooks {
        count
        books {
            ..._Book
        }
    }
}
```