Back

Step by Step Guide to Building a Linear API Integration in Go

Aug 13, 20246 minute read

Introduction

Hey there, fellow developer! Ready to dive into the world of Linear API integration with Go? You're in for a treat. Linear's API is a powerhouse for project management, and Go's simplicity makes this integration a breeze. Let's get cracking!

Prerequisites

Before we jump in, make sure you've got:

  • Go installed on your machine (you're a pro, so I'm sure you do)
  • A Linear account with an API key (if not, hop over to Linear and grab one)

Setting up the project

Let's kick things off by creating a new Go module:

mkdir linear-api-integration cd linear-api-integration go mod init github.com/yourusername/linear-api-integration

Now, let's grab the HTTP client we'll need:

go get github.com/go-resty/resty/v2

Authentication

Time to get that API key working for us. Create a new file called main.go and let's set up our client:

package main import ( "github.com/go-resty/resty/v2" ) const baseURL = "https://api.linear.app/graphql" func main() { client := resty.New() client.SetHeader("Authorization", "YOUR_API_KEY_HERE") client.SetHeader("Content-Type", "application/json") }

Making API requests

Linear uses GraphQL, so our requests will be a bit different from typical REST APIs. Let's fetch some issues:

func getIssues(client *resty.Client) { query := ` query { issues { nodes { id title state { name } } } } ` resp, err := client.R(). SetBody(map[string]string{"query": query}). Post(baseURL) if err != nil { panic(err) } fmt.Println(resp.String()) }

Handling responses

Now, let's parse that JSON response:

type IssueResponse struct { Data struct { Issues struct { Nodes []struct { ID string `json:"id"` Title string `json:"title"` State struct { Name string `json:"name"` } `json:"state"` } `json:"nodes"` } `json:"issues"` } `json:"data"` } var issueResp IssueResponse err = json.Unmarshal(resp.Body(), &issueResp) if err != nil { panic(err) } for _, issue := range issueResp.Data.Issues.Nodes { fmt.Printf("Issue: %s, State: %s\n", issue.Title, issue.State.Name) }

Implementing key Linear API features

Let's create an issue:

func createIssue(client *resty.Client, title string, description string) { mutation := fmt.Sprintf(` mutation { issueCreate(input: {title: "%s", description: "%s"}) { success issue { id title } } } `, title, description) resp, err := client.R(). SetBody(map[string]string{"query": mutation}). Post(baseURL) if err != nil { panic(err) } fmt.Println(resp.String()) }

Pagination and rate limiting

Linear uses cursor-based pagination. Here's how to handle it:

func getIssuesWithPagination(client *resty.Client, cursor string) { query := fmt.Sprintf(` query { issues(first: 10, after: "%s") { pageInfo { hasNextPage endCursor } nodes { id title } } } `, cursor) // Make the request and handle the response as before }

For rate limiting, Linear is pretty generous, but it's good practice to add a small delay between requests:

time.Sleep(100 * time.Millisecond)

Testing the integration

Don't forget to test! Here's a simple example:

func TestGetIssues(t *testing.T) { client := setupTestClient() issues := getIssues(client) assert.NotEmpty(t, issues, "Issues should not be empty") }

Best practices and optimization

  • Cache frequently accessed data to reduce API calls
  • Use GraphQL to request only the fields you need
  • Implement exponential backoff for retries on failures

Conclusion

And there you have it! You've just built a solid Linear API integration in Go. Remember, this is just scratching the surface - Linear's API is packed with features for you to explore.

Keep coding, keep learning, and most importantly, have fun with it! If you hit any snags, the Linear API docs are your best friend. Now go forth and build something awesome!