Foal version 3.0 is here 🚀
Tags: node, typescript, javascript
Published: 2022-11-02 06:11:23

---
title: Foal version 3.0 is here 🚀
published: true
description: Foal version 3.0 release notes
tags: NodeJS, TypeScript, JavaScript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inn4c13p4se2z66s6dnb.png
---

Version 3.0 of [Foal](https://foalts.org/) is finally there!

It's been a long work and I'm excited to share with you the new features of the framework 🎉 . The upgrading guide can be found [here](https://foalts.org/docs/upgrade-to-v3).

Here are the new features and improvements of version 3!

> For those who don't know it yet, Foal is a 5-year-old full-featured Node.JS framework for building web applications. It is simple and easy to use, based on TypeScript with very detailed documentation. And the best part: it is tested by more than 2,000 tests.

## Full support of TypeORM v0.3

TypeORM v0.3 provides greater typing safety and this is something that will be appreciated when moving to the new version of Foal.

The version 0.3 of TypeORM has a lot of changes compared to the version 0.2 though. Features such as the `ormconfig.json` file have been removed and functions such as `createConnection`, `getManager` or `getRepository` have been deprecated.

A lot of work has been done to make sure that `@foal/typeorm`, new projects generated by the CLI and examples in the documentation use version 0.3 of TypeORM without relying on deprecated functions or patterns.

In particular, the connection to the database is now managed by a file `src/db.ts` that replaces the older `ormconfig.json`.

> For those new to Foal, TypeORM is the default ORM used in all new projects. But you can use any other ORM or query builder if you want, as the core framework is ORM independent.

## Code simplified

Some parts of the framework have been simplified to require less code and make it more understandable.

### Authentication

The `@UseSessions` and `@JWTRequired` authentication hooks called obscure functions such as `fetchUser`, `fetchUserWithPermissions` to populate the `ctx.user` property. The real role of these functions was not clear and a newcomer to the framework could wonder what they were for.

This is why these functions have been removed and replaced by direct calls to database models.

```typescript
// Version 2
@UseSessions({ user: fetchUser(User) })
@JWTRequired({ user: fetchUserWithPermissions(User) })

// Version 3
@UseSessions({ user: (id: number) => User.findOneBy({ id }) })
@JWTRequired({ user: (id: number) => User.findOneWithPermissionsBy({ id }) })
```

### File upload

When uploading files in a _multipart/form-data_ request, it was not allowed to pass optional fields. This is now possible. 

The interface of the `@ValidateMultipartFormDataBody` hook, renamed to `@ParseAndValidateFiles` to be more understandable for people who don't know the HTTP protocol handling the upload, has been simplified.

*Examples with only files*
```typescript
// Version 2
@ValidateMultipartFormDataBody({
  files: {
    profile: { required: true }
  }
})

// Version 3
@ParseAndValidateFiles({
  profile: { required: true }
})
```

*Examples with files and fields*
```typescript
// Version 2
@ValidateMultipartFormDataBody({
  files: {
    profile: { required: true }
  }
  fields: {
    description: { type: 'string' }
  }
})

// Version 3
@ParseAndValidateFiles(
  {
    profile: { required: true }
  },
  // The second parameter is optional
  // and is used to add fields. It expects an AJV object.
  {
    type: 'object',
    properties: {
      description: { type: 'string' }
    },
    required: ['description'],
    additionalProperties: false
  }
)
```

### Database models

Using functions like `getRepository` or `getManager` to manipulate data in a database is not necessarily obvious to newcomers. It adds complexity that is not necessary for small or medium sized projects. Most frameworks prefer to use the Active Record pattern for simplicity.

This is why, from version 3 and to take into account that TypeORM v0.3 no longer uses a global connection, the examples in the documentation and the generators will extend all the models from `BaseEntity`. Of course, it will still be possible to use the functions below if desired. 

```typescript
// Version 2
@Entity()
class User {}

const user = getRepository(User).create();
await getRepository(User).save(user);

// Version 3
@Entity()
class User extends BaseEntity {}

const user = new User();
await user.save();
```

## Better typing

The use of TypeScript types has been improved and some parts of the framework ensure better type safety.

### Validation with AJV

Foal's version uses `ajv@8` which allows you to bind a TypeScript type with a JSON schema object. To do this, you can import the generic type `JSONSchemaType` to build the interface of the schema object.

```typescript
import { JSONSchemaType } from 'ajv';

interface MyData {
  foo: number;
  bar?: string
}

const schema: JSONSchemaType<MyData> = {
  type: 'object',
  properties: {
    foo: { type: 'integer' },
    bar: { type: 'string', nullable: true }
  },
  required: ['foo'],
  additionalProperties: false
}
```

### File upload

In version 2, handling file uploads in the controller was tedious because all types were `any`. Starting with version 3, it is no longer necessary to cast the types to `File` or `File[]`:

```typescript
// Version 2
const name = ctx.request.body.fields.name;
const file = ctx.request.body.files.avatar as File;
const files = ctx.request.body.files.images as File[];

// After
const name = ctx.request.body.name;
// file is of type "File"
const file = ctx.files.get('avatar')[0];
// files is of type "Files"
const files = ctx.files.get('images');
```

### Authentication

In version 2, the `user` option of `@UseSessions` and `@JWTRequired` expected a function with this signature:

```typescript
(id: string|number, services: ServiceManager) => Promise<any>;
```

There was no way to guess and guarantee the type of the user ID and the function had to check and convert the type itself if necessary.

The returned type was also very permissive (type `any`) preventing us from detecting silly errors such as confusion between `null` and `undefined` values.

In version 3, the hooks have been added a new `userIdType` option to check and convert the JavaScript type if necessary and force the TypeScript type of the function. The returned type is also safer and corresponds to the type of `ctx.user` which is no longer `any` but `{ [key : string] : any } | null`.

*Example where the ID is a string*
```typescript
@JWTRequired({
  user: (id: string) => User.findOneBy({ id });
  userIdType: 'string',
})
```

*Example where the ID is a number*
```typescript
@JWTRequired({
  user: (id: number) => User.findOneBy({ id });
  userIdType: 'number',
})
```

By default, the value of `userIdType` is a number, so we can simply write this: 

```typescript
@JWTRequired({
  user: (id: number) => User.findOneBy({ id });
})
```

### GraphQL

In version 2, GraphQL schemas were of type `any`. In version 3, they are all based on the `GraphQLSchema` interface.

## Closer to JS ecosystem standards

Some parts have been modified to get closer to the JS ecosystem standards. In particular:

### Development command

The `npm run develop` has been renamed to `npm run dev`.

### Configuration through environment variables

When two values of the same variable are provided by a `.env` file and an environment variable, then the value of the environment is used (the behavior is similar to that of the [dotenv](https://www.npmjs.com/package/dotenv) library).

### `null` vs `undefined` values

When the request has no session or the user is not authenticated, the values of `ctx.session` and `ctx.user` are `null` and no longer `undefined`. This makes sense from a semantic point of view, and it also simplifies the user assignment from the `find` functions of popular ORMs (Prisma, TypeORM, Mikro-ORM). They all return `null` when no value is found.

## More open to other ORMs

TypeORM is the default ORM used in the documentation examples and in the projects generated by the CLI. But it is quite possible to use another ORM or query generator with Foal. For example, the authentication system (with sessions or JWT) makes no assumptions about database access.

Some parts of the framework were still a bit tied to TypeORM in version 2. Version 3 fixed this.

### Shell scripts

When running the `foal generate script` command, the generated script file no longer contains TypeORM code.

### Permission system

The `@PermissionRequired` option is no longer bound to TypeORM and can be used with any `ctx.user` that implements the `IUserWithPermissions` interface.

## Smaller AWS S3 package

The `@foal/aws-s3` package is now based on version 3 of the AWS SDK. Thanks to this, the size of the `node_modules` has been reduced by three.

## Dependencies updated and support of Node latest versions

All Foal's dependencies have been upgraded. The framework is also tested on Node versions 16 and 18.

## Some bug fixes

If the configuration file `production.js` explicitly returns `undefined` for a given key and the `default.json` file returns a defined value for this key, then the value from the `default.json` file is returned by `Config.get`.

> Article originally published [here](https://foalts.org/blog/2022/11/01/version-3.0-release-notes).