# v6

In this example, we will guide you through the steps to turn your project into a TypeScript project. After the guide have been completed, you will benefit from strong type checking and code completion.

{% hint style="warning" %}
Use this example **only** for versions under 6
{% endhint %}

## Requirements

In this example, we will assume that you are familiar with TypeScript and that you have it installed. \
\
If this is not the case, simply run the following to get packages you will need in this Woodshop: `npm install --save-dev typescript tsc-watch nodemon`

To make it easy for you, we created our own typings to help you using our exposed tools using TypeScript. They are available as `@types/forest-express-sequelize` and `@types/forest-express-mongoose`. \
\
To get them, simply run: `npm install --save-dev @types/forest-express-[sequelize | mongoose]`

### Configuration

As for every TypeScript project, we need to create a configuration file. It helps the transpiler to know where to take the files from, and where to transpile them to.\
\
To have a smooth migration into TypeScript, we will use the following configuration:

{% hint style="info" %}
Add this file at the root of your project.
{% endhint %}

{% tabs %}
{% tab title="SQL" %}
{% code title="tsconfig.json" %}

```javascript
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "pretty": true,
    "sourceMap": false,
    "target": "es2017",
    "outDir": "./dist",
    "baseUrl": "./",
    "types" : ["node", "express", "forest-express-sequelize", "sequelize"],
    "allowJs": true
  },
  "include": ["./**/*", ".env"],
  "exclude": ["node_modules", "dist"]
}
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="tsconfig.json" %}

```javascript
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "pretty": true,
    "sourceMap": true,
    "target": "es2017",
    "outDir": "./dist",
    "baseUrl": "./",
    "types" : ["node", "express", "forest-express-mongoose", "mongoose"],
    "allowJs": true
  },
  "include": ["./**/*", ".env"],
  "exclude": ["node_modules", "dist"]
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

This tells the transpiler to take every file using `.ts` or `.js` (see `allowJs` option) as an extension, and to transpile them into a `./dist` folder. This is where your transpiled files will be. This type of configuration allows you to translate your app into TypeScript in dribs and drabs. No need to translate every file at once, change only the files that interest you.\
\
Now that your TypeScript transpiler is set, we need to update our `.package.json` with new scripts to fit with the new structure of our project.

{% code title="package.json" %}

```javascript
"scripts": {
  "start": "node ./dist/server.js",
  "legacy-start": "node server.js",
  "build": "tsc",
  "start-dev": "tsc-watch --project ./tsconfig.json --noClear --onSuccess \"nodemon --watch ./dist ./dist/server.js\""
},
```

{% endcode %}

Note the last script `start-dev` is the script to use while developing. It will transpile your sources every time you perform a change, and it will refresh your server.\
\
Finally, let's install the mandatory typings for a newly generated project, run this from a command prompt:

{% tabs %}
{% tab title="SQL" %}

```javascript
npm install --save-dev @types/node @types/express @types/sequelize @types/forest-express-sequelize
```

{% endtab %}

{% tab title="MongoDB" %}

```javascript
npm install --save-dev @types/node @types/express @types/mongoose @types/forest-express-mongoose
```

{% endtab %}
{% endtabs %}

And that's it! \
\
You are now able to code using TypeScript in your app. Simply change the extension from `.js` to `.ts` , change some code, and your file will be automatically handled.

## How it works

### Directory: /models

Depending on your ORM (sequelize, mongoose) your models' configuration will change.

{% tabs %}
{% tab title="SQL" %}
**Sequelize Models**

The idea here is to create a TypeScript class which will describe how your data should look like, and then init your actual model using sequelize.\
Let's take a simple `user` model, generated by Lumber:

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

```typescript
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  
  const Users = sequelize.define('users', {
    firstname: {
      type: DataTypes.STRING,
    },
    lastname: {
      type: DataTypes.STRING,
    },
    createdAt: {
      type: DataTypes.DATE,
    },
    updatedAt: {
      type: DataTypes.DATE,
    },
  }, {
    tableName: 'users',
    schema: process.env.DATABASE_SCHEMA,
  });

  return Users;
};

```

{% endcode %}

Let's create a TypeScript class to describe our data, based on this model. Create a folder alongside `models` to store interfaces, and create the `users.ts` interfaces:

{% code title="interfaces/users.ts" %}

