HttpResponseTooManyRequests
(issue: #664) (PR: #709)ctx.state
(issue: #711) (PR: #717)forceDownload
option work on all browsers (PR: #722)This version also takes care of closing a lot of pending issues.
foal g vscode-config
(issue: #694) (PR: #714).g service|controller
(ex: foal g controller api/products --register
) (issue: #368) (PR: #714)foal connect
on Windows now can build on unix systems (issue: #562) (PR: #703)--auth
flag in foal g rest-api
(issue: #366) (PR: #716)@matt-harvey
@kingdun3284
@AminTaghikhani
ServiceManager.set
returns itself (PR: #675).UserWithPermissions
extends BaseEntity
(issue: #656) (PR: #682)UserWithPermissions
has a static method to get all users with a given permission (issue: #637) (PR: #682)@types/express
(issue: #683) (PR: #685).allErrors
(issue: #633) (PR: #642)isFileDoesNotExist
function (#657).Published by LoicPoullain over 4 years ago
foal generate controller —register
bug (issue: #606) (PR: #619)tsconfig
(issue: #614) (PR: #623, #624)The function createHttpResponseFile
is deprecated. Prefer to use Disk.createHttpResponse
instead: https://foalts.gitbook.io/docs/topic-guides/file-system/upload-and-download-files#file-downloads
npm update @foal/core # and all the other packages you use
Additionally, you can update your package.json
like this.
@jeredmasters
Published by LoicPoullain almost 5 years ago
'none'
option for SameSite
cookie attribute (PR: #594).noImplicitAny: true
(PR: #600)type-graphql
dependency introduced by mistake in v1.3.1 (PR: #608)unknown
issue when building @foal/typestack
(PR: #557).--no-git
and --no-install
options to createapp
(PR: #554).createapp my-app
if the directory my-app
already exists (PR: #554).Due to a compatibility issue with class-transformer
, Foal TS does not support TypeScript 2 anymore. Version 3.5 or higher is recommended.
@firesharker
@fer22f
AppController.init
to initialize the application (issue: #517) (PR: #544)ExpressOptions.expressInstance
in createApp
(PR: #547)foal createapp
(PR: #541).Published by LoicPoullain about 5 years ago
loggerFormat: none
.Published by LoicPoullain about 5 years ago
@yaaminu
@kingdun3284
@amylily1011
Published by LoicPoullain about 5 years ago
foal g sub-app
should not generate a sub sub-apps
directory by default.foal createapp --mongodb
should not contain TypeORM options.setCsrfToken
, @CsrfTokenRequired
should use the configuration key settings.csrf.cookie.name
if it has a value.foal generate
not fail if index.ts does not exist (PR: #484).Published by LoicPoullain over 5 years ago
Finally the version 1 of FoalTs is out! This means that from now on:
This new version comes with hundreds of new lines of documentation and offers many new features:
JWTRequired
, ValidateBody
, TokenRequired
, etc).ValidateXXX
, JWTRequired
, etc) (issues: #423, #424) (PR: #454)Depending on your application, upgrading your code to version 1 should take between 15 minutes and 1 hour. Most applications are only concerned by a limited number of the points mentioned below.. As we are now on version 1, this the last major upgrade that you'll face.
If you have difficulties to upgrade to version 1, feel free to open an issue. I'll be happy to help!
Feel free to open an issue, if you have trouble upgrading to version 1. I'll be happy to help!
Don't be afraid.
npm install @foal/core@1 # and @foal/typeorm@1, @foal/jwt@1, etc. if it applies.
The function encryptPassword
has been renamed to hashPassword
.
The configuration key settings.staticUrl
has been renamed to settings.staticPath
.
If you created your project with foal createapp <name>
, open config/default.json
and rename the key as follows:
{
"settings": {
"staticPath": "public/"
...
}
...
}
If you created your project with foal createapp <name> --yaml
, open config/default.yml
and rename the key as follows:
settings:
staticPath: public/
...
...
If you use Server-Side Rendering, the function render
now returns a Promise<HttpResponseOK>
instead of an HttpResponseOK
object. In most cases, it does not change anything to your code. The below example still work in version 1.
class ViewController {
@Get('/')
index() {
return render('./templates/index.html', { name: 'Hello!' }, __dirname)
}
}
If you use @foal/ejs
, uninstall the package and install directly ejs
instead.
npm uninstall @foal/ejs
npm install ejs
Then specify the name of the template engine in your configuration.
Example with config/default.json
{
"settings": {
"templateEngine": "ejs"
...
}
...
}
Example with config/default.yml
settings:
templateEngine: "ejs"
...
...
If you use cookies, the maxAge
directive is now the number of seconds until the cookie expires (it was previously the number of milliseconds).
// Before
const response = new HttpResponseOK();
response.setCookie('my-cookie-name', 'my-cookie-value', { maxAge: 60000 })
// After
const response = new HttpResponseOK();
response.setCookie('my-cookie-name', 'my-cookie-value', { maxAge: 60 })
If you provide your own express instance to createApp
, the createApp
function now accepts only two parameters.
// Before
const app = createApp(AppController, { /* ... */ }, myExpressApp);
// After
const app = createApp(AppController, myExpressApp);
If you use hook post functions (which the case if you enabled CORS), their interface has been simplified. From now on, they only accept one parameter: the response object.
CORS example
// Before
@Hook(() => (ctx, services, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
})
export class ApiController {
// ...
}
// After
@Hook(() => response => {
response.setHeader('Access-Control-Allow-Origin', '*');
})
export class ApiController {
// ...
}
If you call directly the functions getApiComponents
or getApiCompleteOperation
(very unlikely), they now accept 2-3 parameters:
// Before
getApiComponents(Foobar);
getApiComponents(Foobar, 'foo');
// After
getApiComponents(Foobar, new Foobar());
getApiComponents(Foobar, new Foobar(), 'foo');
If you use the hooks @ValidateBody
, @ValidateQuery
, @ValidateParams
, @ValidateCookies
, their HTTP responses are now more detailed.
Example of HTTP response body for @ValidateParams
// Before
[
// ...
]
// After
{
"pathParams": [
// ...
]
}
If you use sessions or authentication with sessions (@LoginRequired
, @LoginOptional
), go to the appendix Sessions below.
If you use cookies and the CSRF protection,
go to the appendix CSRF below.
Previous versions of FoalTS used a ormconfig.json
or ormcofing.yml
file to specify the database credentials and the configuration of TypeORM.
However, this had some disadvantages:
TYPEORM_USERNAME
or TYPEORM_PASSWORD
while most cloud providers use a different naming: DATABASE_PASSWORD
, RDS_USERNAME
, etc.This is why new versions of FoalTS use an ormconfig.js
file along with the Config
class (see example below). In this way, we can configure different configurations per environment (with config/
files) and use custom environment variable in ormconfig.js
(RDS_USERNAME
, etc).
I recommend that you migrate to this new configuration because it is used by default in new projects and in the new documentation.
In addition, new projects generated by FoalTS will not use the synchronize
option by default. This option recreated the database schema each time the application was launched. This is very convenient during prototyping because we don't need to create and run migrations every time we modify a model. However, this use is considered unsafe and can erase data from the database by mistake (which we do NOT want in production).
This is why new projects disable this option by default (except during testing). As a replacement, developpers must generate and run migrations each time an entity is modified. It is a little annoying but more secure. The get started tutorials will explain how migrations work and how to use them.
Example with SQLite
ormconfig.js (it replaces ormconfig.js and ormconfig.yml)
const { Config } = require('@foal/core');
module.exports = {
type: "sqlite",
database: Config.get('database.database'),
dropSchema: Config.get('database.dropSchema', false),
entities: ["build/app/**/*.entity.js"],
migrations: ["build/migrations/*.js"],
cli: {
migrationsDir: "src/migrations"
},
synchronize: Config.get('database.synchronize', false)
}
config/default.yml
port: 3001
settings:
...
database:
database: './db.sqlite3'
config/default.json
{
"port": 3001,
"settings": {
...
},
"database": {
"database": "./db.sqlite3"
}
}
config/test.yml
database:
database: './test_db.sqlite3'
dropSchema: true
synchronize: true
config/test.json
{
"database": {
"database": "./test_db.sqlite3",
"dropSchema": true,
"synchronize": true
}
}
Note: the tests generated by
foal g rest-api
assume that you use this new configuration.
If you have been using JWT so far to manage authentication and find it difficult, then take a look at the new Session Tokens.
If you do not use Permission
s or Group
s, i.e your class User
does not extend UserWithPermissions
, you can remove the shell scripts create-perm.ts
and create-group.ts
from the directory src/scripts
.
If you do not use Server-Side Rendering with EJS, you can uninstall the @foal/ejs
package from your application.
npm uninstall @foal/ejs
HttpResponse.setHeader
and HttpResponse.setCookie
now returns this
. So the below example can be refactored as follows to reduce the amount of code.
class ApiController {
@Get('/products')
readProducts() {
// Before
const response = new HttpResponseOK();
response.setHeader('XXX', 'my header value');
response.setCookie('XXX', 'my cookie value');
return response;
// Possible refactoring after
return new HttpResponseOK()
.setHeader('XXX', 'my header value')
.setCookie('XXX', 'my cookie value');
}
}
In version 1, the session system has been redesigned to do less magic and to allow developpers to build any kind of applications using sessions (SPA, Mobile, Regular Web Applications).
Remove the ExpressJS store from your file src/index.ts
.
Before
// ...
import * as sqliteStoreFactory from 'connect-sqlite3';
// ...
async function main() {
// ...
const app = createApp(AppController, {
store: session => new (sqliteStoreFactory(session))({ db: 'db.sqlite3' })
});
// ...
}
main();
After
// ...
async function main() {
// ...
const app = createApp(AppController);
// ...
}
main();
npm uninstall connect-sqlite3
In the new version, FoalTS only supports Redis and SQL databases for session storage. If you need more option, feel free to open an issue on Github.
Replace @LoginRequired({ user: xxx })
with @TokenRequired({ user: xxx, store: TypeORMStore, cookie: true })
.
Before
import { LoginRequired } from '@foal/core';
import { fetchUser } from '@foal/typeorm';
import { User } from '../entities';
@LoginRequired({ user: fetchUser(User) })
export class ApiController {
// ...
}
After
import { TokenRequired, TypeORMStore } from '@foal/core';
import { fetchUser } from '@foal/typeorm';
import { User } from '../entities';
@TokenRequired({
store: TypeORMStore,
user: fetchUser(User),
cookie: true
})
export class ApiController {
// ...
}
In the same way, replace @LoginOptional({ user: xxx })
with @TokenOptional({ user: xxx, store: TypeORMStore, cookie: true })
.
Open config/default.json
(or config/default.yml
) and replace the session settings with the following:
Before
{
"settings": {
...
"session": {
"cookie": {
"path": "/"
},
"resave": false,
"saveUninitialized": false,
"secret": "my-secret"
}
},
...
}
After
{
"settings": {
...
"session": {
"cookie": {
"path": "/"
},
"secret": "my-secret"
}
},
...
}
Open config/production.json
(or config/production.yml
) and replace the session settings with the following:
Before
{
"settings": {
"session": {
"cookie": {
"httpOnly": true,
"maxAge": 3600000,
"sameSite": "lax",
"secure": true
},
"name": "id"
}
}
}
After
{
"settings": {
"session": {
"cookie": {
"httpOnly": true,
"maxAge": 3600,
"sameSite": "lax",
"secure": true,
"name": "id"
}
}
}
}
If you use ctx.request.session
, update your code as follows:
// Before
const x = ctx.request.session.x;
ctx.request.session.y = 1;
// After
const x = ctx.session.get('x');
ctx.session.set('y', 1);
Update your "login and logout controller" as follows:
Before
export class AuthController {
@Post('/login')
// ...
async login(ctx: Context) {
// ...
logIn(ctx, user);
return new HttpResponseRedirect('/');
}
@Get('/logout')
logout(ctx) {
logOut(ctx);
return new HttpResponseRedirect('/signin');
}
}
After
import { Context, dependency, Post, removeSessionCookie, Session, setSessionCookie, TokenRequired } from '@foal/core';
import { TypeORMStore } from '@foal/typeorm';
export class AuthController {
@dependency
store: TypeORMStore;
@Post('/login')
// ...
async login(ctx: Context) {
// ...
const session = await this.store.createAndSaveSessionFromUser(user);
const response = new HttpResponseRedirect('/');
setSessionCookie(response, session.getToken());
return new HttpResponseRedirect('/');
}
@Post('/logout')
@TokenRequired({
store: TypeORMStore,
cookie: true,
extendLifeTimeOrUpdate: false
})
async logout(ctx: Context<any, Session>) {
await this.store.destroy(ctx.session.sessionID);
const response = new HttpResponseRedirect('/login');
removeSessionCookie(response);
return response;
}
}
Install the package @foal/csrf
.
npm install @foal/csrf
If you use sessions, add the hook @CsrfTokenRequired()
after @TokenRequired(...)
Example
import { CsrfTokenRequired } from '@foal/csrf';
@TokenRequired({
cookie: true,
/* ... */
})
@CsrfTokenRequired()
export class ApiController {
// ...
}
OR if you use double submit cookie technique, add the hook @CsrfTokenRequired({ doubleSubmitCookie: true })
on the protected routes.
Example
import { CsrfTokenRequired } from '@foal/csrf';
// Ex: @JWTRequired()
@CsrfTokenRequired({ doubleSubmitCookie: true })
export class ApiController {
}
The CSRF configuration has changed from
settings:
csrf: true
to
settings:
csrf:
enabled: true
Replace ctx.request.csrfToken()
with getCsrfToken(ctx.session)
if you use sessions or getCsrfToken()
if you use double submit cookie technique.
Before
export class PageController {
@Get('/home')
home(ctx: Context) {
return render(
'./templates/index.html',
{ csrfToken: ctx.request.csrfToken() }
, __diname
);
}
}
After
import { getCsrfToken } from '@foal/csrf';
@TokenRequired(/* ... */) // Only if you use sessions
export class PageController {
@Get('/home')
home(ctx: Context) {
return render(
'./templates/index.html',
{ csrfToken: getCsrfToken(ctx.session) }
, __diname
);
}
}
Published by LoicPoullain over 5 years ago
Config
(issue: #437) (PR: #438).npm install -g @foal/cli
npm update @foal/core # and other @foal/xxx if needed
Published by LoicPoullain over 5 years ago
HttpResponseMovedPermanently
(PR: #403)@Log
hook (issue: #409) (PR: #410)npm install -g @foal/cli
npm update @foal/core # and other @foal/xxx if needed
Published by LoicPoullain over 5 years ago
ConfigMock
to mock the Config
when it used as a service (issue: #367) (PR: #394).ValidateCookies
hook (issue: #375) (PR: #398).npm install -g @foal/cli
npm update # in your project
@swanncastel
@LoicPoullain