npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bootgs

v1.9.2

Published

Boot Framework for Google Apps Script™ projects.

Readme

Project banner for Google Apps Script Boot Framework

Boot Framework for Google Apps Script™

Introduction

Boot.gs is a lightweight framework designed to help build structured Google Apps Script applications. It aims to bring familiar development patterns, such as decorators and dependency injection, to the Apps Script environment to aid in code organization.

Installation

Install the framework via npm:

npm install bootgs

Quick Start

1. Define a Controller

Create a class to handle your application's logic. Decorators make it easy to map methods to specific endpoints or events.

import {Get, RestController} from "bootgs";

@RestController("api/sheet")
export class SheetController {
    /**
     * Handles GET requests to /api/sheet/active-range
     */
    @Get("active-range")
    getActiveRange(): string {
        return "This action returns the active sheet range.";
    }
}

2. Initialize the Application

Bootstrap your application by creating an App instance and delegating the standard Apps Script entry points (doGet, doPost) to it.

[!IMPORTANT] The framework requires that you delegate these global entry points so it can intercept and route the incoming events.

Synchronous Application

Use App for synchronous execution:

import {App} from "bootgs";
import {SheetController} from "./SheetController";

/**
 * Global entry point for GET requests.
 */
export function doGet(event: GoogleAppsScript.Events.DoGet) {
    const app = App.create({
        controllers: [SheetController]
    });
    return app.doGet(event);
}

/**
 * Global entry point for POST requests.
 */
export function doPost(event: GoogleAppsScript.Events.DoPost) {
    const app = App.create({
        controllers: [SheetController]
    });
    return app.doPost(event);
}

Asynchronous Application

Use AsyncApp when you need to handle asynchronous operations (e.g., UrlFetchApp promises or other async tasks) in your controllers:

[!TIP] Only use AsyncApp if your controller methods are async or return a Promise. For standard synchronous tasks, the regular App is more lightweight.

import {AsyncApp} from "bootgs";
import {SheetController} from "./SheetController";

/**
 * Global entry point for GET requests.
 */
export async function doGet(event: GoogleAppsScript.Events.DoGet) {
    const app = AsyncApp.create({
        controllers: [SheetController]
    });
    return await app.doGet(event);
}

/**
 * Global entry point for POST requests.
 */
export async function doPost(event: GoogleAppsScript.Events.DoPost) {
    const app = AsyncApp.create({
        controllers: [SheetController]
    });
    return await app.doPost(event);
}

Features

  • Decorator-based Routing: Intuitive mapping of HTTP and Apps Script events (GET, POST, etc.).
  • Spring Boot & NestJS Patterns: Familiar decorators like @RequestMapping, @RestController, @ResponseBody, and the ResponseEntity class.
  • Flexible Responses: Full control over HTTP status codes, headers, and MIME types using ResponseEntity.
  • Validation: Declarative parameter validation using Spring Boot-style decorators like @Min, @Max, @Email, etc.
  • Pipes & Validation: Transform and validate incoming data with @UsePipes and built-in pipes (e.g., ParseNumberPipe).
  • Global Error Handling: Centralized exception management using @ControllerAdvice and @ExceptionHandler.
  • Dependency Injection: Fully-featured DI for better decoupling and testability.
  • Type Safety: Built with TypeScript for a robust development experience.
  • Modern Architecture: Inspired by frameworks like NestJS and Spring Boot.

Calling the API (Virtual Transport Layer)

The primary goal of Boot.gs is to ensure your code remains environment-agnostic. It should function identically whether it’s triggered via doGet/doPost or google.script.run.

Since Google Apps Script (GAS) has certain constraints on headers and routing, the framework implements a Virtual Transport Layer. This layer "tucks" your request metadata (like the HTTP method and path) into parameters so the framework handles the routing for you seamlessly.

Virtual Request Parameters

To simulate a standard HTTP request, you pass these key parameters to the framework:

  • method: Simulates the HTTP method (GET, POST, PUT, DELETE, etc.).
  • pathname (or path): The virtual resource path (e.g., /api/users/1).
  • headers: A JSON-stringified object containing request headers.

[!CAUTION] Never pass sensitive secrets in the headers object via query parameters! Since the Virtual Transport Layer passes all request metadata (including headers) via URL query parameters, you must never include sensitive information like API keys or Bearer tokens inside the headers object when calling the script via its Web App URL. URLs (and their query strings) are frequently logged in plain text. For sensitive data, always use the payload body of a POST request.

Supported Response Types (MIME Types)

The framework supports a variety of output formats. You can specify the desired format using the produces property in the @RequestMapping decorator (or its aliases like @Get, @Post) or by returning a ResponseEntity with a specific MIME type.

How to Call the API

1. Internal Usage (google.script.run)

Use this when building Sidebars, Modals, or Add-ons.