```typescript
import { Model } from "sequelize";

export default class Users extends Model {
    public id!: number;
    public firstname!: string;
    public lastname!: string;
    public readonly createdAt: Date;
    public readonly updatedAt: Date;
}
```

{% endcode %}

�Now our data are typed, let's go back to our model `models/users.js` , switch the file to TypeScript, and change its content with the following:

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

```typescript
import Users from '../interfaces/users';

export default (sequelize, DataTypes) => {
  Users.init(
    {
      id: {
        type: DataTypes.INTEGER.UNSIGNED,
        autoIncrement: true,
        primaryKey: true
      },
      firstname: {
        type: new DataTypes.STRING(128),
        allowNull: false
      },
      lastname: {
        type: new DataTypes.STRING(128),
        allowNull: false
      }
    },{
        tableName: "users",
        modelName: 'users',
        sequelize,
    }
  );

  return Users;
}
```

{% endcode %}

Note the following:

* The import statement has changed.
* We force the `modelName` property to ensure our model sticks to its previous name.&#x20;

And that's it! \
\
Your model is now defined in TypeScript, you can use it anywhere you want by importing it using:&#x20;

```typescript
import users from './interfaces/users';
```

You can find more resource here about how to use TypeScript with the sequelize ORM:

* <https://vivacitylabs.com/setup-typescript-sequelize/>
* <https://sequelize.org/master/manual/typescript.html>
  {% endtab %}

{% tab title="MongoDB" %}
**Mongoose Models**

The idea here, is to create a schema as you would do in JS, but also an interface to force the typings of your models. Let's take a simple `user` model, generate by Lumber:

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

```typescript
const mongoose = require('mongoose');

const schema = mongoose.Schema({
  'firstName': String,
  'lastName': Date,
}, {
  timestamps: false,
});

module.exports = mongoose.model('users', schema, 'users');
```

{% endcode %}

A translation to typescript would look like the following:

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

```typescript
import { Schema, Document, model} from 'mongoose';

interface IUser extends Document {
    firstName: string;
    lastName: string;
}

const schema = new Schema({
    'firstName': String,
    'lastName': Boolean,
}, {
    timestamps: false,
});

export default model<IUser>('users', schema, 'users');
```

{% endcode %}

Note the following:

* The import statement has changed
* We create an interface to specify attribute type
* We apply the types at the model creation

And that's it! your model is now defined in typescript, you can use it anywhere you want by importing it using:&#x20;

```typescript
import users from '../models/users';
```

You can find more resource here about how to use typescript with the mongoose ODM:

* <https://medium.com/@tomanagle/strongly-typed-models-with-mongoose-and-typescript-7bc2f7197722>
* <https://medium.com/@agentwhs/complete-guide-for-typescript-for-mongoose-for-node-js-8cc0a7e470c1>
* <https://github.com/tomanagle/Mongoose-TypeScript-example>
  {% endtab %}
  {% endtabs %}

### Directory: /routes

Routes consist of a simple router, handling CRUD routes, and using models/record tools to manipulate data.

So there is nothing special to do in this section, but changing the Javascript import statements to TypeScript ones.\
\
Here are the **default routes** created for the collection `users`:

{% tabs %}
{% tab title="SQL" %}
{% code title="routes/users.js" %}

```javascript
const express = require('express');
const { PermissionMiddlewareCreator } = require('forest-express-sequelize');
const { users } = require('../models');

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('users');

// Create a User
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  next();
});

// Update a User
router.put('/users/:recordId', permissionMiddlewareCreator.update(), (request, response, next) => {
  next();
});

...

// Delete a list of Users
router.delete('/users', permissionMiddlewareCreator.delete(), (request, response, next) => {
  next();
});

module.exports = router;
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="routes/users.js" %}

```javascript
const express = require('express');
const { PermissionMiddlewareCreator } = require('forest-express-mongoose');
const { users } = require('../models');

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('users');

// Create a User
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  next();
});

// Update a User
router.put('/users/:recordId', permissionMiddlewareCreator.update(), (request, response, next) => {
  next();
});

...

// Delete a list of Users
router.delete('/users', permissionMiddlewareCreator.delete(), (request, response, next) => {
  next();
});

module.exports = router;
```

{% endcode %}
{% endtab %}
{% endtabs %}

�And here is how your routes file should look like if you want to **use TypeScript**:

{% tabs %}
{% tab title="SQL" %}
{% code title="routes/users.ts" %}

```typescript
import * as express from 'express';
import { PermissionMiddlewareCreator } from 'forest-express-sequelize';
import users from '../interfaces/users';

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('users');

// Create a User
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  next();
});

// Update a User
router.put('/users/:recordId', permissionMiddlewareCreator.update(), (request, response, next) => {
  next();
});

// Delete a User
router.delete('/users/:recordId', permissionMiddlewareCreator.delete(), (request, response, next) => {
  // Learn what this route does here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/routes/default-routes#delete-a-record
  next();
});

...

// Delete a list of Users
router.delete('/users', permissionMiddlewareCreator.delete(), (request, response, next) => {
  next();
});

export default router;
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="routes/users.ts" %}

```typescript
import * as express from 'express';
import { PermissionMiddlewareCreator } from 'forest-express-mongoose';
import users from '../models/users';

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('users');

// Create a User
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  next();
});

// Update a User
router.put('/users/:recordId', permissionMiddlewareCreator.update(), (request, response, next) => {
  next();
});

// Delete a User
router.delete('/users/:recordId', permissionMiddlewareCreator.delete(), (request, response, next) => {
  // Learn what this route does here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/routes/default-routes#delete-a-record
  next();
});

...

// Delete a list of Users
router.delete('/users', permissionMiddlewareCreator.delete(), (request, response, next) => {
  next();
});

export default router;
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Directory: /forest

There is nothing special to do here. Translating your smart configuration (actions, fields, and segments) in `.ts` is as simple as just renaming the file and updating your imports.

Let's take the following example:

{% tabs %}
{% tab title="SQL" %}
{% code title="forest/users.js" %}

```javascript
const { collection } = require('forest-express-sequelize');

collection('users', {
  actions: [{
    name: "Promote to admin",
    type: 'single',
  }],
  fields: [],
  segments: [],
});
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="forest/users.js" %}

```javascript
const { collection } = require('forest-express-mongoose');

collection('users', {
  actions: [{
    name: "Promote to admin",
    type: 'single',
  }],
  fields: [],
  segments: [],
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

�Translating this file into TypeScript will give the following:

{% tabs %}
{% tab title="SQL" %}
{% code title="forest/users.ts" %}

```typescript
import { collection } from 'forest-express-sequelize';

collection('users', {
  actions: [{
    name: "Upgrade to admin",
    type: 'single',
  }],
  fields: [],
  segments: [],
});
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="forest/users.ts" %}

```typescript
import { collection } from 'forest-express-mongoose';

collection('users', {
  actions: [{
    name: "Upgrade to admin",
    type: 'single',
  }],
  fields: [],
  segments: [],
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Debugging your application

If you want to ease debugging sessions on runtime, you can ask TypeScript to keep a link between your `.ts` and `.js` files by using `.js.map` files. These files are source map files, that let debugging tools map between the emitted JavaScript code and the TypeScript source files that created it. Many debuggers (e.g. Visual Studio, Chrome's dev tools...) can consume these files so you can debug the TypeScript file instead of the JavaScript file.\
\
You can ask TypeScript to generate those files by changing the configuration with the following:

{% code title="tsconfig.json" %}

```javascript
{
  "compilerOptions": {
    ...
    "sourceMap": true,
    ...
  },
  ...
}
```

{% endcode %}

Now that your `.js.map` files are generated, we need to prevent your generated project from considering `.js.map files` under your **models directory** as models. To do it, reach `models/index.js` and change the following original code:

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

```javascript
...
fs
  .readdirSync(__dirname)
  .filter(function (file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js');
  })
  .forEach(function (file) {
    try {
      var model = sequelize['import'](path.join(__dirname, file));
      db[model.name] = model;
    } catch (error) {
      console.error('Model creation error: ' + error);
    }
  });
...
```

{% endcode %}

with this:

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

```javascript
...
fs
  .readdirSync(__dirname)
  .filter(function (file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js') && file.indexOf('.js.map') === -1;
  })
  .forEach(function (file) {
    try {
      var model = sequelize['import'](path.join(__dirname, file));
      db[model.name] = model;
    } catch (error) {
      console.error('Model creation error: ' + error);
    }
  });
...
```

{% endcode %}

{% hint style="warning" %}
We now filter actual model files correctly by not taking `.js.map` files into account.
{% endhint %}
