A Doc full of {{Client}} and {{Amount}} placeholders, a sheet row with those headers, and a Drive folder for the finished copies. fillDocTemplate(templateId, folder, rowObj) copies the template into the folder, replaces every {{key}} with the matching value from the row object (blank if the key is missing), and returns the new file's URL.
When to use
- One filled Doc per sheet row: invoices, offer letters, client kickoffs
- A shared template that non-coders edit in Docs, while the script only fills values
- Writing the resulting link back into the sheet so the row points at its Doc
How to use this snippet
/**
* Copy a Docs template into a folder and fill {{placeholders}} from a row object.
* Missing keys become blank. Returns the new file's URL.
*
* @param {string} templateId Drive file id of the template Doc
* @param {Folder|string} folder destination Folder, or its id
* @param {Object} rowObj keys match placeholder names (header → value)
* @return {string} URL of the filled copy
*/
function fillDocTemplate(templateId, folder, rowObj) {
rowObj = rowObj || {};
var folderObj = typeof folder === 'string' ? DriveApp.getFolderById(folder) : folder;
var copy = DriveApp.getFileById(templateId).makeCopy(folderObj);
var doc = DocumentApp.openById(copy.getId());
var body = doc.getBody();
var keys = {};
var text = body.getText();
var re = /\{\{\s*([^{}]+?)\s*\}\}/g;
var m;
while ((m = re.exec(text)) !== null) keys[m[1].trim()] = true;
Object.keys(keys).forEach(function (key) {
var val = Object.prototype.hasOwnProperty.call(rowObj, key) ? rowObj[key] : '';
if (val == null) val = '';
body.replaceText('\\{\\{\\s*' + key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\s*\\}\\}', String(val));
});
doc.saveAndClose();
return copy.getUrl();
}
Example
function fillInvoiceForRow(row) {
var folderId = PropertiesService.getScriptProperties().getProperty('INVOICE_FOLDER');
var url = fillDocTemplate('1abcTEMPLATE', folderId, row);
DriveApp.getFileById(extractDriveFileId(url))
.setName('Invoice ' + row.InvoiceId + ' — ' + row.Client);
return url;
}
// Template body: "Invoice {{InvoiceId}} for {{Client}}: ${{Amount}}"
// row = { InvoiceId: 'INV-9', Client: 'Ada', Amount: '1200' }
Tips:
- Placeholders are matched by exact key, with optional spaces inside the braces (
{{ Client }}works). Unknown keys write a blank, so typos in the template show up as empty spots instead of leftover{{Tyop}}. - The copy keeps Drive's default name (
Copy of …). Rename it after, from a column on the row, so filenames stay searchable. folderaccepts a Folder object or an id string. Pass the object when you're already iterating a folder tree.- Pair with
extractDriveFileIdwhen you need the id back from the URL tosetNameor share.
Tip: NitroGAS keeps fillDocTemplate in the snippet catalog. Paste your template's placeholder list and ask Co-Pilot to wire the sheet headers to the call.
Happy Coding!