[!TIP] To receive a raw string (which is faster and easier to parse in client-side JS), include the X-Request-Source: internal header in your request.

Example (Client-side JS):

const path = "/api/users/123";
const method = "GET";
const headers = JSON.stringify({
  "X-Request-Source": "internal"
});

// Constructing the Virtual Transport Event
const event = {
  pathInfo: path,
  parameter: {
    method,
    pathname: path,
    headers
  },
  parameters: {
    method: [method],
    pathname: [path],
    headers: [headers]
  },
  queryString: `method=${method}&pathname=${encodeURIComponent(path)}&headers=${encodeURIComponent(headers)}`
};

google.script.run
  .withSuccessHandler((response) => {
    // Parse the optimized string response
    const result = typeof response === "string" ? JSON.parse(response) : response;

    console.log("Status:", result.status);
    console.log("Data:", result.body);
  })
  .doGet(event);

2. External Usage (Web App URL)

Use this when accessing the script via a direct link, a webhook, or a third-party service. This returns a standard GAS TextOutput or HtmlOutput.

Example Request URL: https://script.google.com/.../exec?method=GET&pathname=%2Fapi%2Fusers%2F123

Response Wrapping Logic

The framework automatically handles your controller's return value based on whether the @ResponseBody decorator is used (note that @RestController applies this by default):

A. Default Wrapper (No @ResponseBody)

If the controller method is not marked with @ResponseBody, the framework returns a full HTTP-like payload:

{
  "status": 200,
  "statusText": "OK",
  "ok": true,
  "headers": { "Content-Type": "application/json" },
  "body": { "id": 123, "name": "John Doe" }
}

B. Direct Context (@ResponseBody)

If the method is marked with @ResponseBody, the framework bypasses the payload wrapper and returns only the data directly.

[!TIP] Custom Axios Adapter Building those google.script.run payloads manually can be tedious. A custom Axios adapter specifically for GAS Web Apps is currently in development. It will completely abstract the virtual transport layer, allowing you to use standard axios.get() or axios.post() in your frontend.

[!NOTE] Added full support for XML, RSS, and other MIME types as requested!

Decorators

Class decorators

Method decorators

Parameter decorators

Built-in Pipes

Pipes can be used to transform data before it reaches your handler:

Controlling the Response

ResponseEntity

The ResponseEntity class provides a flexible way to build full HTTP responses, including status codes, headers, and MIME types.

import { Get, RestController, ResponseEntity, HttpStatus, ContentMimeType, Param } from "bootgs";

@RestController("users")
export class UserController {

    @Get("{id}")
    getUser(@Param("id") id: string): ResponseEntity {
        const user = { id, name: "John Doe" };

        if (!user) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }

        return ResponseEntity.ok()
            .header("X-Custom-Header", "Value")
            .body(user);
    }

    @Get({ path: "export", produces: ContentMimeType.CSV })
    exportData(): ResponseEntity<string> {
        const csvData = "id,name\n1,John Doe";
        return ResponseEntity.ok(csvData);
    }
}

ResponseBody

The @ResponseBody decorator indicates that the return value of a method should be bound directly to the response body, bypassing the default framework wrapper (which normally includes status, ok, and body fields in the JSON response).

[!NOTE] @RestController automatically applies @ResponseBody to all its methods.

import { Get, HttpController, ResponseBody } from "bootgs";

@HttpController("raw")
export class RawController {

    @Get("data")
    @ResponseBody()
    getRawData(): object {
        return { message: "This will be returned as the root JSON object" };
    }
}

Advanced Examples

Pipes

Transform parameters with pipes:

import {Get, RestController, Query, ParseNumberPipe} from "bootgs";

@RestController("users")
export class UserController {

    @Get("details")
    getUserDetails(@Query("id", ParseNumberPipe) id: number): object {
        return {
            userId: id,
            message: "Success!"
        };
    }
}

Global Error Handling

Use @ControllerAdvice to handle exceptions globally across the whole application:

import {ControllerAdvice, ExceptionHandler, ResponseStatus} from "bootgs";

@ControllerAdvice()
export class GlobalExceptionHandler {

    @ExceptionHandler(Error)
    @ResponseStatus(500)
    handleError(error: Error): object {
        return {
            status: "Error",
            message: error.message
        };
    }
}

Recommended

[!TIP] For enhanced development with Google Apps Script, we recommend using apps-script-utils, a collection of utility functions and classes that complement this framework.

Contributors

Contributing

We welcome contributions! Please see our Contributing Guidelines for details on our code of conduct, and the process for submitting pull requests.

Roadmap

Check out our Roadmap to see what we have planned for future releases.

Changelog

For a detailed list of changes and updates, please refer to the CHANGELOG.

License

This project is licensed under the Apache-2.0 License.