Let's create a React File Manager Chapter XVI: Create Server Requests
Tags: express, typescript, mongez, javascript
Published: 2022-09-15 18:21:02

---
title: Let's create a React File Manager Chapter XVI: Create Server Requests
published: true
description: 
series: File Manager React Mongez
tags: express, typescript, mongez, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aaxrogi0uf39gqllexjt.png
# Use a ratio of 100:42 for best results.
---

This chapter is about creating server requests, let's start with the listing directory.

## No Security

This project is not about security, so we'll not be adding any security layer to our server, we'll just make it work.

So don't use it in production, it's just for learning purposes.

## Defining Routes

Let's start by listing the directory, we'll create a new route in our server, and we'll call it `/file-manager` and it will accept a `path` as query string.

Let's create `src/routes.ts` file and add the following code:

```ts
// routes.ts
import { Express } from "express";

export default function listRoutes(app: Express) {
  //
}
```

Now let's import it from our `src/index.ts` file and call it.

```ts
// index.ts
// imported express server
import express, { Express } from "express";
👉🏻 import listRoutes from "./routes";

// port to run the server
const port = 8001;

// 👇🏻 create express app
const app: Express = express();

listRoutes(app);

// start the Express server
app.listen(port, () => {
  console.log(`⚡️[server]: Server is running at http://localhost:${port}`);
});
```

Now let's define our routes list

```ts
import { Express } from "express";

import fileManager from "./controllers/file-manager";

export default function listRoutes(app: Express) {
  // Let's define our routes
  // list directory nodes
  app.get("/file-manager", fileManager.list);
  // create a directory node
  app.post("/file-manage/directory", fileManager.createDirectory);
}
```

We created two requests, one for listing the directory and the other for creating a directory.

Now let's create a new controllers directory which will manage our requests, create `src/controllers/file-manager` directory and inside it let's create an index file

```ts
// src/controllers/file-manager/index.ts
const fileManager = {
  list: listDirectory,
  createDirectory: createDirectory,
};

export default fileManager;
```

We just created a simple object that contains our route mapping, ,`list` to list our directory and `create` to create a directory.

Now let's create a file for each of them, let's start with `listDirectory` file.

```ts
// src/controllers/file-manager/listDirectory.ts
import { Request, Response } from "express";

/**
 *List directories
 */
export default async function listDirectory(request: Request, response: Response) {
  //
}
```

Now let's define the second function `createDirectory`

```ts
// src/controllers/file-manager/createDirectory.ts
import { Request, Response } from "express";

/**
 * Create a directory
 */
export default async function createDirectory(request: Request, response: Response) {
  //
}
```

Let's import it now in our `src/controllers/file-manager/index.ts` file

```ts
// src/controllers/file-manager/index.ts
import createDirectory from "./createDirectory";
import listDirectory from "./listDirectory";

const fileManager = {
  list: listDirectory,
  createDirectory: createDirectory,
};

export default fileManager;
```

## Express request and response

As you can see above, when we define a function for a route we always receive two parameters, `request` and `response`, these are the request and response objects from Express as it injects to our function directly when the request route is match with its corresponding function.

## Listing Directory

Now let's list our directory, we'll use `@mongez/fs` module to list our directory.

The path of the directory will be relative to `/data` directory in our root directory, so if we want to list the `/data` directory we'll just use  `/` as the default path.

```ts
// src/controllers/file-manager/listDirectory.ts
import { Request, Response } from "express";

/**
 *List directories
 */
export default async function listDirectory(
  request: Request,
  response: Response
) {
  // get the directory path from the request query string
  // let's set `/` as the default path
  const { path = '/' } = request.query;
}
```

## Config file

Before we continue, we need to get the dataDirectory, we defined it in our generator file, let's create a config.ts file and add it there then we can import it in our files.

```ts
// src/config.ts
export const root = process.cwd();
export const dataDirectory = root + "/data";
```

Now we import it in our generator file.

```ts
// src/generator.ts
import { faker } from "@faker-js/faker";
import fs from "@mongez/fs";
// import it here
import { dataDirectory } from "./config";

