n8n GraphQL API Node: REST Endpoint Integration
I query GraphQL APIs from n8n because REST endpoints don’t give me the flexibility I need. GraphQL’s typed schema and precise field selection make it perfect for workflow data pipelines.
The HTTP Request node in n8n handles GraphQL queries through POST requests with JSON bodies. I construct queries as strings and send them with the appropriate authentication headers. This approach works with any GraphQL endpoint — whether it’s a public API or your own internal service.
GraphQL gives you something REST can’t: the ability to request exactly the data you need and nothing more. For n8n workflows that process large datasets, this precision matters. Fetching fewer fields means faster responses, lower bandwidth, and less memory consumption.
GraphQL Query Construction
The HTTP Request node sends GraphQL queries through the body parameter. I set the method to POST, the Content-Type to application/json, and construct the query string in the body.
`javascript
const query = `
query getUser($id: ID!) {
user(id: $id) {
id
name
email
createdAt
}
}
`;
return [{
json: {
query: query,
variables: { id: $(‘Trigger’).item.json.userId }
}
}];
`
The query string uses GraphQL’s template syntax. Variables are passed separately in the variables field. This separation keeps queries clean and prevents injection attacks. The n8n workflow fills in variable values from previous node outputs.
For paginated results, I use cursor-based pagination. The query includes first and after parameters. The response contains pageInfo with hasNextPage and endCursor. I loop through pages until hasNextPage is false.
`graphql
query getUsers($first: Int!, $after: String) {
users(first: $first, after: $after) {
edges {
node { id name email }
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
`
Mutation queries work the same way. I use mutations for create, update, and delete operations. Each mutation returns the modified data, which I pass to subsequent nodes in the workflow.
Authentication and Token Management
GraphQL APIs typically use JWT or OAuth2 for authentication. I store tokens in n8n credentials and inject them into the Authorization header.
`javascript
const credentials = await this.getCredentials(‘graphqlApi’);
return [{
json: {
url: credentials.endpoint,
headers: {
Authorization: Bearer ${credentials.token},
‘Content-Type’: ‘application/json’
},
body: {
query: ‘mutation { login(username: “…”, password: “…”) { token } }’
}
}
}];
`
Token refresh happens automatically through n8n’s credential rotation. When a token expires, the credential node requests a new one using the refresh token. The workflow never sees the expired token. This keeps authentication seamless.
For API key authentication, I pass the key through a custom X-API-Key header. Some GraphQL endpoints require this instead of JWT. Check the API documentation for the correct authentication method.
OAuth2 flows work differently. For the authorization code grant, I use n8n’s OAuth2 credential type. The workflow triggers the authorization URL, the user grants permission, and n8n exchanges the code for tokens automatically.
Response Parsing and Error Handling
GraphQL responses follow a standard structure: { data: {...}, errors: [...] }. I parse the data field and check for errors in the errors array.
`javascript
const response = $(‘HTTP Request’).item.json;
if (response.errors && response.errors.length > 0) {
console.error(‘GraphQL errors:’, response.errors);
throw new Error(‘GraphQL query failed: ‘ + response.errors[0].message);
}
return response.data;
`
Error handling distinguishes between client errors and server errors. Client errors include invalid queries, missing required fields, and permission denied. Server errors include 500 responses, timeouts, and database failures. Client errors require query fixes. Server errors require retry logic.
I validate response schemas using JSON Schema. If the response doesn’t match the expected structure, I catch it before downstream nodes process malformed data. This prevents cascading failures.
`javascript
// Schema validation
const Ajv = require(‘ajv’);
const ajv = new Ajv();
const schema = {
type: ‘object’,
properties: {
user: {
type: ‘object’,
properties: {
id: { type: ‘string’ },
name: { type: ‘string’ },
email: { type: ‘string’, format: ’email’ }
},
required: [‘id’, ‘name’, ’email’]
}
}
};
const valid = ajv.validate(schema, response.data);
if (!valid) {
console.error(‘Schema validation failed:’, ajv.errors);
}
`
For bulk operations, I process errors individually. If one record fails, I log it and continue with the rest. The error workflow node catches batch failures and sends notifications for records that need manual intervention.
Advanced Patterns
Batch queries let you fetch multiple resources in a single request. Instead of making N API calls for N records, you make one call with a batch query.
`graphql
query {
users(ids: [1, 2, 3, 4, 5]) {
id name email
}
}
`
Field aliasing lets you fetch the same field with different arguments. This is useful when you need user data in multiple formats or from different sources.
`graphql
query {
primaryUser: user(id: “123”) { name email }
secondaryUser: user(id: “456”) { name email }
}
`
Fragment reuse reduces query duplication. Define fragments for commonly accessed fields and include them in multiple queries. This makes queries shorter and easier to maintain.
`graphql
fragment UserFields on User {
id
name
email
avatar { url }
}
query {
user(id: “123”) {
…UserFields
createdAt
}
}
`
Action Card: Quick Setup
`bash
GraphQL query via HTTP Request node
POST /graphql
Headers: { Authorization: Bearer * Content-Type: application/json }
Body: { query: “
Test with curl
curl -X POST https://api.example.com/graphql \
-H “Authorization: Bearer YOUR_TOKEN” \
-H “Content-Type: application/json” \
-d ‘{“query”: “{ user(id: 1) { name email } }”}’
`
Connecting to Your Setup
The n8n Docker Deployment guide covers container setup for hosting GraphQL-dependent workflows. The n8n execution engine explains how HTTP nodes process external API responses. The n8n nodes reference lists all available integration nodes for API connectivity.
