mdawar.dev

A blog about programming, Web development, Open Source, Linux and DevOps.

How to Move a File in Node.js?

(Updated: )

To move a file using Node.js you can use the fs.rename function:

js
// Using the fs Promises API
import fs from 'node:fs/promises';

const oldPath = '/path/to/file.txt';
const newPath = '/path/to/another/directory/file.js';

try {
  // Top level await is available without a flag since Node.js v14.8
  await fs.rename(oldPath, newPath);

  // Handle success (fs.rename resolves with `undefined` on success)
  console.log('File moved successfully');
} catch (error) {
  // Handle the error
  console.error(error);
}

Note: If the destination path already exists, it will be overwritten.

Handle non-existent destination directory

If the destination directory does not exist, you will get an error with the code ENOENT (no such file or directory), so we have to take this case into consideration.

js
import fs from 'node:fs/promises';
import path from 'node:path';

const oldPath = '/path/to/file.js';
const newPath = '/path/to/another/directory/file.js';

// Create a helper function
async function moveFile(oldPath, newPath) {
  // 1. Create the destination directory if it does not exist
  // Set the `recursive` option to `true` to create all the subdirectories
  await fs.mkdir(path.dirname(newPath), { recursive: true });

  // 2. Rename the file (move it to the new directory)
  // Return the promise
  return fs.rename(oldPath, newPath);
}

try {
  await moveFile(oldPath, newPath);

  // Handle success
  console.log('File moved successfully');
} catch (error) {
  // Handle the error
  console.error(error);
}

Handle moving files across different mount points

The rename function does not work across different mount points, you will get the error code EXDEV (cross-device link not permitted), for example when using Docker volumes you will hit this error.

As per the rename manual page :

EXDEV oldpath and newpath are not on the same mounted filesystem. (Linux permits a filesystem to be mounted at multiple points, but rename() does not work across different mount points, even if the same filesystem is mounted on both.)

To handle this case, you can fallback to copying the file to the new path and deleting the original file:

js
import fs from 'node:fs/promises';
import path from 'node:path';

const oldPath = '/path/to/file.js';
const newPath = '/path/to/another/directory/file.js';

// The final version of the move function
async function moveFile(oldPath, newPath) {
  // 1. Create the destination directory
  // Set the `recursive` option to `true` to create all the subdirectories
  await fs.mkdir(path.dirname(newPath), { recursive: true });

  try {
    // 2. Rename the file (move it to the new directory)
    await fs.rename(oldPath, newPath);
  } catch (error) {
    if (error.code === 'EXDEV') {
      // 3. Copy the file as a fallback
      await fs.copyFile(oldPath, newPath);
      // Remove the old file
      await fs.unlink(oldPath);
    } else {
      // Throw any other error
      throw error;
    }
  }
}

try {
  await moveFile(oldPath, newPath);

  // Handle success
  console.log('File moved successfully');
} catch (error) {
  // Handle the error
  console.error(error);
}