function start() {
  make(3, 3, dataDirectory);
}

start();

function makeFiles(maxFilesPerDirectory: number, path: string) {
  const filesList: string[] = [];

  while (filesList.length < maxFilesPerDirectory) {
    const file = faker.system.fileName();
    const filePath = path + "/" + file;
    if (!filesList.includes(filePath) && !fs.isFile(filePath)) {
      filesList.push(filePath);
      fs.put(filePath, faker.lorem.paragraphs());
    }
  }
}

function make(
  maxDirectories: number,
  maxFilesPerDirectory: number,
  path: string
) {
  const directoriesList: string[] = [];

  while (directoriesList.length < maxDirectories) {
    const directory = faker.system.directoryPath().split("/")[1];
    const directoryPath = path + "/" + directory;
    if (
      !directoriesList.includes(directoryPath) &&
      !fs.isDirectory(directoryPath)
    ) {
      directoriesList.push(directoryPath);
      fs.makeDirectory(directoryPath, 777);
      makeFiles(maxFilesPerDirectory, directoryPath);
      // to make recursive directories and files, you'll need to stop the script after some time
      //   and run it again
      //   make(maxDirectories, maxFilesPerDirectory, directoryPath);
    }
  }
}
```

Now let's get the full path of the directory and check if it exists.

```ts
// src/controllers/file-manager/listDirectory.ts
import fs from "@mongez/fs";
import { Request, Response } from "express";
import { dataDirectory } from "../../config";

/**
 *List directories
 */
export default async function listDirectory(
  request: Request,
  response: Response
) {
  // get the directory path from the request query string
  // let's set `/` as the default path
  const { path = "/" } = request.query;
  const directoryPath = dataDirectory + path; // the full path to the directory

  if (!fs.isDirectory(directoryPath)) {
    return response.status(404).json({
      message: "Directory not found",
    });
  }
}
```

Here we got the full path of the given directory, then we checked if the directory does not exist we'll return a 404 response with a message to indicate directory is not found.

## Paths utility

As we can see we added full directory path as follows:

```ts
const directoryPath = dataDirectory + path;
```

Let's make a function to handle it properly.

Now let's create a utility file to handle our paths, let's create `src/utils/paths.ts` file

```ts
// src/utils/paths.ts
import path from "path";
import { dataDirectory } from "../config";

export function dataPath(relativePath: string): string {
  return path.resolve(dataDirectory, relativePath);
}
```

Here we created a method to get the full path of the given relative path, we used `path.resolve` to resolve the full path to generate it properly.

Let's use it now in our `listDirectory` function.

```ts
// src/controllers/file-manager/listDirectory.ts

import fs from "@mongez/fs";
import { Request, Response } from "express";
import { dataPath } from "../../utils/paths";

/**
 *List directories
 */
export default async function listDirectory(
  request: Request,
  response: Response
) {
  // get the directory path from the request query string
  // let's set `/` as the default path
  const { path = "/" } = request.query;
  const directoryPath = dataPath(path); // the full path to the directory

  if (!fs.isDirectory(directoryPath)) {
    return response.status(404).json({
      message: "Directory not found",
    });
  }
}
```

## Relative Paths

As we can see our paths have `../../` which as you know really hate, so let's install [link-module-alias](https://www.npmjs.com/package/link-module-alias)

`yarn add link-module-alias -D`

Open now `package.json` and update it to look like this

```json
{
  "name": "backend",
  "version": "1.0.0",
  "main": "src/index.ts",
  "license": "MIT",
  "author": {
    "name": "Hasan Zohdy",
    "email": "hassanzohdy@gmail.com"
  },
  "scripts": {
    // 👇🏻  add this
    "postinstall": "npx link-module-alias",
    "generate": "npx ts-node ./src/generator.ts",
    "start": "npx ts-node ./src/index.ts"
  },
   // 👇🏻 and this
  "_moduleAliases": {
    "app": "./src"
  },
  "dependencies": {
    "@faker-js/faker": "^7.5.0",
    "@mongez/fs": "^1.0.3",
    "express": "^4.18.1"
  },
  "devDependencies": {
    "@types/express": "^4.17.14",
    "@types/node": "^18.7.18",
    "link-module-alias": "^1.2.0",
    "ts-node": "^10.9.1",
    "tslib": "^2.4.0",
    "typescript": "^4.8.3"
  }
}
```

Final step is to run command `postinstall` to link the aliases.

`yarn postinstall`

If you see this, then everything is ok

![postinstall](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z9fwmn7kbxwexqmhq1ta.png)

> **Tip** Why we named the command `postinstall`? because this is a special command that will be executed after installing the dependencies, so we don't need to run it manually.

Now let's update or listDirectory function to use the new relative path.

```ts
// src/controllers/file-manager/listDirectory.ts
import fs from "@mongez/fs";
👉🏻 import { dataPath } from "app/utils/paths";
import { Request, Response } from "express";

