FoalTS Documentation

repository·master·Indexed 23 days ago

https://github.com/foalts/foal

Foal (or FoalTS) is a comprehensive Node.js framework written in TypeScript for building web applications. It provides an integrated environment featuring a CLI, ORM, authentication, GraphQL, Swagger/OpenAPI, and testing tools to reduce reliance on multiple third-party packages. The framework includes native TypeScript support, AWS integration via @foal/aws-s3, and built-in support for various databases including MySQL, PostgreSQL, MongoDB, Redis, and SQLite (via better-sqlite3).

Tokens
127.8K
Snippets
411
Records
717
Agent score
84%

What's inside FoalTS

  1. What is Foal?

    master

    Foal (or FoalTS) is a comprehensive Node.js framework designed for building web applications. It aims to provide a complete, integrated environment to reduce the need for searching and configuring disparate npm packages.

    Key features included in the framework:

    • CLI: Command-line interface for scaffolding and management.
    • Testing Tools: Built-in support for reliable testing.
    • Frontend Utilities: Tools to assist with client-side development.
    • Advanced Authentication: Ready-to-use security components.
    • ORM: Object-Relational Mapping for database interactions.
    • API Support: Integrated GraphQL and Swagger/OpenAPI documentation.
    • Cloud Utilities: AWS integration (e.g., @foal/aws-s3).
    • TypeScript Native: Entirely written in TypeScript, providing static type-checking and excellent autocompletion.
  2. Enable stateless CSRF protection

    master

    Stateless CSRF protection is automatically managed when using @JWTRequired, setAuthCookie, and removeAuthCookie.

    Enabling CSRF

    To enable it, add the following to your configuration:

    settings:
      jwt:
        csrf:
          enabled: true

    How it works

    1. When enabled, an additional XSRF-TOKEN cookie is sent to the client alongside the auth cookie.
    2. This cookie contains a signed, stateless CSRF token with the same expiration as your JWT.
    3. For every request, the @JWTRequired hook expects the client to include the value of the XSRF-TOKEN cookie in a header named XSRF-TOKEN.
  3. Use TypeORM with FoalTS

    master

    FoalTS uses TypeORM as its default Object-Relational Mapping (ORM) layer. It is integrated by default in new projects, allowing you to create models, run migrations, and use the authentication system out of the box. TypeORM supports both Active Record and Data Mapper patterns and handles relations, transactions, and migrations.

    import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
    
    @Entity()
    export class User {
    
        @PrimaryGeneratedColumn()
        id: number;
    
        @Column()
        firstName: string;
    
        @Column()
        lastName: string;
    
    }
  4. Handle errors in Hook post functions

    master

    When an error occurs in a controller or hook, it is converted into an HttpResponseInternalServerError (or your custom response from handleError).

    In a hook's post function, you should check if the response is an error before executing logic that depends on a successful request. Use isHttpResponseInternalServerError(response) to detect these errors.

    @Hook(() => response => {
      if (isHttpResponseInternalServerError(response)) {
        return;
      }
    
      // Else execute some logic.
    })
  5. Understand default security HTTP headers in FoalTS

    master

    To protect applications against common attacks, FoalTS automatically sets several security-related HTTP headers by default. These headers include:

    • Strict-Transport-Security: max-age=31536000; includeSubDomains (Enforces HTTPS)
    • X-Content-Type-Options: nosniff (Prevents MIME type sniffing)
    • X-Frame-Options: SAMEORIGIN (Protects against clickjacking by restricting framing to the same origin)

    Note that while these provide a layer of protection, they are not a complete security solution.

  6. What are Hooks and how to use them

    master

    Hooks are decorators that execute extra logic before and/or after the execution of a controller method. They are commonly used for authentication, access control, request validation, sanitization, and logging.

    Scope

    • Method Level: Decorating a controller method applies the hook only to that specific method.
    • Controller Level: Decorating a controller class applies the hook to all its methods and sub-controllers.
    • Global Level: Decorating the root controller (e.g., AppController) applies the hook globally to the entire application.

    If a hook returns an HttpResponse object, the remaining hooks and the controller method are skipped, and that response is sent to the client.

    import {
      Context, Get, HttpResponseCreated, HttpResponseOK, Post, ValidateBody
    } from '@foal/core';
    import { JWTRequired } from '@foal/jwt';
    
    @JWTRequired()
    class AppController {
      private products = [
        { name: 'Hoover' }
      ];
    
      @Get('/products')
      listProducts() {
        return new HttpResponseOK(this.products);
      }
    
      @Post('/products')
      @ValidateBody({
        additionalProperties: false,
        properties: {
          name: { type: 'string' }
        },
        required: [ 'name' ],
        type: 'object',
      })
      addProduct(ctx: Context) {
        this.products.push(ctx.request.body);
        return new HttpResponseCreated();
      }
    
    }
  7. Implement automatic password upgrades with PasswordService

    master

    The PasswordService.verifyPassword method supports an optional onPasswordUpgrade callback. This allows you to automatically upgrade a user's password hash to current security standards (e.g., higher PBKDF2 iterations) during a successful login.

    When a hash is detected as outdated, the service verifies the password, generates a new hash, and triggers the callback with the newHash value. You should use this callback to persist the new hash to your database.

    import { PasswordService, dependency } from '@foal/core';
    
    export class AuthService {
      @dependency
      passwordService: PasswordService;
    
      @dependency
      userRepository: UserRepository;
    
      async login(email: string, password: string) {
        const user = await this.userRepository.findByEmail(email);
        
        const isValid = await this.passwordService.verifyPassword(
          password,
          user.passwordHash,
          {
            onPasswordUpgrade: async (newHash) => {
              // Automatically save the upgraded hash
              await this.userRepository.updatePasswordHash(user.id, newHash);
            }
          }
        );
    
        if (!isValid) {
          throw new Error('Invalid credentials');
        }
    
        // User is authenticated
      }
    }
  8. Define REST API routes using Controllers

    master

    In Foal, a Controller is a class that receives HTTP requests and processes them. You define routes by adding methods to a controller class and decorating them with HTTP method decorators.

    Available decorators include:

    • @Get(path)
    • @Post(path)
    • @Patch(path)
    • @Put(path)
    • @Delete(path)

    Each decorated method acts as a route handler for the specified path and HTTP method.

    import { Get, Post, Delete } from '@foal/core';
    
    export class ApiController {
      @Get('/todos')
      async getTodos() {
        // handler logic
      }
    
      @Post('/todos')
      async postTodo(ctx: Context) {
        // handler logic
      }
    
      @Delete('/todos/:id')
      async deleteTodo(ctx: Context) {
        // handler logic
      }
    }
  9. Understand the FoalTS authentication pattern

    master

    Authentication in FoalTS follows a two-step pattern regardless of the application architecture (API, Web App, SPA, or Mobile):

    Step 1: User Login

    1. Verify credentials (e.g., email/password or social providers).
    2. Generate a token (either a stateful Session Token or a stateless JSON Web Token).
    3. Return the token to the client via a cookie, response body, or header.

    Step 2: Subsequent Requests On every request, the server receives the token, validates it, and retrieves the associated user.

  10. Configure environment-specific .env files

    master

    FoalTS supports multiple .env files based on the NODE_ENV environment variable.

    If NODE_ENV is set to production, the framework will first look for values in .env.production. If a value is missing from that file, it will fall back to the default .env file.

    You can reference environment variables in your configuration files using the env(VARIABLE_NAME) syntax (for YAML/JSON) or via the Env.get() method (for JS).

    settings:
      jwt:
        secret: env(SETTINGS_JWT_SECRET)
  11. Use validation decorators with TypeORM entities

    master

    Validation decorators from class-validator are compatible with TypeORM. You can define your database schema and your validation rules within a single class, reducing duplication.

    By applying both @Column (TypeORM) and validation decorators (like @Length, @IsEmail, @Min) to the same class properties, the class serves as both a database entity and a validation model.

    import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
    import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, Min, Max } from 'class-validator';
    
    @Entity()
    export class Post {
    
        @PrimaryGeneratedColumn()
        id: number;
        
        @Column()
        @Length(10, 20)
        title: string;
    
        @Column()
        @Contains("hello")
        text: string;
    
        @Column()
        @IsInt()
        @Min(0)
        @Max(10)
        rating: number;
    
        @Column()
        @IsEmail()
        email: string;
    
        @Column()
        @IsFQDN()
        site: string;
    
        @Column()
        @IsDate()
        createDate: Date;
    
    }