I need to connect my google sheet workflow through n8n

Hey Buddies , i have created a workflow in n8n , in that i have a google sheets node , that node i need to control with REST api so, 80% done , The issue i am facing is that i am unable to control document _mode through my restapi how can i do that

1 Like

Share more screenshot or the workflow might be helpful.

Can’t guess the real issue here.

This below is the screenshot of configuration i need to get in my sheets through a REST api

Api Reference: router.post(“/workflows/:id/add-google-sheets”, async (req, res) => {

try {

const id = req.params.id;

const {

nodeName = “Append to Google Sheets”,

documentUrl, // preferred: full URL from UI

documentId: documentUrlOrId, // legacy: URL or bare ID

range = “Sheet1!A:Z”,

googleCredentialId,

// we’ll force modern (v6) for Option A

headerMapping = { Date: “Date”, Name: “Name”, Mobile: “Mobile” }, // header → payload key

mapping = { A: “Date”, B: “Name”, C: “Mobile” }, // v4 fallback (A/B/C)

} = req.body || {};

// — helpers —

const isUrl = (s) => /^https?:\/\//i.test(String(s || “”));

const extractSheetId = (str) => {

if (!str) return “”;

const m = String(str).match(/\/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);

return m ? m[1] : String(str).trim();

};

const parseSheetNameFromRange = (rng) => {

const m = String(rng || “”).match(/^([^!]+)!/);

return m ? m[1] : “Sheet1”;

};

const rawDoc = documentUrl ?? documentUrlOrId;

if (!rawDoc) {

return res.status(400).json({ error: “documentUrl (or documentId) is required” });

}

const canonicalUrl = isUrl(rawDoc)

? String(rawDoc)

: `https://docs.google.com/spreadsheets/d/${extractSheetId(rawDoc)}/edit\`;

const sheetName = parseSheetNameFromRange(range);

// — load workflow & webhook —

const wf = await getWorkflow(id);

const webhookNode =

wf.nodes.find((n) => n.type === “n8n-nodes-base.webhook”) ||

wf.nodes.find((n) => n.name === “Webhook”);

if (!webhookNode) {

return res.status(400).json({ error: “No Webhook node found in this workflow” });

}

// — Option A: Modern UI, no list lookups (sheet by name) —

// Variant 1: nested objects (many builds accept this)

const paramsModernV1 = {

resource: “sheetWithinDocument”,

operation: “appendRow”,

document: { mode: “url”, value: canonicalUrl }, // Document → By URL

sheet: { mode: “name”, value: sheetName }, // Sheet → By name (e.g., “Sheet1”)

columnsMode: “mapEachColumnManually”,

columnsUi: {

column: Object.entries(headerMapping).map(([header, key]) => ({

column: header,

value: `={{$json[“${key}”]}}`,

    })),

  },

options: {},

};

// Variant 2: flattened keys (other builds prefer this)

const paramsModernV2 = {

resource: “sheetWithinDocument”,

operation: “appendRow”,

documentMode: “url”,

documentUrl: canonicalUrl,

sheetMode: “name”,

sheetName,

columnsMode: “mapEachColumnManually”,

columnsUi: {

column: Object.entries(headerMapping).map(([header, key]) => ({

column: header,

value: `={{$json[“${key}”]}}`,

    })),

  },

options: {},

};

// — v4 fallback (kept for resilience; targets Sheet1 via range) —

const paramsV4 = {

operation: “append”,

documentId: canonicalUrl, // URL works in many n8n builds; bare ID works as well

range, // e.g., “Sheet1!A:Z”

valueInputMode: “RAW”,

fieldsUi: {

field: Object.entries(mapping).map(([col, key]) => ({

column: col,

value: `={{$json[“${key}”]}}`,

    })),

  },

};

// — add/update node helper —

const isSheets = (n) => n.type === “n8n-nodes-base.googleSheets”;

const existingIdx = wf.nodes.findIndex(isSheets);

const willUpdate = existingIdx >= 0;

const baseNode = {

id: willUpdate ? wf.nodes[existingIdx].id : `${nodeName.replace(/\s+/g, “_”)}_${Date.now()}`,

name: willUpdate ? wf.nodes[existingIdx].name : nodeName,

type: “n8n-nodes-base.googleSheets”,

typeVersion: 6, // force modern for Option A

position: willUpdate

? wf.nodes[existingIdx].position

: [(webhookNode.position?.[0] ?? 260) + 300, webhookNode.position?.[1] ?? 300],

…(googleCredentialId

? { credentials: { googleSheetsOAuth2Api: { id: Number(googleCredentialId) } } }

: {}),

};

const putWithParams = async (parameters, forceTypeVersion) => {

const node = {

…baseNode,

…(forceTypeVersion ? { typeVersion: forceTypeVersion } : {}),

parameters,

  };

let nodes = wf.nodes.slice();

if (willUpdate) nodes[existingIdx] = { …wf.nodes[existingIdx], …node };

else nodes = […nodes, node];

const connections = { …(wf.connections || {}) };

const fromKey = webhookNode.name;

if (!connections[fromKey]) connections[fromKey] = { main: [] };

if (!connections[fromKey].main) connections[fromKey].main = [];

const targetName = willUpdate ? nodes[existingIdx].name : node.name;

const already = connections[fromKey].main[0].some((e) => e.node === targetName);

if (!already) connections[fromKey].main[0].push({ node: targetName, type: “main”, index: 0 });

const updated = await updateWorkflow(id, {

name: wf.name,

nodes,

connections,

settings: wf.settings || {},

staticData: wf.staticData || {},

  });

return { updated, targetName };

};

// Try modern (V1), then modern (V2); finally v4 as safety

let out, mode = “modern”;

try {

out = await putWithParams(paramsModernV1, 6);

} catch {

try {

out = await putWithParams(paramsModernV2, 6);

  } catch {

mode = “v4”;

out = await putWithParams(paramsV4, 4);

  }

}

return res.json({

workflowId: out.updated.id,

nodeName: out.targetName,

modeUsed: mode, // “modern” or “v4”

sheetName,

usedDocumentUrl: canonicalUrl,

});

} catch (e) {

console.error(e);

res.status(500).json({ error: String(e.message || e) });

}

});

HERE. , all configurations are getting except that document mode needs to select as By URL

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.