/**
 *List directories
 */
export default async function listDirectory(
  request: Request,
  response: Response
) {
  // get the directory path from the request query string
  // let's set `/` as the default path
  const { path = "/" } = request.query;
  const directoryPath = dataPath(path); // the full path to the directory

  if (!fs.isDirectory(directoryPath)) {
    return response.status(404).json({
      message: "Directory not found",
    });
  }
}
```

If you get an error from typescript that the function can not accept the path, we can cast it to string

![Path Error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybk5c22yqiww8r9vcf3j.png)

```ts
import fs from "@mongez/fs";
import { dataPath } from "app/utils/paths";
import { Request, Response } from "express";

/**
 *List directories
 */
export default async function listDirectory(
  request: Request,
  response: Response
) {
  // get the directory path from the request query string
  // 👇🏻 let's set `/` as the default path
  const path = (request.query.path as string) || "/";
  const directoryPath = dataPath(path); // the full path to the directory

  if (!fs.isDirectory(directoryPath)) {
    return response.status(404).json({
      message: "Directory not found",
    });
  }
}
```

Now let's list our directories inside the root path of data directory.

```ts
// src/controllers/file-manager/listDirectory.ts

...
  // get the directory content
  const children = fs.list(directoryPath);

  return response.json({
    node: {
      path,
      name: path.split("/").pop(),
      children,
    },
  });
```

Now let's start the server again and test it.

Navigate to our browser to `http://localhost:8001/file-manager?path=/` and you'll see the following response:

The output will be like this

![Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8m48xh1ll53undzktyl.png)

Why? because we didn't resolve the paths right, go to `dataPath` function and try to log the output of it.

It will be `/`!

Let's fix it by making sure the relative path start with `./`

So we need to remove any `/` and add `./` if it doesn't start with it.

Install `@mongez/reinforcements` so we can use some good utilities from it.

`yan add @mongez/reinforcements`

Now let's modify the `dataPath` function to look like this

```ts
// src/utils/paths.ts
import { ltrim } from "@mongez/reinforcements";
import path from "path";
import { dataDirectory } from "../config";

export function dataPath(relativePath: string): string {
  relativePath = ltrim(relativePath, "/");
  return path.resolve(dataDirectory, relativePath);
}
```

> [ltrim](https://github.com/hassanzohdy/reinforcements#left-trimming-values-from-string) removes the given string from the beginning of the string, so we will remove `/` to make sure the path is properly set.

Now when we restart the server, we can see results are coming properly.

![Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z302nlqm9d7nys85dzay.png)

In our next chapter, we'll create our node properly and distinguish between children files and directories and create `createDirectory` request as well.

## Article Repository

You can see chapter files in [Github Repository](https://github.com/hassanzohdy/file-manager-react/tree/16-create-server-reqeusts)

> Don't forget the `main` branch has the latest updated code.

## Tell me where you are now

If you're following up with me this series, tell me where are you now and what you're struggling with, i'll try to help you as much as i can.

Salam.