Skip to content

Commit 0afdfa6

Browse files
committed
Use GitHub module paths, generate graphqlclient into SDK, make config optional
Rename Go module paths from local names (gqlkit, gqlkit-sdl) to proper GitHub-importable paths (github.com/khanakia/gqlkit/gqlkit, etc.) so users can `go get` the generator library for programmatic use. Generate graphqlclient package into the SDK output alongside builder, making the generated SDK fully self-contained with no external deps. Make config.jsonc optional for both generate and generate-ts commands. Add --config/-c flag to Go generate and -o shorthand to gqlkit-sdl fetch.
1 parent 05820c7 commit 0afdfa6

23 files changed

Lines changed: 436 additions & 51 deletions

File tree

docs/getting-started-go.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,16 @@ gqlkit generate \
113113
| `--output` | `-o` | `./sdk` | Output directory for generated SDK |
114114
| `--package` | `-p` | `sdk` | Go package name |
115115
| `--module` | `-m` | | Go module path (e.g., `github.com/user/myapi`) |
116+
| `--config` | `-c` | | Path to `config.jsonc` file (optional) |
116117

117118
### Option B — Go script (for more control)
118119

120+
Install the generator library:
121+
122+
```bash
123+
go get github.com/khanakia/gqlkit/gqlkit/pkg/clientgen
124+
```
125+
119126
Create a Go file (e.g., `cmd/generate/main.go`) to invoke the generator programmatically:
120127

