9 - Setting up

This is the first section of the third part in this book. In this part, we'll do spend more time coding to prepare ourselves for migrating the file.io app vanilla JS app to React. We've already decomposed the file.io into components. We've a good idea about the app design. We're ready with the list of components.

Development environment

Let's start the work by setting up the environment & dependencies.

We'll create two folders in the root directory:

  • server: This will contain the server code. This will run separately. The server code will be similar to the server of vanilla app except for a couple of server routes that are not required in React.

  • client: This will contain the React code. The initial code will be generated using create-react-app. Some code will be removed that we don't need. Additional dependencies will be added.

$ cd learn-react-by-example-file-io-app
$ cd react-app
$ mkdir server
$ npx create-react-app client
Creating a new React app in /Users/mayankc/Work/source/React-learning/learn-react-by-example-file-io-app/react-app/client.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...


added 1425 packages in 2m

Installing template dependencies using npm...

added 62 packages, and changed 1 package in 2s

Removing template package using npm...

removed 1 package, and audited 1487 packages in 1s

6 high severity vulnerabilities

Success! Created client at /Users/mayankc/Work/source/React-learning/learn-react-by-example-file-io-app/react-app/client
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd client
  npm start

Happy hacking!

The base directories have been set up.

Server

Now, let's copy the server code from the vanilla app. The server app for React based file.io doesn't need the following:

  • Static file server (handled separately by client part of this app)

  • Redirection of / to /upload.html and /:file to /download.html. First, there are no such files like upload.html or download.html. Second, this will be taken care by a package called react-router which is part of the client app.

The server port is changed from 3000 to 3001 because port 3000 is used by webpack development server.

server.mjs

import express from "express";
import multer from "multer";
import crypto from "crypto";
import { nanoid } from "nanoid";
import { readFile, stat, unlink, writeFile } from "fs/promises";

const storage = multer.memoryStorage();
const app = express();
const upload = multer({ storage });

const port = 3001;
const algorithm = "aes-256-ctr";
const uploadPath = process.cwd() + "/uploads";

let key = "file-encryption-key";
key = crypto.createHash("sha256").update(key).digest("base64").substr(0, 32);
const iv = crypto.randomBytes(16);

app.put("/api/files", upload.single("uploadedFile"), async (req, res) => {
  const fileId = nanoid(6);
  const fileName = `${uploadPath}/${fileId}`;
  await writeFile(fileName, encrypt(req.file.buffer));
  const metaFileName = `${fileName}.meta`;
  await writeFile(
    metaFileName,
    JSON.stringify({
      fileName: req.file.originalname,
      fileType: req.file.mimetype,
      fileSize: req.file.size,
    }),
  );
  res.json({ fileUrl: `http://localhost:3000/${fileId}` });
});

app.get("/api/files/:fileId/meta", async (req, res) => {
  const fileId = req.params.fileId;
  const fileName = `${uploadPath}/${fileId}.meta`;
  try {
    const fileMeta = JSON.parse(await readFile(fileName, "utf-8"));
    res.json(fileMeta);
  } catch (e) {
    res.status(404).send();
  }
});

app.get("/api/files/:fileId", async (req, res) => {
  const fileId = req.params.fileId;
  const fileName = `${uploadPath}/${fileId}`;
  try {
    if (await stat(fileName)) {
      const encFileBuf = await readFile(fileName);
      const decFileBuf = decrypt(encFileBuf);
      await writeFile(fileName, decFileBuf);
      res.sendFile(fileName);
      res.on("finish", async () => {
        await unlink(fileName);
        await unlink(`${fileName}.meta`);
      });
    }
  } catch (e) {
    console.log(e);
    res.status(404).send();
  }
});

app.listen(port, () => {
  console.log(`File.io vanilla app listening on port ${port}`);
});

const encrypt = (buffer) => {
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  const result = Buffer.concat([cipher.update(buffer), cipher.final()]);
  return result;
};

function decrypt(buffer) {
  let encryptedBuffer = Buffer.from(buffer);
  let decipher = crypto.createDecipheriv(algorithm, key, iv);
  let decrypted = decipher.update(encryptedBuffer);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return decrypted;
}

The server side part (or you can call it the backend part) of the React file.io app needs to be started in a separate terminal. This is different from the vanilla app where the server provided both the client and API code.

You can open a separate terminal and run:

learn-react-by-example-file-io-app/react-app/server: npm start

> react-server-app@1.0.0 start
> node --watch server.mjs

(node:42163) ExperimentalWarning: Watch mode is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
File.io vanilla app listening on port 3001

Client or React

The client code has been generated by the create-react-app utility. We need to make a couple of changes to it:

  • Remove the code/files that we don't need

  • Add additional dependencies like react router, bootstrap, QR generator, and sweetalert

After removing the unwanted files, the client code looks something like this:

We'll need to add four dependencies to the project:

  • React router

  • Bootstrap

  • QR generator

  • Sweet alert

In the vanilla world, we were loading bootstrap, QR & sweetalert from CDN. Of course, there was no react router in the vanilla world. We can use CDNs in React world too, but we don't have to. We can use NPM packages for all the required dependencies. Later, these dependencies will get clubbed using webpack.

The following are the instructions to install required dependencies:

react-app/client: npm i react-router-dom --save

added 3 packages, and audited 1490 packages in 2s

~/Work/source/React-learning/learn-react-by-example-file-io-app/react-app/client: npm i bootstrap react-bootstrap --save

added 19 packages, and audited 1509 packages in 3s

~/Work/source/React-learning/learn-react-by-example-file-io-app/react-app/client: npm i react-qr-code --save

added 2 packages, and audited 1511 packages in 6s

~/Work/source/React-learning/learn-react-by-example-file-io-app/react-app/client: npm i sweetalert2 sweetalert2-react-content --save

added 2 packages, and audited 1513 packages in 2s

Finally, let's update the app by:

  • Removing App.css

  • Removing generated code from App.js

  • Importing bootstrap at this level because bootstrap is a global CSS

  • Testing out bootstrap by adding a simple HTML code

  • Just for verifying the dependencies, we'll also import QRCode component from the react-qr-code package. This component can be used just like any other react component. Later, we'll remove QRCode beacuse QR's usage is only inside the FileAccess component.

This needs a quick check. Let's open the page and find it out.

We can see that the imported bootstrap CSS is working fine. The famous btn-primary in visible in the right font and style. The QR code component is also able to generate a QR code. We're ready with the environment for the file.io app.

--

That's all about setting up the app for migration. In the next section, we'll cover routing.

Last updated