githubv4 Go Client Library

repository·main·Indexed 22 days ago

https://github.com/shurcool/githubv4

A Go client library for interacting with the GitHub GraphQL API v4. It provides type safety and performance by allowing developers to define queries and mutations using Go structs that map directly to the GraphQL schema, including support for GitHub Enterprise, scalar types, inline fragments, and pagination.

Tokens
16.6K
Snippets
31
Records
80
Agent score
79%

What's inside githubv4

  1. Use GitHub GraphQL scalar types

    main

    The githubv4 package provides specific Go types for every scalar in the GitHub GraphQL schema (e.g., githubv4.String, githubv4.DateTime, githubv4.Boolean, githubv4.ID, githubv4.ReactionContent).

    Alternatively, because the library respects encoding/json rules, you can use standard Go types like string, time.Time, or bool. The library will unmarshal the JSON response into these types automatically.

  2. Execute GraphQL mutations

    main

    Mutations are executed using client.Mutate. You define a struct representing the mutation response and pass the input object (which must match the expected GraphQL input type) as the third argument.

    var m struct {
    	AddReaction struct {
    		Reaction struct {
    			Content githubv4.ReactionContent
    		} 
    		Subject struct {
    			ID githubv4.ID
    		} 
    	} `graphql:"addReaction(input: $input)"` 
    }
    
    // Define the input using the provided githubv4 input type
    input := githubv4.AddReactionInput{
    	SubjectID: targetIssue.ID,
    	Content:   githubv4.ReactionContentHooray,
    }
    
    // Execute mutation
    err := client.Mutate(context.Background(), &m, input, nil)
  3. Handle inline fragments in queries

    main

    To handle GraphQL inline fragments (e.g., ... on Organization), use the graphql struct tag on a field within your query struct. You can either define the fragment fields directly in the parent struct or use embedded structs for better organization.

    type ( 
    	OrganizationFragment struct {
    		Description string
    	} 
    	UserFragment struct {
    		Bio string
    	} 
    )
    
    var q struct {
    	RepositoryOwner struct {
    		Login                string
    		OrganizationFragment `graphql:"... on Organization"` 
    		UserFragment         `graphql:"... on User"` 
    	} `graphql:"repositoryOwner(login: \"github\")"` 
    }
    
    // client.Query(ctx, &q, nil)
  4. Authenticate with GitHub GraphQL API v4

    main

    The githubv4 package does not handle authentication directly. You must provide an http.Client that performs authentication (e.g., using OAuth2) when creating a new client.

    For standard GitHub, use githubv4.NewClient(httpClient). For GitHub Enterprise, use githubv4.NewEnterpriseClient(endpoint, httpClient).

    import (
    	"context"
    	"os"
    	"github.com/shurcooL/githubv4"
    	"golang.org/x/oauth2"
    )
    
    func main() {
    	src := oauth2.StaticTokenSource(
    		&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
    	)
    	httpClient := oauth2.NewClient(context.Background(), src)
    
    	// For standard GitHub
    	client := githubv4.NewClient(httpClient)
    
    	// For GitHub Enterprise
    	// client := githubv4.NewEnterpriseClient(os.Getenv("GITHUB_ENDPOINT"), httpClient)
    	
    	// Use client...
    }
  5. Pass arguments and variables to queries

    main

    To pass dynamic arguments to a GraphQL field, use the graphql struct tag to specify the field and its arguments using variable syntax (e.g., $name). You must then provide a map[string]interface{} containing the variables, ensuring the values are converted to githubv4 scalar types.

    func fetchRepoDescription(ctx context.Context, client *githubv4.Client, owner, name string) (string, error) {
    	var q struct {
    		Repository struct {
    			Description string
    		} `graphql:"repository(owner: $owner, name: $name)"`
    	}
    
    	variables := map[string]interface{}{
    		"owner": githubv4.String(owner),
    		"name":  githubv4.String(name),
    	}
    
    	err := client.Query(ctx, &q, variables)
    	if err != nil {
    		return "", err
    	}
    	return q.Repository.Description, nil
    }
  6. Implement pagination

    main

    Pagination in githubv4 is typically handled by checking the PageInfo object in the GraphQL response. You can loop through pages by updating a cursor variable in your variables map using the EndCursor from the previous result.

    var q struct {
    	Repository struct {
    		Issue struct {
    			Comments struct {
    				Nodes    []comment
    				PageInfo struct {
    					EndCursor   githubv4.String
    					HasNextPage bool
    				}
    			} `graphql:"comments(first: 100, after: $commentsCursor)"`
    		} `graphql:"issue(number: $issueNumber)"`
    	} `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
    }
    
    variables := map[string]interface{}{
    	"repositoryOwner": githubv4.String(owner),
    	"repositoryName":  githubv4.String(name),
    	"issueNumber":     githubv4.Int(issue),
    	"commentsCursor":  (*githubv4.String)(nil), // Start with nil for the first page
    }
    
    for {
    	err := client.Query(ctx, &q, variables)
    	if err != nil {
    		break
    	}
    	// Process q.Repository.Issue.Comments.Nodes...
    
    	if !q.Repository.Issue.Comments.PageInfo.HasNextPage {
    		break
    	}
    	// Update cursor for next iteration
    	variables["commentsCursor"] = githubv4.NewString(q.Repository.Issue.Comments.PageInfo.EndCursor)
    }
  7. Use custom scalar types for outbound GraphQL queries

    main

    When constructing outbound GraphQL queries with githubv4, you must use the provided custom scalar types for specific GitHub API fields. While native Go types (like string or int) can be used for unmarshaling response data, these custom types are required for correctly encoding input arguments in your query structures.

    Common scalar types include:

    • ID: Base64 obfuscated unique identifier.
    • String: UTF-8 textual data.
    • Int: Signed 32-bit integers.
    • Float: IEEE 754 double-precision fractional values.
    • Boolean: true/false values.
    • Date / DateTime: ISO-8601 encoded dates.
    • GitObjectID / GitRefname: Git-specific identifiers.
    • URI: RFC compliant URIs.
    • HTML: Strings containing HTML code.
    • Base64String: Base64 encoded strings.
    // Example of using custom scalars in a query struct
    type MyQuery struct {
    	SomeField githubv4.String `graphql:"someField"
    "	SomeID    githubv4.ID     `graphql:"someId"
    }
    
    // Use the helper functions to create pointers for query arguments
    query := MyQuery{
    	SomeField: githubv4.String("hello"),
    	SomeID:    githubv4.ID("VXNlci0xMA=="),
    }
  8. Use generated Input types for mutations

    main

    The githubv4 package provides an Input interface and a collection of generated structs that represent GraphQL INPUT_OBJECT types. When performing mutations, you should use these generated structs to ensure type safety and correct JSON mapping.

    Generated input structs use json tags to match the GraphQL field names. Fields marked as NON_NULL in the schema are required in the Go struct, while others are optional and use the omitempty tag.

  9. Use generated Enum types

    main

    GraphQL ENUM types are generated as Go string types. Each enum value is provided as a constant prefixed with the type name to prevent collisions.

    Example Pattern: If a GraphQL enum is named Status with a value ACTIVE, the generated code will look like:

    type Status string
    const (StatusActive Status = "ACTIVE")
  10. Generate githubv4 types from GitHub GraphQL schema

    main

    The githubv4 package uses a code generation tool (gen.go) to create Go type definitions (Enums and Input Objects) directly from the GitHub GraphQL schema. This ensures that the Go types stay in sync with the actual GitHub API.

    To run the generator, you must have a valid GitHub personal access token available in your environment variables.

    Requirements:

    • A GITHUB_TOKEN environment variable must be set.
    • The tool fetches the schema from https://api.github.com/graphql.

    Generated Files:

    • enum.go: Contains Go string types and const blocks for all GraphQL ENUM types.
    • input.go: Contains Go struct definitions for all GraphQL INPUT_OBJECT types, implementing the Input interface.
    # Set your token
    export GITHUB_TOKEN=your_token_here
    
    # Run the generator (assuming it is built/run as a main package)
    go run gen.go
  11. Perform a simple GraphQL query

    main

    To execute a query, define a Go struct that mirrors the shape of the GraphQL response. Use the client.Query method, passing a pointer to your struct and a map of variables (if any).

    var query struct {
    	Viewer struct {
    		Login     githubv4.String
    		CreatedAt githubv4.DateTime
    	} `graphql:"viewer"` // Note: The tag is used for field arguments if needed
    }
    
    // For a simple query without arguments, the struct fields match the schema
    var query struct {
    	Viewer struct {
    		Login     string
    		CreatedAt string
    	}
    }
    
    err := client.Query(context.Background(), &query, nil)
    if err != nil {
    	// Handle error
    }
    fmt.Println("Login:", query.Viewer.Login)