121128
```go
@@ -124,7 +131,7 @@ package main
124131
import (
125132
"fmt"
126133

127-
"gqlkit/pkg/clientgen"
134+
"github.com/khanakia/gqlkit/gqlkit/pkg/clientgen"
128135
)
129136

130137
func main() {
@@ -164,29 +171,22 @@ Either option creates the `sdk/` directory with the following packages:
164171

165172
```
166173
sdk/
167-
├── builder/ # Runtime builder types (included in SDK, no extra dependency needed)
168-
├── enums/ # GraphQL enums as Go types
169-
├── fields/ # Field selector types (one per GraphQL type)
170-
├── inputs/ # Input type structs
171-
├── mutations/ # Mutation builders + MutationRoot
172-
├── queries/ # Query builders + QueryRoot
173-
├── scalars/ # Custom scalar type aliases
174-
└── types/ # Go struct definitions for GraphQL types
174+
├── builder/ # Runtime builder types
175+
├── graphqlclient/ # HTTP client (NewClient, WithAuthToken, etc.)
176+
├── enums/ # GraphQL enums as Go types
177+
├── fields/ # Field selector types (one per GraphQL type)
178+
├── inputs/ # Input type structs
179+
├── mutations/ # Mutation builders + MutationRoot
180+
├── queries/ # Query builders + QueryRoot
181+
├── scalars/ # Custom scalar type aliases
182+
└── types/ # Go struct definitions for GraphQL types
175183
```
176184

177185
---
178186

179187
## Step 5: Use the generated SDK
180188

181-
Add the `gqlkit` runtime dependency:
182-
183-
```bash
184-
go get gqlkit/pkg/graphqlclient
185-
```
186-
187-
> **Note:** The `builder` package is already copied into your generated `sdk/builder/` directory — no need to install it separately.
188-
189-
Then use the SDK in your code:
189+
The generated SDK is fully self-contained — no external dependencies needed. The `builder` and `graphqlclient` packages are included in the generated output.
190190

191191
```go
192192
package main
@@ -196,7 +196,7 @@ import (
196196
"fmt"
197197
"log"
198198

199-
"gqlkit/pkg/graphqlclient"
199+
"yourmodule/sdk/graphqlclient"
200200

201201
"yourmodule/sdk/fields"
202202
"yourmodule/sdk/inputs"
@@ -350,7 +350,7 @@ results, err := builder.Execute(ctx)
350350
```go
351351
import (
352352
"errors"
353-
"gqlkit/pkg/graphqlclient"
353+
"yourmodule/sdk/graphqlclient"
354354
)
355355

356356
result, err := qr.Todos().Select(/* ... */).Execute(ctx)

example-go-mockapi/cmd/generate/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package main
33
import (
44
"fmt"
55

6-
"gqlkit/pkg/clientgen"
6+
"github.com/khanakia/gqlkit/gqlkit/pkg/clientgen"
77
)
88

99
func main() {

example-go-mockapi/cmd/samples/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"fmt"
88
"log"
99

10-
"gqlkit/pkg/graphqlclient"
10+
"example-go-mockapi/sdk/graphqlclient"
1111

1212
sdkbuilder "example-go-mockapi/sdk/builder"
1313
"example-go-mockapi/sdk/fields"

example-go-mockapi/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module example-go-mockapi
22

33
go 1.25.5
44

5-
require gqlkit v0.0.0
5+
require github.com/khanakia/gqlkit/gqlkit v0.0.0
66

77
require (
88
github.com/agnivade/levenshtein v1.2.1 // indirect
@@ -13,4 +13,4 @@ require (
1313
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
1414
)
1515

16-
replace gqlkit => ../gqlkit
16+
replace github.com/khanakia/gqlkit/gqlkit => ../gqlkit
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package graphqlclient
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
)
11+
12+
// Client sends GraphQL requests over HTTP to a single endpoint. It holds
13+
// persistent headers (e.g. auth tokens) and an optional custom http.Client.
14+
type Client struct {
15+
endpoint string
16+
httpClient *http.Client
17+
headers map[string]string
18+
}
19+
20+
// ClientOption is a functional option for configuring a Client at creation time.
21+
type ClientOption func(*Client)
22+
23+
// NewClient creates a new GraphQL client pointed at the given endpoint.
24+
func NewClient(endpoint string, opts ...ClientOption) *Client {
25+
c := &Client{
26+
endpoint: endpoint,
27+
httpClient: http.DefaultClient,
28+
headers: make(map[string]string),
29+
}
30+
for _, opt := range opts {
31+
opt(c)
32+
}
33+
return c
34+
}
35+
36+
// WithHTTPClient sets the HTTP client
37+
func WithHTTPClient(httpClient *http.Client) ClientOption {
38+
return func(c *Client) {
39+
c.httpClient = httpClient
40+
}
41+
}
42+
43+
// WithHeader adds a header to all requests
44+
func WithHeader(key, value string) ClientOption {
45+
return func(c *Client) {
46+
c.headers[key] = value
47+
}
48+
}
49+
50+
// WithHeaders adds multiple headers to all requests
51+
func WithHeaders(headers map[string]string) ClientOption {
52+
return func(c *Client) {
53+
for key, value := range headers {
54+
c.headers[key] = value
55+
}
56+
}
57+
}
58+
59+
// WithAuthToken adds an authorization bearer token
60+
func WithAuthToken(token string) ClientOption {
61+
return func(c *Client) {
62+
c.headers["Authorization"] = "Bearer " + token
63+
}
64+
}
65+
66+
type graphQLRequest struct {
67+
Query string `json:"query"`
68+
Variables map[string]interface{} `json:"variables,omitempty"`
69+
}
70+
71+
// GraphQLError represents a single error entry returned in a GraphQL response.
72+
type GraphQLError struct {
73+
Message string `json:"message"`
74+
Locations []GraphQLErrorLocation `json:"locations,omitempty"`
75+
Path []interface{} `json:"path,omitempty"`
76+
Extensions map[string]interface{} `json:"extensions,omitempty"`
77+
}
78+
79+
// GraphQLErrorLocation represents the location of a GraphQL error
80+
type GraphQLErrorLocation struct {
81+
Line int `json:"line"`
82+
Column int `json:"column"`
83+
}
84+
85+
// Error implements the error interface
86+
func (e GraphQLError) Error() string {
87+
return e.Message
88+
}
89+
90+
type graphQLResponse struct {
91+
Data json.RawMessage `json:"data"`
92+
Errors []GraphQLError `json:"errors,omitempty"`
93+
}
94+
95+
// GraphQLErrors is a slice of GraphQLError that implements the error interface.
96+
type GraphQLErrors []GraphQLError
97+
98+
// Error implements the error interface
99+
func (e GraphQLErrors) Error() string {
100+
if len(e) == 0 {
101+
return ""
102+
}
103+
if len(e) == 1 {
104+
return e[0].Message
105+
}
106+
var msgs []string
107+
for _, err := range e {
108+
msgs = append(msgs, err.Message)
109+
}
110+
return fmt.Sprintf("multiple errors: %v", msgs)
111+
}
112+
113+
// Execute sends a GraphQL request to the configured endpoint.
114+
func (c *Client) Execute(ctx context.Context, query string, variables map[string]interface{}, result interface{}) error {
115+
reqBody := graphQLRequest{
116+
Query: query,
117+
Variables: variables,
118+
}
119+
120+
body, err := json.Marshal(reqBody)
121+
if err != nil {
122+
return fmt.Errorf("failed to marshal request: %w", err)
123+
}
124+
125+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint, bytes.NewReader(body))
126+
if err != nil {
127+
return fmt.Errorf("failed to create request: %w", err)
128+
}
129+
130+
req.Header.Set("Content-Type", "application/json")
131+
req.Header.Set("Accept", "application/json")
132+
133+
for key, value := range c.headers {
134+
req.Header.Set(key, value)
135+
}
136+
137+
resp, err := c.httpClient.Do(req)
138+
if err != nil {
139+
return fmt.Errorf("failed to execute request: %w", err)
140+
}
141+
defer resp.Body.Close()
142+
143+
respBody, err := io.ReadAll(resp.Body)
144+
if err != nil {
145+
return fmt.Errorf("failed to read response body: %w", err)
146+
}
147+
148+
if resp.StatusCode != http.StatusOK {
149+
return fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(respBody))
150+
}
151+
152+
var gqlResp graphQLResponse
153+
if err := json.Unmarshal(respBody, &gqlResp); err != nil {
154+
return fmt.Errorf("failed to unmarshal response: %w", err)
155+
}
156+
157+
if len(gqlResp.Errors) > 0 {
158+
return GraphQLErrors(gqlResp.Errors)
159+
}
160+
161+
if result != nil && gqlResp.Data != nil {
162+
if err := json.Unmarshal(gqlResp.Data, result); err != nil {
163+
return fmt.Errorf("failed to unmarshal data: %w", err)
164+
}
165+
}
166+
167+
return nil
168+
}
169+
170+
// RawQuery executes a GraphQL query and returns the raw JSON "data" payload.
171+
func (c *Client) RawQuery(ctx context.Context, query string, variables map[string]interface{}) (json.RawMessage, error) {
172+
var result struct {
173+
Data json.RawMessage `json:"data"`
174+
}
175+
176+
if err := c.Execute(ctx, query, variables, &result); err != nil {
177+
return nil, err
178+
}
179+
180+
return result.Data, nil
181+
}

example-go-mockapi/sdk/scalars/scalars.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example-ts/cmd/generate/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package main
33
import (
44
"fmt"
55

6-
"gqlkit/pkg/clientgents"
6+
"github.com/khanakia/gqlkit/gqlkit/pkg/clientgents"
77
)
88

99
func main() {

example-ts/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module example-ts
22

33
go 1.25.5
44

5-
require gqlkit v0.0.0
5+
require github.com/khanakia/gqlkit/gqlkit v0.0.0
66

77
require (
88
github.com/agnivade/levenshtein v1.2.1 // indirect
@@ -13,4 +13,4 @@ require (
1313
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
1414
)
1515

16-
replace gqlkit => ../gqlkit
16+
replace github.com/khanakia/gqlkit/gqlkit => ../gqlkit

gqlkit-sdl/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module gqlkit-sdl
1+
module github.com/khanakia/gqlkit/gqlkit-sdl
22

33
go 1.25.5
44

gqlkit-sdl/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"os"
66
"strings"
77

8-
"gqlkit-sdl/schema"
8+
"github.com/khanakia/gqlkit/gqlkit-sdl/schema"
99

1010
"github.com/spf13/cobra"
1111
)

0 commit comments

Comments
 (0)