Skip to content

Custom ContentService responses (e.g., XML/RSS) are not respected and get serialized to {} #101

@felipepmdias

Description

@felipepmdias

Hi! First of all, thank you again for your work on Boot.gs — it’s a very valuable project that significantly improves structure and developer experience in Google Apps Script.

I’m opening a few issues as I explore the framework more deeply, with the intention of contributing and helping improve it. If this becomes inconvenient or noisy in any way, please feel free to let me know and I’ll stop — no problem at all.


Problem

Currently, it is not possible to return custom response types beyond the predefined formats (JSON, text, HTML) when using HTTP controllers.

For example, when trying to return an XML (RSS) response using ContentService, the response is not delivered correctly. Instead, the client receives an empty object {}.

Example:

@Get('/feed/{id}')
public getPodcastFeed(@Param('id') id: string): GoogleAppsScript.Content.TextOutput {
  const xml = this.podcastService.getFeedFile(Number(id));

  return ContentService
    .createTextOutput(xml)
    .setMimeType(ContentService.MimeType.RSS);
}

Expected behavior:

  • Response body contains the XML
  • Content-Type: application/rss+xml

Actual behavior:

{}

🔍 Root Cause

From analyzing the current implementation, it seems that all controller return values are being passed through a response builder that serializes the result, regardless of its type.

The logic likely resembles something like:

return JSON.stringify(result);

or:

return ResponseBuilder.wrap(result);

This causes TextOutput (and potentially other native GAS response types) to be treated as plain objects and serialized incorrectly, resulting in {}.


Suggested Solution

Before applying any transformation or serialization, the framework should detect if the returned value is already a valid ContentService response and return it as-is.

Option 1 (explicit type check):

if (result instanceof ContentService.TextOutput) {
  return result;
}

Option 2 (more generic / flexible):

const isNativeGasObject = body && typeof body === 'object' && 
  ('setMimeType' in body || 'setTitle' in body || 'getContent' in body);

if (isNativeGasObject) {
  return body;
}

This approach is more robust and future-proof, as it allows handling different native GAS response types without tightly coupling to a specific class.


🚀 Why this matters

This change would enable support for:

  • RSS feeds (application/rss+xml)
  • XML APIs
  • File responses
  • Any custom MIME type supported by GAS

Without this, developers are limited to JSON/text/HTML responses, which restricts many valid use cases.


Final note

Again, thank you for building and maintaining this project — it’s genuinely very useful. I’m opening these issues in the spirit of collaboration and improvement.

Metadata

Metadata

Labels

documentationImprovements or additions to documentationenhancementNew feature or requestfeatureMajor new functionality or capabilitiesquestionFurther information is requested

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions