This repository contains information about migrating the acbs-database (backend and frontend) from JavaScript to TypeScript.
In addition to the dependencies needed to install and deploy acbs-database (which include Node.js, Express.js, and MongoDB), you'll also need to install:
TypeScript is a high-level programming language that adds static typing with optional type annotations to JavaScript.
TypeScript type definitions (declarations) provides a way to declare the existence of some types or values without actually providing implementations for those values.
In this case, we're looking at migrating the acbs-database backend from JavaScript to TypeScript. The frontend can be migrated in a similar way, following the algorithm described below.
Let's get started! Assuming the acbs-database backend is located in the acbs-database/acbs-database-backend directory. Navigate to it and install the TypeScript compiler:
cd acbs-database/acbs-database-backend
npm install typescript --save-devThen initialize the TypeScript configuration:
npx tsc --initThis will generate a tsconfig.json file, which specifies the root files and compiler options required to compile the project. Make the following changes to this file:
{
"compilerOptions": {
"target": "ES2017",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}After initializing the TypeScript configuration, install the necessary type definitions (declarations) according to the dependencies specified in the package.json file:
npm install --save-dev @types/node @types/express @types/mongoose @types/cors @types/bcryptjs @types/jsonwebtokenNow, following common conventions, move the folders containing the application's source code files into the src folder:
mkdir src
move models src & move controllers src & move routes src & move middleware src & move app.js src
cd srcSince the TypeScript compiler can only work with TypeScript files, you need to change the file extensions from .js to .ts.
You can change the extension of all .js files using the using the following command:
for /r %f in (*.js) do ren "%f" "%~nf.ts"Or using PowerShell command (recommended):
Get-ChildItem -Path . -Recurse -File -Include '*.ts' | ForEach-Object {
Rename-Item -LiteralPath $_.FullName -NewName ($_.BaseName + '.ts')
}After changing file extensions, your TypeScript files will have many type errors. Don't worry, they will be fixed as you rewrite your source code from JavaScript to TypeScript.
Then you need to rewrite the source code from JavaScript to TypeScript file by file, adding type annotations to variables, function parameters, and function return values. This will help the TypeScript compiler understand our code and identify potential type errors.
Let's take the file formerly app.js as an example. Its source code, written in JavaScript, looks like this:
const express = require('express');
const cors = require('cors');
const app = express();
const mongoose = require("mongoose");
const apiRouter = require("./routes/api.router.js");
app.use(express.json());
app.use(cors({
origin: 'http://localhost',
methods: 'GET, PUT, POST, DELETE',
allowedHeaders: 'Content-Type, Authorization'
}));
app.use('/v1/database', apiRouter);
app.use(function (req, res, next) {
res.status(404).send("Not Found");
});
async function main() {
try {
await mongoose.connect("mongodb://localhost:27017/acbs");
app.listen(3000);
console.log("Server started...");
}
catch (err) {
return console.log(err);
}
}
main();
process.on("SIGINT", async () => {
await mongoose.disconnect();
console.log("Server stopped");
process.exit();
});Let's rewrite it in TypeScript using type annotations:
import express, {Application, NextFunction, Request, Response} from 'express';
import cors from 'cors';
const app: Application = express();
import mongoose from 'mongoose';
import apiRouter from './routes/api.router';
app.use(express.json());
app.use(cors({
origin: 'http://localhost',
methods: ['GET', 'PUT', 'POST', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.use('/v1/database', apiRouter);
app.use(function (req: Request, res: Response, next: NextFunction) {
res.status(404).send("Not Found");
});
async function main(): Promise<void> {
try {
await mongoose.connect("mongodb://localhost:27017/acbs");
app.listen(3000);
console.log("Server started...");
}
catch (err) {
return console.log(err);
}
}
main();
process.on("SIGINT", async () => {
await mongoose.disconnect();
console.log("Server stopped");
process.exit();
});It's also worth mentioning that when developing in TypeScript, the best practice is to use interfaces for Mongoose models.
Using TypeScript interfaces with Mongoose models is a best practice that provides significant benefits by creating a strongly-typed, predictable, and maintainable codebase. The TypeScript interface provides type-checking for your code, while the Mongoose schema defines the data structure for the database.
For example, we had an event.model.js model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const eventScheme = new Schema({
id: Number,
title: String,
description: String,
date: String,
tag: String
});
module.exports = mongoose.model("Event", eventScheme);When developing in TypeScript, you need to create an src/interfaces folder and place an event.interface.ts file in it with the following contents:
import { Document } from 'mongoose';
export interface Event extends Document {
id: number;
title: string;
description: string;
date: string;
tag: string;
}And then use the interface you created in the event.model.ts file:
import mongoose, { Schema, Model } from 'mongoose';
import { Event } from '../interfaces/event.interface';
const EventSchema: Schema = new Schema({
id: { type: Number, required: true, unique: true },
title: { type: String },
description: { type: String },
date: { type: String },
tag: { type: String },
});
const Event: Model<Event> = mongoose.model<Event>('Event', EventSchema);
export default Event;Once all of the application's source code has been rewritten in TypeScript, it can be compiled into .js files:
npx tscAnd then launched using the command:
cd dist
npx nodemon app.jsOr you can run the .ts file directly using the following command:
npx tsx app.tsIn case you want to run an application using nodemon, you need to create a nodemon.json file with the following content:
{
"watch": ["src"],
"ext": "ts",
"exec": "ts-node ./src/app.ts"
}Then make the following changes to the scripts block of the package.json file:
"scripts": {
"dev": "nodemon src/app.ts"
}And then run the application using the following command:
npm run devCongratulations! We've just migrated our backend codebase from JavaScript to TypeScript. This will not only allow us to detect errors at compile time rather than runtime, but also improve code quality and application scalability.