Unable to catch unexpected error in default route

Feature(s) impacted

According to routes documentation: The generated routes use next() to call Forest Admin’s default behavior.
We used this implementation in our codebase:

// Get a number of Customers
router.get('/customers/count', permissionMiddlewareCreator.list(), async (request, response, next) => {
  // Learn what this route does here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/routes/default-routes#get-a-number-of-records
  next();
});

Observed behavior

When an error occurs in this endpoint, we can’t catch it with an error middleware.
It seems like the default route is not well implemented in the method next().
When I copy and paste the snippets code of default routes, the routes work and we can handle the errors in a middleware.

Expected behavior

Handle default routes errors in a middleware without overriding.

Failure Logs

[forest] 🌳🌳🌳  Unexpected error: function lower(uuid) does not exist
{
  "name": "SequelizeDatabaseError",
  "parent": {
    "length": 205,
    "name": "error",
    "severity": "ERROR",
    "code": "42883",
    "hint": "No function matches the given name and argument types. You might need to add explicit type casts.",
    "position": "165",
    "file": "parse_func.c",
    "line": "528",
    "routine": "ParseFuncOrColumn",
    "sql": "SELECT count(\"customers\".\"id\") AS ...;"
  },
  "original": {
    "length": 205,
    "name": "error",
    "severity": "ERROR",
    "code": "42883",
    "hint": "No function matches the given name and argument types. You might need to add explicit type casts.",
    "position": "165",
    "file": "parse_func.c",
    "line": "528",
    "routine": "ParseFuncOrColumn",
    "sql": "SELECT count(\"customers\".\"id\") AS ...;"
  },
  "sql": "SELECT ...;",
  "stack": "SequelizeDatabaseError: function lower(uuid) does not exist\n    at Query.formatError (...)"
}

Context

Please provide in this mandatory section, the relevant information about your configuration:

  • Project name: Koala
  • Team name: Operations
  • Environment name: all

Hello @Jules_Camille_Ziriga

Can you share the model of customer (might be on models/customers.js) to identity the error ?

You can add a middleware in the top level of express application with this code in app.js

app.use((err, req, res, next) => { 
   if(err) { 
     console.log('an error occurred in API', err) 
   }
   next();
})

The middleware in the top level of express doesn’t work.
You’ll find bellow the content of file models/customer.ts

// models/customer.ts
import { DataTypes, Model, ModelCtor, Sequelize } from 'sequelize';

import { Address } from './addresses';

interface ICustomerAttributes {
  id: string;
  email: string;
  firstName: string;
  lastName: string;
  language: string;
  phoneNumber: string | null;
  birthday: string | null;
  address: Address | null;
  createdAt: Date;
  updatedAt: Date;
}

interface ICustomerCreationAttributes {
  email: string;
  firstName: string;
  lastName: string;
  language: string;
  phoneNumber: string | null;
  birthday: string | null;
  address: Address | null;
}

export class Customer extends Model<ICustomerAttributes, ICustomerCreationAttributes> {
  public id!: string;

  public email!: string;

  public firstName!: string;

  public lastName!: string;

  public language!: string;

  public phoneNumber!: string | null;

  public birthday!: string | null;

  public addressIdKey!: string | null;

  public address!: Address | null;

  public createdAt!: Date;

  public updatedAt!: Date;

  public static associate(models: Record<string, ModelCtor<Model>>): void {
    Customer.hasMany(models.subscriptions, {
      foreignKey: {
        name: 'customerIdKey',
        field: 'customer_id',
      },
      as: 'subscriptions',
    });
    Customer.belongsTo(models.addresses, {
      foreignKey: {
        name: 'addressIdKey',
        field: 'address_id',
      },
      as: 'address',
    });
  }
}

export default (sequelize: Sequelize, dataTypes: typeof DataTypes): typeof Customer => {
  Customer.init(
    {
      id: {
        type: dataTypes.STRING,
        primaryKey: true,
        allowNull: false,
      },
      email: {
        type: dataTypes.STRING,
        allowNull: false,
      },
      firstName: {
        type: dataTypes.STRING,
        allowNull: false,
      },
      lastName: {
        type: dataTypes.STRING,
        allowNull: false,
      },
      language: {
        type: dataTypes.STRING,
        allowNull: false,
      },
      phoneNumber: {
        type: dataTypes.STRING,
      },
      birthday: {
        type: DataTypes.STRING,
      },
      createdAt: {
        type: dataTypes.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      },
      updatedAt: {
        type: dataTypes.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      },
    },
    {
      tableName: 'customers',
      timestamps: false,
      underscored: true,
      modelName: 'customers',
      sequelize,
    },
  );

  return Customer;
};

In the initialize of sequelize (where you have new Sequelize(...)) can you add in the options object (2nd param) { logging: console.log }.

Re load the collection and send us the query starting with SELECT count(\"customers\".\"id\")

Hi @Jules_Camille_Ziriga and welcome in our community :champagne: !!

I’ll try to reproduce and see if I can find a solution :eyes:

1 Like

Hello @Jules_Camille_Ziriga,
unfortunately I don’t have a quick solution to give you, it is a bug.
I have created a bug ticket to fix this issue. Thanks for the report.

we already fixed the problem related to Sequelize. Currently, we want to use a middleware to handle unexpected errors of default routes.

Thank you for your help. Do you have a link or something to follow the bug ticket?

Yes I was talking about the handler. Actually, our injected customHandler does not forward the error to the next method. A hack is to overwrite the errorHandler by your own before the injection…

The bug ticket is not public sorry.