@@ -65,6 +65,10 @@ export class SheetController {
6565Bootstrap your application by creating an ` App ` instance and delegating the standard Apps Script entry points (` doGet ` ,
6666` doPost ` ) to it.
6767
68+ #### Synchronous Application
69+
70+ Use ` App ` for synchronous execution:
71+
6872``` TypeScript
6973import {App } from " bootgs" ;
7074import {SheetController } from " ./SheetController" ;
@@ -90,10 +94,43 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
9094}
9195```
9296
97+ #### Asynchronous Application
98+
99+ Use ` AsyncApp ` when you need to handle asynchronous operations (e.g., ` UrlFetchApp ` promises or other async tasks) in your controllers:
100+
101+ ``` TypeScript
102+ import {AsyncApp } from " bootgs" ;
103+ import {SheetController } from " ./SheetController" ;
104+
105+ /**
106+ * Global entry point for GET requests.
107+ */
108+ export async function doGet(event : GoogleAppsScript .Events .DoGet ) {
109+ const app = AsyncApp .create ({
110+ controllers: [SheetController ]
111+ });
112+ return await app .doGet (event );
113+ }
114+
115+ /**
116+ * Global entry point for POST requests.
117+ */
118+ export async function doPost(event : GoogleAppsScript .Events .DoPost ) {
119+ const app = AsyncApp .create ({
120+ controllers: [SheetController ]
121+ });
122+ return await app .doPost (event );
123+ }
124+ ```
125+
93126## Features
94127
95- - ** Decorator-based Routing** : Intuitive mapping of HTTP and Apps Script events.
96- - ** Dependency Injection** : Decouple your components for better testability.
128+ - ** Decorator-based Routing** : Intuitive mapping of HTTP and Apps Script events (GET, POST, etc.).
129+ - ** Spring Boot & NestJS Patterns** : Familiar decorators like ` @RequestMapping ` , ` @Autowired ` , ` @Value ` .
130+ - ** Validation** : Declarative parameter validation using Spring Boot-style decorators like ` @Min ` , ` @Max ` , ` @Email ` , etc.
131+ - ** Pipes & Validation** : Transform and validate incoming data with ` @UsePipes ` and built-in pipes (e.g., ` ParseNumberPipe ` ).
132+ - ** Global Error Handling** : Centralized exception management using ` @ControllerAdvice ` and ` @ExceptionHandler ` .
133+ - ** Dependency Injection** : Fully-featured DI for better decoupling and testability.
97134- ** Type Safety** : Built with TypeScript for a robust development experience.
98135- ** Modern Architecture** : Inspired by frameworks like NestJS and Spring Boot.
99136
@@ -117,11 +154,21 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
117154 <td><code>ClassDecorator</code></td>
118155 <td>Marks a class as a general-purpose controller.</td>
119156 </tr>
157+ <tr>
158+ <td><code>@RequestMapping(path?: string, method?: RequestMethod | RequestMethod[])</code></td>
159+ <td><code>ClassDecorator & MethodDecorator</code></td>
160+ <td>Maps a specific request path onto a controller or a handler method.</td>
161+ </tr>
120162 <tr>
121163 <td><code>@HttpController(basePath?: string)</code></td>
122164 <td><code>ClassDecorator</code></td>
123165 <td>Marks a class as an HTTP request controller. Default base path is <code>/</code>.</td>
124166 </tr>
167+ <tr>
168+ <td><code>@ControllerAdvice()</code></td>
169+ <td><code>ClassDecorator</code></td>
170+ <td>Marks a class as a global exception handler and data binder.</td>
171+ </tr>
125172 <tr>
126173 <td><code>@SheetController(sheetName?: string | string[] | RegExp)</code></td>
127174 <td><code>ClassDecorator</code></td>
@@ -165,6 +212,11 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
165212 <td><code>ClassDecorator</code></td>
166213 <td>Alias for <code>@HttpController()</code>.</td>
167214 </tr>
215+ <tr>
216+ <td><code>@RestControllerAdvice()</code></td>
217+ <td><code>ClassDecorator</code></td>
218+ <td>Alias for <code>@ControllerAdvice()</code>.</td>
219+ </tr>
168220 <tr>
169221 <td><code>@SheetsController(sheetName?: string | string[] | RegExp)</code></td>
170222 <td><code>ClassDecorator</code></td>
@@ -236,6 +288,11 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
236288 <tr>
237289 <td colspan="3" align="center"><b>HTTP Methods</b></td>
238290 </tr>
291+ <tr>
292+ <td><code>@RequestMapping(path?: string, method?: RequestMethod | RequestMethod[])</code></td>
293+ <td><code>ClassDecorator & MethodDecorator</code></td>
294+ <td>Maps a specific request path onto a controller or a handler method.</td>
295+ </tr>
239296 <tr>
240297 <td><code>@Get(path?: string)</code></td>
241298 <td><code>MethodDecorator</code></td>
@@ -271,6 +328,24 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
271328 <td><code>MethodDecorator</code></td>
272329 <td>Maps a method to handle HTTP OPTIONS requests.</td>
273330 </tr>
331+ <tr>
332+ <td colspan="3" align="center"><b>Error Handling & Security</b></td>
333+ </tr>
334+ <tr>
335+ <td><code>@ExceptionHandler(value?: Newable | Newable[])</code></td>
336+ <td><code>MethodDecorator</code></td>
337+ <td>Annotation for handling exceptions in specific handler classes and/or handler methods.</td>
338+ </tr>
339+ <tr>
340+ <td><code>@ResponseStatus(value: number)</code></td>
341+ <td><code>MethodDecorator & ClassDecorator</code></td>
342+ <td>Marks a method or exception class with the status code that should be returned.</td>
343+ </tr>
344+ <tr>
345+ <td><code>@UsePipes(...pipes: any[])</code></td>
346+ <td><code>MethodDecorator & ClassDecorator</code></td>
347+ <td>Specifies the pipes to be used for a controller or method.</td>
348+ </tr>
274349 <tr>
275350 <td colspan="3" align="center"><b>Aliases</b></td>
276351 </tr>
@@ -343,48 +418,201 @@ export function doPost(event: GoogleAppsScript.Events.DoPost) {
343418 <td>Injects request headers or a specific header value.</td>
344419 </tr>
345420 <tr>
346- <td><code>@Body(key?: string)</code></td>
421+ <td><code>@Body(key?: string, ...pipes: any[] )</code></td>
347422 <td><code>ParameterDecorator</code></td>
348- <td>Injects the full request body or a specific key.</td>
423+ <td>Injects the full request body or a specific key. Supports transformation pipes. </td>
349424 </tr>
350425 <tr>
351- <td><code>@Param(key?: string)</code></td>
426+ <td><code>@Param(key?: string, ...pipes: any[] )</code></td>
352427 <td><code>ParameterDecorator</code></td>
353- <td>Injects values from URL path parameters.</td>
428+ <td>Injects values from URL path parameters. Supports transformation pipes. </td>
354429 </tr>
355430 <tr>
356- <td><code>@Query(key?: string)</code></td>
431+ <td><code>@Query(key?: string, ...pipes: any[] )</code></td>
357432 <td><code>ParameterDecorator</code></td>
358- <td>Injects values from URL query parameters.</td>
433+ <td>Injects values from URL query parameters. Supports transformation pipes. </td>
359434 </tr>
360435 <tr>
361436 <td><code>@Inject(token: any)</code></td>
362437 <td><code>ParameterDecorator</code></td>
363438 <td>Explicitly specifies an injection token for a dependency.</td>
364439 </tr>
440+ <tr>
441+ <td><code>@Value(key: string)</code></td>
442+ <td><code>ParameterDecorator & PropertyDecorator</code></td>
443+ <td>Injects a value from the application configuration.</td>
444+ </tr>
365445 <tr>
366446 <td colspan="3" align="center"><b>Aliases</b></td>
367447 </tr>
368448 <tr>
369- <td><code>@RequestBody(key?: string)</code></td>
449+ <td><code>@Autowired(token?: any)</code></td>
450+ <td><code>ParameterDecorator & PropertyDecorator</code></td>
451+ <td>Alias for <code>@Inject()</code>.</td>
452+ </tr>
453+ <tr>
454+ <td><code>@RequestBody(key?: string, ...pipes: any[])</code></td>
370455 <td><code>ParameterDecorator</code></td>
371456 <td>Alias for <code>@Body()</code>.</td>
372457 </tr>
373458 <tr>
374- <td><code>@PathVariable(key?: string)</code></td>
459+ <td><code>@PathVariable(key?: string, ...pipes: any[] )</code></td>
375460 <td><code>ParameterDecorator</code></td>
376461 <td>Alias for <code>@Param()</code>.</td>
377462 </tr>
378463 <tr>
379- <td><code>@RequestParam(key?: string)</code></td>
464+ <td><code>@RequestParam(key?: string, ...pipes: any[] )</code></td>
380465 <td><code>ParameterDecorator</code></td>
381466 <td>Alias for <code>@Query()</code>.</td>
382467 </tr>
468+ <tr>
469+ <td colspan="3" align="center"><b>Validation Decorators (Spring Boot style)</b></td>
470+ </tr>
471+ <tr>
472+ <td><code>@AssertFalse()</code></td>
473+ <td><code>ParameterDecorator</code></td>
474+ <td>Validates that the value is <code>false</code>.</td>
475+ </tr>
476+ <tr>
477+ <td><code>@AssertTrue()</code></td>
478+ <td><code>ParameterDecorator</code></td>
479+ <td>Validates that the value is <code>true</code>.</td>
480+ </tr>
481+ <tr>
482+ <td><code>@Email()</code></td>
483+ <td><code>ParameterDecorator</code></td>
484+ <td>Validates that the value is a valid email address.</td>
485+ </tr>
486+ <tr>
487+ <td><code>@Max(value: number)</code></td>
488+ <td><code>ParameterDecorator</code></td>
489+ <td>Validates that the value is less than or equal to the specified maximum.</td>
490+ </tr>
491+ <tr>
492+ <td><code>@Min(value: number)</code></td>
493+ <td><code>ParameterDecorator</code></td>
494+ <td>Validates that the value is greater than or equal to the specified minimum.</td>
495+ </tr>
496+ <tr>
497+ <td><code>@Negative()</code></td>
498+ <td><code>ParameterDecorator</code></td>
499+ <td>Validates that the value is strictly negative.</td>
500+ </tr>
501+ <tr>
502+ <td><code>@NegativeOrZero()</code></td>
503+ <td><code>ParameterDecorator</code></td>
504+ <td>Validates that the value is negative or zero.</td>
505+ </tr>
506+ <tr>
507+ <td><code>@NotBlank()</code></td>
508+ <td><code>ParameterDecorator</code></td>
509+ <td>Validates that the value is not null and contains at least one non-whitespace character.</td>
510+ </tr>
511+ <tr>
512+ <td><code>@NotEmpty()</code></td>
513+ <td><code>ParameterDecorator</code></td>
514+ <td>Validates that the value is not null and not empty (works for strings, arrays, and objects).</td>
515+ </tr>
516+ <tr>
517+ <td><code>@Pattern(regexp: string | RegExp)</code></td>
518+ <td><code>ParameterDecorator</code></td>
519+ <td>Validates that the value matches the specified regular expression.</td>
520+ </tr>
521+ <tr>
522+ <td><code>@Positive()</code></td>
523+ <td><code>ParameterDecorator</code></td>
524+ <td>Validates that the value is strictly positive.</td>
525+ </tr>
526+ <tr>
527+ <td><code>@PositiveOrZero()</code></td>
528+ <td><code>ParameterDecorator</code></td>
529+ <td>Validates that the value is positive or zero.</td>
530+ </tr>
531+ <tr>
532+ <td><code>@Size(options: { min?: number, max?: number })</code></td>
533+ <td><code>ParameterDecorator</code></td>
534+ <td>Validates that the size of the value is between the specified minimum and maximum.</td>
535+ </tr>
383536 </tbody >
384537</table >
385538
386539</details >
387540
541+ ### Built-in Pipes
542+
543+ Pipes can be used to transform data before it reaches your handler:
544+
545+ | Pipe | Description |
546+ | :------------------- | :-------------------------------------------------------------------------- |
547+ | ` ParseNumberPipe ` | Transforms a string to a number. |
548+ | ` ParseFloatPipe ` | Transforms a string to a float. |
549+ | ` ParseBooleanPipe ` | Transforms a string to a boolean. |
550+ | ` AssertFalsePipe ` | Validates that the value is ` false ` . |
551+ | ` AssertTruePipe ` | Validates that the value is ` true ` . |
552+ | ` EmailPipe ` | Validates that the value is a valid email address. |
553+ | ` MaxPipe ` | Validates that the value is less than or equal to the specified maximum. |
554+ | ` MinPipe ` | Validates that the value is greater than or equal to the specified minimum. |
555+ | ` NegativePipe ` | Validates that the value is strictly negative. |
556+ | ` NegativeOrZeroPipe ` | Validates that the value is negative or zero. |
557+ | ` NotBlankPipe ` | Validates that the value is not blank. |
558+ | ` NotEmptyPipe ` | Validates that the value is not empty. |
559+ | ` PatternPipe ` | Validates that the value matches the specified regular expression. |
560+ | ` PositivePipe ` | Validates that the value is strictly positive. |
561+ | ` PositiveOrZeroPipe ` | Validates that the value is positive or zero. |
562+ | ` SizePipe ` | Validates that the size of the value is within range. |
563+
564+ ## Advanced Examples
565+
566+ ### Pipes
567+
568+ Transform parameters with pipes:
569+
570+ ``` TypeScript
571+ import {Get , RestController , Query , ParseNumberPipe } from " bootgs" ;
572+
573+ @RestController (" users" )
574+ export class UserController {
575+
576+ @Get (" details" )
577+ getUserDetails(@Query (" id" , ParseNumberPipe ) id : number ): object {
578+ return {
579+ userId: id ,
580+ message: " Success!"
581+ };
582+ }
583+ }
584+ ```
585+
586+ ### Global Error Handling
587+
588+ Use ` @ControllerAdvice ` to handle exceptions globally across the whole application:
589+
590+ ``` TypeScript
591+ import {ControllerAdvice , ExceptionHandler , ResponseStatus } from " bootgs" ;
592+
593+ @ControllerAdvice ()
594+ export class GlobalExceptionHandler {
595+
596+ @ExceptionHandler (Error )
597+ @ResponseStatus (500 )
598+ handleError(error : Error ): object {
599+ return {
600+ status: " Error" ,
601+ message: error .message
602+ };
603+ }
604+ }
605+ ```
606+
607+ ## Contributors
608+
609+ <a href =" https://github.com/felipepmdias " >
610+ <img src =" https://github.com/felipepmdias.png " width =" 50 " height =" 50 " style =" border-radius : 50% " alt =" felipepmdias " />
611+ </a >
612+ <a href =" https://github.com/kosmo-ds " >
613+ <img src =" https://github.com/kosmo-ds.png " width =" 50 " height =" 50 " style =" border-radius : 50% " alt =" kosmo-ds " />
614+ </a >
615+
388616## Contributing
389617
390618We welcome contributions! Please see our [ Contributing Guidelines] ( CONTRIBUTING.md ) for details on our code of conduct,
0 commit comments