# Migrate Sequelize files

## What has changed from previous versions <a href="#what-has-changed-from-version-6" id="what-has-changed-from-version-6"></a>

In version 8 we introduced the application-wide scopes and dynamic smart action form along with hooks on bulk and global smart actions.

Since version 7, we introduced a new structure of our generated projects to ease the support of multiple databases.&#x20;

This new structure is the major change we need to consider to correctly perform the migration of your files. The idea of this document is to guide you through the project architecture and make as few changes as possible to benefit from TypeScript.&#x20;

The key concepts to handle multi databases are the following:

* A configuration file to export your connection options to your databases.
* A dynamic loading mechanism to dynamically load your models depending on their database connections.

The files related to these key concepts are the files under the `/models` and the `/config` directories. Let's handles these files first.

## Define your models

{% hint style="info" %}
Goals:

* Create models class and interfaces to code using TypeScript
* Synchronize models with the database
  {% endhint %}

To migrate our JavaScript models to TypeScript, we will take the following model generated by lumber as an example:&#x20;

{% code title="models/articles.js" %}

```javascript
module.exports = (sequelize, DataTypes) => {

  const { Sequelize } = sequelize;
  
  const Articles = sequelize.define('articles', {
    title: {
      type: DataTypes.STRING,
    },
    body: {
      type: DataTypes.STRING,
    },
  }, {
    tableName: 'articles',
    underscored: true,
    schema: process.env.DATABASE_SCHEMA,
  });

  Articles.associate = (models) => {
    Articles.belongsTo(models.owners, {
      foreignKey: {
        name: 'ownerIdKey',
        field: 'owner_id',
      },
      as: 'owner',
    });
  };

  return Articles;
};
```

{% endcode %}

