@superglue/cli 1.1.21 → 1.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -7
- package/dist/cli.js +307 -77
- package/dist/skills/superglue/SKILL.md +315 -631
- package/dist/skills/superglue/references/file-handling.md +180 -96
- package/dist/skills/superglue/references/graphql.md +131 -0
- package/dist/skills/superglue/references/http.md +251 -0
- package/dist/skills/superglue/references/integration.md +4 -2
- package/dist/skills/superglue/references/mssql.md +207 -0
- package/dist/skills/superglue/references/odbc.md +162 -0
- package/dist/skills/superglue/references/postgres.md +154 -0
- package/dist/skills/superglue/references/redis.md +68 -118
- package/dist/skills/superglue/references/sftp-smb.md +161 -0
- package/dist/skills/superglue/references/superglue-info.md +91 -0
- package/package.json +1 -1
- package/skills/superglue/SKILL.md +315 -631
- package/skills/superglue/references/file-handling.md +180 -96
- package/skills/superglue/references/graphql.md +131 -0
- package/skills/superglue/references/http.md +251 -0
- package/skills/superglue/references/integration.md +4 -2
- package/skills/superglue/references/mssql.md +207 -0
- package/skills/superglue/references/odbc.md +162 -0
- package/skills/superglue/references/postgres.md +154 -0
- package/skills/superglue/references/redis.md +68 -118
- package/skills/superglue/references/sftp-smb.md +161 -0
- package/skills/superglue/references/superglue-info.md +91 -0
- package/dist/skills/superglue/references/data-handling.md +0 -303
- package/dist/skills/superglue/references/databases.md +0 -253
- package/dist/skills/superglue/references/file-servers.md +0 -112
- package/dist/skills/superglue/references/http-apis.md +0 -248
- package/skills/superglue/references/data-handling.md +0 -303
- package/skills/superglue/references/databases.md +0 -253
- package/skills/superglue/references/file-servers.md +0 -112
- package/skills/superglue/references/http-apis.md +0 -248
|
@@ -1,160 +1,244 @@
|
|
|
1
1
|
# File Handling
|
|
2
2
|
|
|
3
|
-
How the runtime detects, parses, and exposes files across all protocol strategies (HTTP, FTP,
|
|
3
|
+
How the runtime detects, parses, and exposes files across all protocol strategies (HTTP, SFTP/FTP, SMB).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Step Configuration
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
File handling is not a standalone step type — it is a cross-cutting runtime behavior that applies to any step that produces or consumes files. The `file::<alias>.<suffix>` syntax is used in step config `body` / `content` fields to reference files from previous steps or user uploads.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
4. **25MB size fallback** (last resort) — only applies when byte detection returned `RAW` (unrecognized format), the response is larger than 25MB, **and** the `Content-Type` is not clearly text-like (`text/*`, `application/json`, `*+json`, `application/xml`, etc.). This is the only path where size matters.
|
|
9
|
+
```typescript
|
|
10
|
+
// Reference a file downloaded by a previous step
|
|
11
|
+
{ "body": "file::downloadStep.raw" }
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
// Reference a user-uploaded file
|
|
14
|
+
{ "body": '{"fileBase64": "file::document.base64"}' }
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| **Structured text** | JSON, XML, CSV, YAML, HTML | `data` only (parsed to JS objects) |
|
|
20
|
-
| **RAW** | Unrecognized | File if attachment/octet-stream/25MB fallback, else raw string |
|
|
16
|
+
// Reference a specific file from a multi-file step
|
|
17
|
+
{ "content": 'file::batchDownload["report.csv"].raw' }
|
|
18
|
+
```
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
### file:: Reference Syntax
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
Use `file::<alias>.<suffix>` in step config `body` / `content` fields:
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
| Suffix | Returns | Use case |
|
|
25
|
+
| ------------ | --------------------------- | --------------------------------------- |
|
|
26
|
+
| `.raw` | Original bytes (Uint8Array) | SFTP/FTP put, HTTP raw body upload |
|
|
27
|
+
| `.base64` | Base64-encoded string | APIs expecting base64 inside JSON/XML |
|
|
28
|
+
| `.extracted` | Parsed content | Text fields needing parsed file content |
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
| ----- | ------------------------------------------------------------- |
|
|
30
|
-
| JSON | Native JS object/array |
|
|
31
|
-
| CSV | Array of objects (header row = keys) |
|
|
32
|
-
| XML | JS object tree |
|
|
33
|
-
| HTML | JS object tree |
|
|
34
|
-
| YAML | Native JS object |
|
|
35
|
-
| Excel | Object keyed by sheet name → array of row objects (see below) |
|
|
36
|
-
| PDF | `{ textContent, structuredContent: [{ page, text }] }` |
|
|
37
|
-
| DOCX | Markdown string |
|
|
38
|
-
| ZIP | Object keyed by entry filename, each value recursively parsed |
|
|
39
|
-
| GZIP | Recursively parsed inner content |
|
|
40
|
-
| RAW | UTF-8 string fallback |
|
|
30
|
+
Constraints:
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
- Bare `file::<key>` without a suffix is invalid and causes a runtime error
|
|
33
|
+
- File references resolve only in body/content fields, not in headers, query params, or URLs
|
|
34
|
+
- Multi-file steps use bracket notation: `file::stepId["report.csv"].raw`
|
|
35
|
+
- Loop iterations use numeric index: `file::stepId[0].raw`
|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
## Authentication
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
- Access rows via `extracted.SheetName` or `extracted["Sheet1"]`, never `extracted[0]`.
|
|
48
|
-
- When producing xlsx, use namespace `http://schemas.openxmlformats.org/spreadsheetml/2006/main` (not the truncated path).
|
|
39
|
+
N/A — file handling is a runtime behavior, not a protocol. Authentication is handled by the protocol skill that produces or consumes the file (http, sftp-smb, etc.).
|
|
49
40
|
|
|
50
|
-
##
|
|
41
|
+
## File Handling Runtime Details
|
|
51
42
|
|
|
52
|
-
|
|
43
|
+
### Detection Priority
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
Every response is read as raw bytes. The runtime classifies it using this priority chain:
|
|
46
|
+
|
|
47
|
+
1. **Magic-byte detection** — known binary signatures (ZIP `PK\x03\x04`, PDF `%PDF`, GZIP `\x1f\x8b`, plus internal ZIP structure checks for Excel/DOCX)
|
|
48
|
+
2. **`Content-Disposition: attachment`** — `attachment` or `filename=` in this header forces file treatment
|
|
49
|
+
3. **`application/octet-stream`** — explicit octet-stream MIME catches unlabeled binary payloads
|
|
50
|
+
4. **25MB size fallback** — only when byte detection returned `RAW`, the response exceeds 25MB, and `Content-Type` is not text-like (`text/*`, `application/json`, `*+json`, `application/xml`)
|
|
59
51
|
|
|
60
|
-
|
|
52
|
+
File server `get` operations always produce files regardless of detected type.
|
|
61
53
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
### Classification
|
|
55
|
+
|
|
56
|
+
| Classification | Types | Destination |
|
|
57
|
+
| ------------------- | --------------------------- | -------------------------------------------------------------- |
|
|
58
|
+
| **Binary** | PDF, Excel, DOCX, ZIP, GZIP | `producedFiles` + `data` (extracted/parsed content) |
|
|
59
|
+
| **Structured text** | JSON, XML, CSV, YAML, HTML | `data` only (parsed to JS objects) |
|
|
60
|
+
| **RAW** | Unrecognized | File if attachment/octet-stream/25MB fallback, else raw string |
|
|
67
61
|
|
|
68
|
-
|
|
62
|
+
Binary responses expose `stepFileKeys` and populate `sourceData.__files__`. Structured text responses only populate `sourceData.stepId.data`.
|
|
69
63
|
|
|
70
|
-
|
|
64
|
+
### Auto-Parsing
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
| Type | Parsed result |
|
|
67
|
+
| ----- | -------------------------------------------------------------- |
|
|
68
|
+
| JSON | Native JS object/array |
|
|
69
|
+
| CSV | Array of objects (header row = keys) |
|
|
70
|
+
| XML | JS object tree |
|
|
71
|
+
| HTML | JS object tree |
|
|
72
|
+
| YAML | Native JS object |
|
|
73
|
+
| Excel | Object keyed by sheet name, each value is array of row objects |
|
|
74
|
+
| PDF | `{ textContent, structuredContent: [{ page, text }] }` |
|
|
75
|
+
| DOCX | Markdown string |
|
|
76
|
+
| ZIP | Object keyed by entry filename, each value recursively parsed |
|
|
77
|
+
| GZIP | Recursively parsed inner content |
|
|
78
|
+
| RAW | UTF-8 string fallback |
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
**Excel rules:**
|
|
76
81
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
82
|
+
- Excel is never a flat array. It is always `{ "SheetName": [ {row}, ... ] }` — even for single-sheet files
|
|
83
|
+
- Access rows via `extracted.SheetName` or `extracted["Sheet1"]`, never `extracted[0]`
|
|
84
|
+
- When producing xlsx, use namespace `http://schemas.openxmlformats.org/spreadsheetml/2006/main` (not the truncated path)
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
### File Aliasing
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
- one loop iteration download: `downloadReport[0]`
|
|
84
|
-
- one batch step returning multiple files: `downloadReport["summary.csv"]`
|
|
85
|
-
- one loop iteration returning multiple files: `downloadReport[0]["summary.csv"]`
|
|
88
|
+
When a step produces files, the runtime assigns an alias:
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
| Scenario | Alias format |
|
|
91
|
+
| --------------------------- | --------------------------- |
|
|
92
|
+
| Single file, non-loop | `stepId` |
|
|
93
|
+
| Single file, loop iteration | `stepId[N]` |
|
|
94
|
+
| Multiple files, non-loop | `stepId["filename.ext"]` |
|
|
95
|
+
| Multiple files, loop | `stepId[N]["filename.ext"]` |
|
|
88
96
|
|
|
89
|
-
Loop steps
|
|
97
|
+
Loop steps always use numeric indices — never filenames — even when each iteration fetches a named file. Filename brackets only appear when a single step or iteration returns multiple files in one response.
|
|
90
98
|
|
|
91
99
|
These aliases are used both in `file::` references and as keys in `sourceData.__files__`.
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
### stepFileKeys
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
Each step result exposes `stepFileKeys` — an array of runtime aliases for files produced by that step:
|
|
96
104
|
|
|
97
105
|
```javascript
|
|
98
|
-
sourceData.
|
|
99
|
-
sourceData.
|
|
106
|
+
sourceData.downloadStep.stepFileKeys; // ["downloadStep"]
|
|
107
|
+
sourceData.batchDownload[0].stepFileKeys; // ["batchDownload[0]"]
|
|
100
108
|
```
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
### Accessing Files in Transforms
|
|
111
|
+
|
|
112
|
+
Inside `transformCode` and `outputTransform`, use `sourceData.__files__` with the runtime alias. Treat `sourceData.__files__` as read-only — the runtime rebuilds it after every step.
|
|
105
113
|
|
|
106
114
|
```javascript
|
|
107
|
-
sourceData.__files__
|
|
115
|
+
sourceData.__files__.downloadStep; // single-file step
|
|
116
|
+
sourceData.__files__["stepId[0]"]; // loop iteration
|
|
108
117
|
```
|
|
109
118
|
|
|
110
|
-
|
|
119
|
+
Available properties on each file object: `filename`, `contentType`, `size`, `raw`, `extracted`, `fileType`, `parseError`.
|
|
120
|
+
|
|
121
|
+
**base64 access:** `base64` is a lazy getter — it is not stored on the file object by default. Access it directly:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
sourceData.__files__.myStep.base64; // computes and returns base64 string on first access
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The base64 value is computed on first access and cached for subsequent reads. It will not appear in `Object.keys()` or `JSON.stringify()` output (non-enumerable). Throws if the file exceeds 500MB — use `.raw` for large files.
|
|
128
|
+
|
|
129
|
+
Prefer discovering aliases dynamically via `stepFileKeys`:
|
|
111
130
|
|
|
112
131
|
```javascript
|
|
113
132
|
(sourceData) => {
|
|
114
133
|
return (sourceData.fetchThreeFiles.stepFileKeys || []).map(function (alias) {
|
|
115
134
|
var f = sourceData.__files__[alias];
|
|
116
|
-
return {
|
|
117
|
-
alias: alias,
|
|
118
|
-
filename: f ? f.filename : null,
|
|
119
|
-
size: f ? f.size : null,
|
|
120
|
-
};
|
|
135
|
+
return { alias: alias, filename: f ? f.filename : null, size: f ? f.size : null };
|
|
121
136
|
});
|
|
122
137
|
};
|
|
123
138
|
```
|
|
124
139
|
|
|
125
|
-
|
|
140
|
+
### Producing Files From Transforms
|
|
141
|
+
|
|
142
|
+
Return a `__files__` key from a transform to produce new files. The **key name is the filename** — content type is inferred from the extension. Values can be plain strings, bytes, or full file objects:
|
|
126
143
|
|
|
127
144
|
```javascript
|
|
145
|
+
// Simplest: key = filename, value = string content (auto-encoded to bytes)
|
|
128
146
|
(sourceData) => ({
|
|
129
|
-
summary: "processed",
|
|
130
147
|
__files__: {
|
|
131
|
-
"
|
|
132
|
-
filename: "output.csv",
|
|
133
|
-
contentType: "text/csv",
|
|
134
|
-
raw: new TextEncoder().encode("a,b\n1,2\n"),
|
|
135
|
-
},
|
|
148
|
+
"report.csv": Papa.unparse(sourceData.step1.data),
|
|
136
149
|
},
|
|
137
|
-
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Multiple files with different types
|
|
153
|
+
(sourceData) => ({
|
|
154
|
+
__files__: {
|
|
155
|
+
"data.csv": Papa.unparse(sourceData.step1.data),
|
|
156
|
+
"config.yaml": yaml.dump(sourceData.step1.data),
|
|
157
|
+
},
|
|
158
|
+
summary: { rowCount: sourceData.step1.data.length },
|
|
159
|
+
})
|
|
138
160
|
```
|
|
139
161
|
|
|
140
|
-
|
|
162
|
+
Supported value types for `__files__` entries:
|
|
141
163
|
|
|
142
|
-
|
|
164
|
+
- **String** — auto-encoded to UTF-8 bytes. Filename and content type from the key.
|
|
165
|
+
- **Uint8Array / ArrayBuffer** — used as-is. Filename and content type from the key.
|
|
166
|
+
- **Object with `raw`** — `raw` is normalized (string/bytes/array), `filename` defaults to key if missing, `contentType` inferred from key extension if missing.
|
|
167
|
+
- **Full file object** — `{ filename, contentType, raw }` for explicit control (backwards compatible).
|
|
143
168
|
|
|
144
|
-
|
|
145
|
-
- **String** → UTF-8 encoded. Pass a JSON string for structured data: `raw: JSON.stringify(data)`.
|
|
146
|
-
- **Object / array of objects** → automatically JSON-stringified to UTF-8 bytes. Works, but prefer explicit `JSON.stringify` with a `text/csv` or `application/json` contentType for clarity.
|
|
147
|
-
- **`null` / `undefined`** → produces an empty file (0 bytes).
|
|
148
|
-
- **`Uint8Array` / `ArrayBuffer` / `number[]`** → used as-is for binary content.
|
|
149
|
-
- For binary formats (xlsx, zip, pdf): pass through original bytes via `sourceData.__files__.stepId.raw` — never construct the internal file structure as a JS object.
|
|
169
|
+
Everything except `__files__` becomes the step's `data`. Single-file transforms get the step ID as alias; multi-file transforms use bracket notation.
|
|
150
170
|
|
|
151
|
-
|
|
171
|
+
**Available libraries in transforms:** The following libraries are available for file generation in any transform code (step transforms and output transforms):
|
|
152
172
|
|
|
153
|
-
|
|
173
|
+
- `Papa` — [papaparse](https://www.papaparse.com/). Use `Papa.unparse(rows)` to generate CSV from an array of objects. Handles quoting, escaping, and special characters automatically.
|
|
174
|
+
- `XLSX` — [SheetJS](https://sheetjs.com/). Use `XLSX.utils.json_to_sheet(data)` + `XLSX.write(wb, { type: "array", bookType: "xlsx" })` to generate Excel files.
|
|
175
|
+
- `yaml` — [js-yaml](https://github.com/nodeca/js-yaml). Use `yaml.dump(obj)` to generate YAML.
|
|
176
|
+
|
|
177
|
+
Example — CSV with Papa:
|
|
154
178
|
|
|
155
179
|
```javascript
|
|
156
|
-
sourceData
|
|
157
|
-
sourceData.
|
|
180
|
+
(sourceData) => ({
|
|
181
|
+
__files__: { "report.csv": Papa.unparse(sourceData.step1.data) },
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Auto-parsing of transform-produced files:** if `extracted` is omitted from the file object, the runtime runs the full detection and parsing pipeline on the raw bytes (same as for HTTP/file server responses). This means a CSV file produced with `raw: "a,b\n1,2"` will automatically get `extracted: [{a: "1", b: "2"}]`. To skip auto-parsing and keep the raw content as-is, set `extracted` explicitly (e.g. `extracted: null`).
|
|
186
|
+
|
|
187
|
+
**`raw` field rules:**
|
|
188
|
+
|
|
189
|
+
- `Uint8Array` / `ArrayBuffer` / typed arrays — used as-is for binary content
|
|
190
|
+
- String — UTF-8 encoded. Pass a JSON string for structured data: `raw: JSON.stringify(data)`
|
|
191
|
+
- Object / array — auto JSON-stringified to UTF-8 bytes. Prefer explicit `JSON.stringify` with an appropriate `contentType` for clarity
|
|
192
|
+
- `null` / `undefined` — produces an empty file (0 bytes)
|
|
193
|
+
- For binary formats (xlsx, zip, pdf): pass through original bytes via `sourceData.__files__.stepId.raw` — never construct the internal file structure as a JS object
|
|
194
|
+
|
|
195
|
+
### Returning Files as Tool Output
|
|
196
|
+
|
|
197
|
+
To make files downloadable from the tool's API response, set `outputFile: true` on the step that produces the file. The output transform is only for shaping JSON data — it has nothing to do with files.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"steps": [
|
|
202
|
+
{
|
|
203
|
+
"id": "fetchData",
|
|
204
|
+
"config": { "type": "request", "url": "https://api.example.com/report" }
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "generateReport",
|
|
208
|
+
"config": {
|
|
209
|
+
"type": "transform",
|
|
210
|
+
"transformCode": "(sourceData) => ({ __files__: { \"report.csv\": Papa.unparse(sourceData.fetchData.data) } })"
|
|
211
|
+
},
|
|
212
|
+
"outputFile": true
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
"outputTransform": "(sourceData) => ({ rowCount: sourceData.fetchData.data.length })"
|
|
216
|
+
}
|
|
158
217
|
```
|
|
159
218
|
|
|
160
|
-
|
|
219
|
+
Only steps with `outputFile: true` have their files stored to S3 and returned as downloadable artifacts. Steps without the flag keep their files internal (for inter-step passing only).
|
|
220
|
+
|
|
221
|
+
This works on any step type — transform steps, HTTP steps, SFTP/FTP steps. Just set `outputFile: true`.
|
|
222
|
+
|
|
223
|
+
Use `sg run download <runId>` to download file artifacts from the CLI.
|
|
224
|
+
|
|
225
|
+
## Common Pitfalls
|
|
226
|
+
|
|
227
|
+
- Using bare `file::<key>` without `.raw`, `.base64`, or `.extracted` suffix — causes a runtime error
|
|
228
|
+
- Placing file references in headers or query params instead of body/content fields — they only resolve in body
|
|
229
|
+
- Expecting Excel parsed output to be a flat array — it is always `{ "SheetName": [...] }` even for single-sheet files
|
|
230
|
+
- Hardcoding bracket aliases like `sourceData.__files__["step[\"file.csv\"]"]` — prefer `stepFileKeys` or `Object.keys(sourceData.__files__)` to discover aliases dynamically
|
|
231
|
+
- Trying to construct binary file internals (xlsx, zip) as JS objects — always pass through original bytes via `.raw`
|
|
232
|
+
- Accessing `sourceData.__files__` in step configs — `file::` syntax is for step configs, `sourceData.__files__` is for transform code
|
|
233
|
+
- Manually base64-encoding file content with `btoa()` — never do this. Return raw bytes (Uint8Array) or strings directly. The runtime handles encoding automatically. Use `XLSX.write(wb, { type: "array" })` not `type: "buffer"` followed by manual btoa.
|
|
234
|
+
- Putting file logic in the output transform — the output transform is for JSON data only. Use `outputFile: true` on the step instead.
|
|
235
|
+
|
|
236
|
+
## Error Recovery
|
|
237
|
+
|
|
238
|
+
When a file-related step fails:
|
|
239
|
+
|
|
240
|
+
1. **File input failure** - Ask the user whether the file is present in the session state and verify whether you can access the file at all
|
|
241
|
+
2. **Check the file:: reference** — verify the alias matches a step that actually produces files. Use `stepFileKeys` on the producing step's result to see available aliases.
|
|
242
|
+
3. **Verify the suffix** — bare `file::<key>` without `.raw`, `.base64`, or `.extracted` always fails. Choose the suffix based on how the consuming step needs the data.
|
|
243
|
+
4. **Check the producing step** — if `stepFileKeys` is empty or undefined, the producing step may not have returned a file. Verify the response is binary or has `Content-Disposition: attachment` (for HTTP), or that the operation is `get` (for file servers).
|
|
244
|
+
5. **Check aliasing for loops** — loop iterations use numeric indices (`stepId[0]`), not filenames. If the alias doesn't match, run the producing step and inspect the result to see the actual aliases assigned.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# GraphQL
|
|
2
|
+
|
|
3
|
+
GraphQL APIs use HTTP POST under the hood. This skill covers the GraphQL-specific conventions; the http skill covers the underlying HTTP runtime behavior.
|
|
4
|
+
|
|
5
|
+
## Step Configuration
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
{
|
|
9
|
+
type: "request",
|
|
10
|
+
systemId: "my_graphql_api",
|
|
11
|
+
url: "<<my_graphql_api_url>>/graphql",
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: {
|
|
14
|
+
"Authorization": "Bearer <<my_graphql_api_access_token>>",
|
|
15
|
+
"Content-Type": "application/json"
|
|
16
|
+
},
|
|
17
|
+
body: '{"query": "query GetUser($id: ID!) { user(id: $id) { name email } }", "variables": {"id": "<<userId>>"}}'
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Fields
|
|
22
|
+
|
|
23
|
+
| Field | Required | Notes |
|
|
24
|
+
| ---------- | -------- | ------------------------------------------------------ |
|
|
25
|
+
| `url` | yes | GraphQL endpoint — typically `/graphql` |
|
|
26
|
+
| `method` | yes | Always `POST` |
|
|
27
|
+
| `headers` | yes | Must include `Content-Type: application/json` and auth |
|
|
28
|
+
| `body` | yes | JSON string with `query` and optional `variables` |
|
|
29
|
+
| `systemId` | no | Links system credentials and URL |
|
|
30
|
+
|
|
31
|
+
### Body Format
|
|
32
|
+
|
|
33
|
+
The body is always a JSON string with:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"query": "query { ... }",
|
|
38
|
+
"variables": { ... }
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For dynamic variables, use `<<>>` expressions:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"query": "query GetOrders($customerId: ID!, $limit: Int) { orders(customerId: $customerId, first: $limit) { id total } }",
|
|
47
|
+
"variables": {
|
|
48
|
+
"customerId": "<<customerId>>",
|
|
49
|
+
"limit": "<<(sourceData) => sourceData.pageSize || 50>>"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For complex variable objects, use a preceding transform step to build the variables, then reference with `<<(sourceData) => JSON.stringify(sourceData.prepStep.data)>>` as the entire body.
|
|
55
|
+
|
|
56
|
+
## Authentication
|
|
57
|
+
|
|
58
|
+
Same as HTTP — credentials are placed in headers using `<<systemId_credentialKey>>` syntax. Nothing is injected automatically.
|
|
59
|
+
|
|
60
|
+
## GraphQL Runtime Details
|
|
61
|
+
|
|
62
|
+
### Queries vs Mutations
|
|
63
|
+
|
|
64
|
+
- **Queries** (read-only): set `modify: false` on the step (this is the default)
|
|
65
|
+
- **Mutations** (write): set `modify: true`
|
|
66
|
+
|
|
67
|
+
Both use `method: "POST"` — do not rely on HTTP method to distinguish read vs write.
|
|
68
|
+
|
|
69
|
+
### Response Shape
|
|
70
|
+
|
|
71
|
+
GraphQL APIs return `{ "data": { ... }, "errors": [ ... ] }`. The superglue step result wraps this in the standard envelope:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
sourceData.stepId.data; // → { data: { user: { name: "Alice" } }, errors: [...] }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Access the actual data via `sourceData.stepId.data.data.user` (double `.data` — one for the envelope, one for the GraphQL response).
|
|
78
|
+
|
|
79
|
+
### Pagination
|
|
80
|
+
|
|
81
|
+
GraphQL APIs typically use cursor-based pagination (Relay-style). Use superglue's `cursorBased` pagination:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
{
|
|
85
|
+
body: '{"query": "query($cursor: String) { users(first: 50, after: $cursor) { edges { node { id name } } pageInfo { endCursor hasNextPage } } }", "variables": {"cursor": "<<cursor>>"}}',
|
|
86
|
+
pagination: {
|
|
87
|
+
type: "cursorBased",
|
|
88
|
+
pageSize: "50",
|
|
89
|
+
cursorPath: "data.users.pageInfo.endCursor",
|
|
90
|
+
stopCondition: "response.data.data.users.pageInfo.hasNextPage === false"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Note the `cursorPath` and `stopCondition` must account for the GraphQL `data` wrapper.
|
|
96
|
+
|
|
97
|
+
### Schema Discovery
|
|
98
|
+
|
|
99
|
+
Superglue automatically introspects GraphQL schemas during documentation fetching. When a system URL contains "graphql", introspection is the first strategy tried — the full schema is fetched and stored as system documentation.
|
|
100
|
+
|
|
101
|
+
Use `sg system search-docs` to query the stored schema for types, fields, and arguments before building tools. This is more reliable than manual introspection and works even when the API has introspection disabled for external callers.
|
|
102
|
+
|
|
103
|
+
If the schema is not available via `sg system search-docs` (e.g. the system was created without a GraphQL URL, or introspection was blocked), test manually with `sg system call`:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{ "query": "{ __typename }" }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If that succeeds, try a targeted introspection for the types you need:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{ "query": "{ __type(name: \"User\") { fields { name type { name kind } } } }" }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Common Pitfalls
|
|
116
|
+
|
|
117
|
+
- Forgetting `Content-Type: application/json` header — most GraphQL servers reject requests without it
|
|
118
|
+
- Accessing `sourceData.stepId.data.user` instead of `sourceData.stepId.data.data.user` — the GraphQL response has its own `data` wrapper inside the step envelope
|
|
119
|
+
- Building complex variable objects inline with multiple `<<>>` expressions — use a transform step to build the variables object
|
|
120
|
+
- Using `cursorPath` or `stopCondition` that doesn't account for the GraphQL `data` wrapper (e.g. `users.pageInfo` instead of `data.users.pageInfo`)
|
|
121
|
+
- Not setting `modify: true` on mutation steps
|
|
122
|
+
|
|
123
|
+
## Error Recovery
|
|
124
|
+
|
|
125
|
+
When a GraphQL step fails:
|
|
126
|
+
|
|
127
|
+
1. **Check GraphQL-level errors** — even 200 responses can contain `errors` in the response body. Check `sourceData.stepId.data.errors` for field-level or validation errors.
|
|
128
|
+
2. **Verify the endpoint works** — use `sg system call` with `{ "query": "{ __typename }" }` to confirm connectivity and auth before debugging the query itself.
|
|
129
|
+
3. **Check schema compatibility** — fields, types, and arguments change between API versions. Use `sg system search-docs` to check the auto-introspected schema first. If unavailable, use targeted `__type` queries or web search to verify the current schema.
|
|
130
|
+
4. **Verify variables match the query signature** — GraphQL type mismatches (e.g. passing a string where `ID!` is expected) cause validation errors. Check that variable types in the query match what you're passing.
|
|
131
|
+
5. **Check for auth scope issues** — some GraphQL APIs return partial data with `null` fields and errors when the token lacks required scopes for specific fields.
|