To declare our models using TypeScript we need to follow the [documentation from Sequelize](https://sequelize.org/master/manual/typescript.html). Two interfaces need to be created describing the attributes for the creation and existing records, and a model class should use these interfaces to benefit from TypeScript:

{% code title="models/articles.ts" %}

```typescript
import { Model, ModelCtor } from 'sequelize';

interface IArticleAttributes {
  id: number;
  title: string | null;
  body: string | null;
  createdAt: Date;
  updatedAt: Date;
}

interface IArticleCreationAttributes {
  title: string | null;
  body: string | null;
}

export class Article extends Model<IArticleAttributes, IArticleCreationAttributes> {
  public id!: number;
  public title!: string | null;
  public body!: string | null;
  public createdAt!: Date;
  public updatedAt!: Date;

  public static associate(models: Record<string, ModelCtor<Model>>): void {
    Article.belongsTo(models.owners, {
      foreignKey: {
        name: 'ownerIdKey',
        field: 'owner_id',
      },
      as: 'owner',
    })
  }
}
```

{% endcode %}

{% hint style="warning" %}
Note the static `associate` function. This function is mandatory to perform dynamic loading and associate models with each other, we will dive into that later on.&#x20;
{% endhint %}

Now our TypeScript definitions are done, we still need to initialize our interfaces and class with the `articles` table in the database using Sequelize:

{% code title="models/articles.ts" %}

```javascript
import { Model, Sequelize, DataTypes, ModelCtor } from 'sequelize';

... //previous class and interfaces are hidden to ease reading

export default function(sequelize: Sequelize, dataTypes: typeof DataTypes): typeof Model {

  Article.init({
    id: {
      type: dataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    title: {
      type: new dataTypes.STRING(128),
      allowNull: true,
    },
    body: {
      type: new dataTypes.STRING(128),
      allowNull: true,
    },
  },{
    tableName: 'articles',
    underscored: true,
    modelName: 'articles',
    sequelize,
  });

  return Article;
}
```

{% endcode %}

{% hint style="warning" %}
Note that the function to initialize the model is the **default export** from the model file. This is mandatory and will be explained in the [next section](https://docs.forestadmin.com/woodshop/how-tos/translate-your-project-into-typescript/v8/migrate-sequelize-files#connect-to-your-databases).
{% endhint %}

And that's it! Your model definition is finished and you will be able to use it in your code soon, just go ahead to the [next section](https://docs.forestadmin.com/woodshop/how-tos/translate-your-project-into-typescript/v8/migrate-sequelize-files#connect-to-your-databases) to see how to instantiate your models.

## Connect to your databases

{% hint style="info" %}
Goals:

* Connect to each of your databases
* Import models definition in those connections
  {% endhint %}

The database connections are configured in `config/databases.js`. Here is an example of a fresh new generated project with one connection:

{% code title="config/databases.js" %}

```javascript
const path = require('path');

const databaseOptions = {
  logging: !process.env.NODE_ENV || process.env.NODE_ENV === 'development' ? console.log : false,
  pool: { maxConnections: 10, minConnections: 1 },
  dialectOptions: {},
};

if (process.env.DATABASE_SSL && JSON.parse(process.env.DATABASE_SSL.toLowerCase())) {
  const rejectUnauthorized = process.env.DATABASE_REJECT_UNAUTHORIZED;
  if (rejectUnauthorized && (JSON.parse(rejectUnauthorized.toLowerCase()) === false)) {
    databaseOptions.dialectOptions.ssl = { rejectUnauthorized: false };
  } else {
    databaseOptions.dialectOptions.ssl = true;
  }
}

module.exports = [{
  name: 'default',
  modelsDir: path.resolve(__dirname, '../models'),
  connection: {
    url: process.env.DATABASE_URL,
    options: { ...databaseOptions },
  },
}];
```

{% endcode %}

This file is responsible for providing the database connections with the corresponding options. In the end, this file should export an array of connections. If we translate this file into TypeScript, we have to export the same database structure, here is an example:

{% code title="config/databases.ts" %}

```typescript
import * as path from 'path';
import { Options } from "sequelize";
import { DatabaseConfiguration } from "forest-express-sequelize";

const databaseOptions: Options = {
  logging: !process.env.NODE_ENV || process.env.NODE_ENV === 'development' ? console.log : false,
  pool: { max: 10, min: 1 },
  dialectOptions: { },
};

if (process.env.DATABASE_SSL && JSON.parse(process.env.DATABASE_SSL.toLowerCase())) {
  const rejectUnauthorized = process.env.DATABASE_REJECT_UNAUTHORIZED;
  if (rejectUnauthorized && (JSON.parse(rejectUnauthorized.toLowerCase()) === false)) {
    databaseOptions.dialectOptions["ssl"] = { rejectUnauthorized: false };
  } else {
    databaseOptions.dialectOptions["ssl"] = true;
  }
}

const databasesConfiguration: DatabaseConfiguration[] = [{
  name: 'default',
  modelsDir: path.resolve(__dirname, '../models'),
  connection: {
    url: process.env.DATABASE_URL,
    options: { ...databaseOptions },
  },
}];

export default databasesConfiguration;
```

{% endcode %}

Now your connections are configured, let's load the models for every connection.

A generated project uses a dynamic model instantiation approach. To do so, the `models/index.js` file under the `models` folder is responsible for browsing your connections' configuration and models to load them. Here is an example of this generated file:

{% code title="models/index.js" %}

```javascript
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');

const databasesConfiguration = require('../config/databases');

const connections = {};
const db = {};

databasesConfiguration.forEach((databaseInfo) => {
  const connection = new Sequelize(databaseInfo.connection.url, databaseInfo.connection.options);
  connections[databaseInfo.name] = connection;

  const modelsDir = databaseInfo.modelsDir || path.join(__dirname, databaseInfo.name);
  fs
    .readdirSync(modelsDir)
    .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js')
    .forEach((file) => {
      try {
        const model = connection.import(path.join(modelsDir, file));
        db[model.name] = model;
      } catch (error) {
        console.error(`Model creation error: ${error}`);
      }
    });
});

Object.keys(db).forEach((modelName) => {
  if ('associate' in db[modelName]) {
    db[modelName].associate(db);
  }
});

db.objectMapping = Sequelize;
db.connections = connections;

module.exports = db;
```

{% endcode %}

Note that line 20 is the place where we should use the default export from our model files. At this line, this default export should be called and Sequelize will automatically detect that it is an initialization function, and will instantiate our `Article` class for example.\
Also, note that the association between models is done from line 28 to line 32. We should call the `associate` function we kept on our models at this same place. Otherwise, our models will not be linked to each other.

Finally, we still need to export at least Sequelize as `objectMapping` and the connections, the same way it is done in JavaScript.&#x20;

Here is an implementation of all those requirements in TypeScript:

{% hint style="warning" %}
Please read line 16 carefully and observe that we also filter`*.js.map` files. If you do use mapping files you need to skip them as in this example, to not consider them as model files to import.&#x20;
{% endhint %}

{% code title="models/index.ts" %}

```typescript
import { readdirSync } from "fs";
import { join } from 'path';
import * as Sequelize from 'sequelize';
import databasesConfiguration from "../config/databases";

const connections: Record<string, Sequelize.Sequelize> = {};
const objectMapping = Sequelize;
const models: Record<string, typeof Sequelize.Model> = {};

databasesConfiguration.forEach((databaseInfo) => {
  const connection = new Sequelize.Sequelize(databaseInfo.connection.url, databaseInfo.connection.options);
  connections[databaseInfo.name] = connection;

  const modelsDir = databaseInfo.modelsDir || join(__dirname, databaseInfo.name);
  readdirSync(modelsDir)
    .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js' && !file.includes('.map'))
    .forEach((file) => {
      try {
        const model = connection.import(join(modelsDir, file));
        models[model.name] = model;
      } catch (error) {
        console.error(`Model creation error: ${error}`);
      }
    });
});

Object.keys(models).forEach((modelName) => {
  if ('associate' in models[modelName]) {
    // @ts-ignore
    models[modelName].associate(models);
  }
});

export { objectMapping, models, connections };
```

{% endcode %}

Now we configured everything regarding connections and models, we can benefit from TypeScript and our models using:&#x20;

```javascript
const { Article } from '../models/article';

...

Artcile.findByPk(...)...
```

## Initialize your Liana

The `middlewares/forestadmin.js` is pretty straightforward and just needs to be translated from `.js` to `.ts` . See the following snippet:&#x20;

{% code title="middlewares/forestadmin.ts" %}

```javascript
import * as chalk from 'chalk';
import { join } from 'path';
import { init, LianaOptions } from "forest-express-sequelize";
import { objectMapping, connections } from '../models';
import { Application } from "express";

export = async function forestadmin(app: Application): Promise<void> {
  const lianaOptions: LianaOptions = {
    configDir: join(__dirname, '../forest'),
    envSecret: process.env.FOREST_ENV_SECRET,
    authSecret: process.env.FOREST_AUTH_SECRET,
    objectMapping,
    connections,
  }

  app.use(await init(lianaOptions));

  console.log(chalk.cyan('Your admin panel is available here: https://app.forestadmin.com/projects'));

  return;
};
```

{% endcode %}

## Override your routes

Nothing special needs to be done in your routes. Change the files extension to `.ts` , change the way dependencies are imported, and let yourself be guided by the Types we provide. Here is an example for the route to get an `Article`:

{% code title="routes/articles.ts" %}

```javascript
import * as express from 'express';
import { PermissionMiddlewareCreator, RecordsGetter } from "forest-express-sequelize";
import { Article } from "../models/article";

...

router.get('/articles', permissionMiddlewareCreator.list(), async (request: ForestRequest, response) => {
  const { user, query } = request;
  const recordsGetter = new RecordsGetter(Article, user, query);
  const articles = await recordsGetter.getAll(request.params);
  const articlesSerialized = await recordsGetter.serialize(articles);
  response.json(articlesSerialized);
});
```

{% endcode %}

{% hint style="warning" %}
Note the `request: ForestRequest` to have access to `user` and `query`
{% endhint %}

## Customize your collections

As for the [previous sections](https://docs.forestadmin.com/woodshop/how-tos/v7/migrate-sequelize-files#override-your-routes), nothing special needs to be changed to your collection configuration. Change the files extension to `.ts` , change the way dependencies are imported, and let yourself be guided by the Types we provide. Here is an example for the `Article` collection:

{% code title="forest/articles.ts" %}

```javascript
import { collection } from "forest-express-sequelize";

collection('articles', {
  actions: [],
  fields: [{
    field: 'Description',
    type: 'String',
  }],
  segments: [],
});
```

{% endcode %}
