Categories
JavaScript Nodejs

Node.js FS Module — Creating Directories

Manipulating files and directories are basic operations for any program. Since Node.js is a server-side platform and can interact with the computer that it’s running on directly, being able to manipulate files is a core feature.

Fortunately, Node.js has the fs module built into its library. It has many functions that can help with manipulating files and folders. File and directory operations that are supported include basic ones like manipulating and opening files in directories.

Likewise, it can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API that has functions that support promises.

Also, it can show statistics for a file. Almost all the file operations that we can think of can be done with the built-in fs module. In this article, we will create directories with the mkdir and mkdtemp family of functions to create normal directories and temporary directories respectively.

Creating Permanent Directories with fs.mkdir Family of Functions

To create permanent directories, we can use the mkdir function to create them asynchronously. It takes 3 arguments. The first argument is the path object, which can be a string, a Buffer object or an URL object.

The second argument is an object with various properties that we can set as options.

The recursive property is a boolean property that lets us create all the levels of the directory if not all the levels exist. The default value of the recursive property is false .

The mode property is an octal number property that we set the directory’s permission and sticky bit for POSIX systems. This option isn’t supported on Windows.

The default value for mode is 0o777 , which is readable, writable and executable for everyone.

The mode integer can also replace the options object as the second argument. The third argument is a callback function which is called when the directory creation operation is complete.

The function takes an err parameter which is null when the operation succeeds and has an object with the error information otherwise.

Calling mkdir when the path is a directory that exists results in an error only when the recursive option is set to false .

We can use the mkdir function like in the following code:

const fs = require("fs");  
const dirToCreate = "./files/createdFolder/createdFolder";

fs.mkdir(  
  dirToCreate,  
  {  
    recursive: true,  
    mode: 0o77  
  },  
  err => {  
    if (err) {  
      throw err;  
    }  
    console.log("Directory created!");  
  }  
);

If we run the code above, we can see that it created all the parent directories for the lowest level directories since we set recursive to true .

The synchronous version of the mkdir function is the mkdirSync function. It takes 2 arguments. The first argument is the path object, which can be a string, a Buffer object or an URL object.

The second argument is an object with various properties that we can set as options. The recursive property is a boolean property that lets us create all the levels of the directory if not all the levels exist. The default value of the recursive property is false .

The mode property is an octal number property that we set the directory’s permission and sticky bit for POSIX systems.

This option isn’t supported on Windows. The default value for mode is 0o777 , which is readable, writable and executable for everyone. The mode integer can also replace the options object as the second argument. The function returns undefined .

Calling mkdir when the path is a directory that exists results in an error only when the recursive option is set to false .

We can use the mkdirSync function like in the following code:

const fs = require("fs");  
const dirToCreate = "./files/createdFolder/createdFolder";

fs.mkdirSync(dirToCreate, {  
  recursive: true,  
  mode: 0o77  
});  
console.log("Directory created!");

If we run the code above, we can see that it created all the parent directories for the lowest level directories since we set recursive to true .

There’s also a promise version of the mkdir function. . It takes 2 arguments. The first argument is the path object, which can be a string, a Buffer object or an URL object.

The second argument is an object with various properties that we can set as options. The recursive property is a boolean property that let us create all the levels of the directory if not all the levels exist. The default value of the recursive property is false .

The mode property is an octal number property that we set the directory’s permission and sticky bit for POSIX systems. This option isn’t supported on Windows.

The default value for mode is 0o777 , which is readable, writable and executable for everyone. The mode integer can also replace the options object as the second argument.

The function returns undefined . Calling mkdir when the path is a directory that exists results in a promise reject only when the recursive option is set to false .

The function returns a promise that resolves with no argument when the directory creation operation succeeds.

We can use the promise version of the mkdir function like in the following code:

const fsPromises = require("fs").promises;  
const dirToCreate = "./files/createdFolder/createdFolder";

(async () => {  
  await fsPromises.mkdir(dirToCreate, {  
    recursive: true,  
    mode: 0o77  
  });  
  console.log("Directory created!");  
})();

If we run the code above, we can see that it created all the parent directories for the lowest level directories since we set recursive to true . This is a better option than using mkdirSync for creating directories sequentially since it doesn’t tie up the whole program like the synchronous version does when the directory creation operation is being run.

Creating Temporary Directories with fs.mkdtemp Family of Functions

The Node.js fs module has special functions for creating temporary directories. The mkdtemp family of functions allows us to do this with one function call.

The mkdtemp function takes 3 arguments. The first argument is the prefix for the temporary folder which is a string. It’s the path of the folder that you want to create, which will have some characters appended to it. The second argument is an options string or object.

The options object consists of the encoding property which is the character encoding for the folder name, which defaults to utf8 .

The encoding value string can replace the object as the second argument. The second argument is optional.

The third argument is a callback function that is called when the temporary directory creation function ends.

The callback function has 2 parameters. The first one is the err object which is null when the operation succeeds. The second parameter is the folder path which is a string.

The folder name for the generated folder will have the prefix with 6 random characters appended behind the prefix. In some systems like BSD systems, it can return more than 6 random characters after the prefix.

If we want to create a temporary directory within /tmp , then the prefix must end with a platform-specific path separator, which we can get from require(‘path’).sep .

We can create a temporary directory with the following code:

const fs = require("fs");  
const prefix = "./files/tempDir";

fs.mkdtemp(  
  prefix,  
  {  
    encoding: "utf8"  
  },  
  (err, folder) => {  
    if (err) {  
      throw err;  
    }  
    console.log("Temp directory created!", folder);  
  }  
);

If we run the code, we get the folder path logged in the console.log and we should see a new temporary folder created in the given path.

The mkdtemp function has a synchronous counterpart called the mkdtempSync function. The mkdtemp function takes 2 arguments. The first argument is the prefix for the temporary folder which is a string.

It’s the path of the folder that you want to create, which will have some characters appended to it.

The second argument is an optional string or object. The options object consists of the encoding property which is the character encoding for the folder name, which defaults to utf8 .

The encoding value string can replace the object as the second argument. The second argument is optional. It returns the created folder path.

If we want to create a temporary directory within /tmp , then the prefix must end with a platform-specific path separator, which we can get from require(‘path’).sep .

We can use the mkdtempSync function like in the following code:

const fs = require("fs");  
const prefix = "./files/tempDir";  
try {  
  const folder = fs.mkdtempSync(prefix, {  
    encoding: "utf8"  
  });  
  console.log("Temp directory created!", folder);  
} catch (error) {  
  console.error(error);  
}

If we run the code, we get the folder path logged in the console.log and we should see a new temporary folder created in the given path.

There’s also a promise version of the mkdtemp function. The mkdtemp function takes 2 arguments. The first argument is the prefix for the temporary folder which is a string.

It’s the path of the folder that you want to create, which will have some characters appended to it. The second argument is an optional string or object.

The optional object consists of the encoding property which is the character encoding for the folder name, which defaults to utf8 .

The encoding value string can replace the object as the second argument. The second argument is optional.

It returns a Promise that resolves with the path of the created directory when the temporary directory creation operation succeeds.

If we want to create a temporary directory within /tmp , then the prefix must end with a platform-specific path separator, which we can get from require(‘path’).sep .

We can use the promise version of the mkdtemp function like in the following code:

const fsPromises = require("fs").promises;  
const prefix = "./files/tempDir";

(async () => {  
  try {  
    const folder = await fsPromises.mkdtemp(prefix, {  
      encoding: "utf8"  
    });  
    console.log("Temp directory created!", folder);  
  } catch (error) {  
    console.error(error);  
  }  
})();

If we run the code, we get the folder path logged in the console.log and we should see a new temporary folder created in the given path. This is a better option than using mkdtempSync for creating directories sequentially since it doesn’t tie up the whole program like the synchronous version does when the directory creation operation is being run.

We created permanent directories with the mkdir family of functions to create normal directories. The mkdir family of functions can create all the parent directories along with the actual directory that we can create.

Also, we created temporary directories with the mkdtemp family of functions.

There’re the regular callback-based asynchronous function, the new promised basic asynchronous function, and the synchronous version of each function.

This promise versions of the functions are better creating directories sequentially since it doesn’t tie up the whole program like the synchronous version does when the directory creation operation is being run.

Categories
Express JavaScript Nodejs

List of Express body-parser Errors

By default, Express 4.x or later doesn’t come with anything to parse request bodies. Therefore, we need to add a library to handle this.

body-parser is a middleware that we can use to parse various kinds of request bodies. It throws several kinds of errors that we need to manage. In this article, we’ll look at what errors it throws.

Errors

When body-parser throws an error, it sends a response code and an error message. The message property of the error has the error message.

The following errors are common errors that are thrown.

Content-Encoding Unsupported

This error is thrown when the request has a Content-Encoding header that contains an encoding but the inflation option was set to false.

The status of the response will be 415m and type property is set to encoding.unsupported. The charset property will be set to the encoding that’s unsupported.

For example, we can reproduce it if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: false,  
};
app.use(bodyParser.urlencoded(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.listen(3000);

Then when we send a POST request to / with Content-Encoding set to abc and Content-Type set to application/x-www-form-urlencoded, we get:

content encoding unsupported

and a 415 status code.

Request Aborted

This error occurs when the request is aborted by the client before the reading of the body is finished.

The received property will be set to the number of bytes received before the request was aborted.

The status of the response will be 400 and type property set to request.aborted.

Request Entity Too Large

The error will occur when the request body is larger than what’s specified in the limit option.

A 413 error response will be sent with the type property set to entity.too.large.

For example, if we set limit to 0 as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: false,  
  limit: 0  
};
app.use(bodyParser.json(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.use((err, req, res, next) => {  
  res.status(400).send(err);  
});
app.listen(3000);

Then when we make a POST request to / with a JSON body that has content, then we get:

{"message":"request entity too large","expected":20,"length":20,"limit":0,"type":"entity.too.large"}

Request Size Didn’t Match Content-Length

The request’s length didn’t match the length from the Content-Length header.

This occurs when the request is malformed. Content-Length usually is calculated based on characters instead of bytes.

The status returned will be 400 and the type property is set to request.size.invalid.

For example, if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: false,    
};
app.use(bodyParser.urlencoded(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.listen(3000);

and we send a POST request to / with the Content-Length header set to -1 and Content-Type set to application/x-www-form-urlencoded, we get a 400 response.

Stream Encoding Shouldn’t Be Set

req.setEncoding method is called prior to body-parser methods will cause this error.

We can’t call req.setEncoding directly when using body-parser.

The status will be 500 and the type property set to stream.encoding.set.

Too Many Parameters

The error occurs when the number of parameters in the request body exceeds what’s set in the parameterLimit.

The response status will be 413 and the type property set to parameters.too.many

Unsupported Charset “BOGUS”

This will occur when the request has a Content-Encoding header that contains an unsupported encoding. The encoding is contained in the message as well as in the encoding property.

The status will be set to 415. The type will be set to encoding.unsupported, and the encoding property is set to the encoding that’s unsupported.

Conclusion

There’re multiple kinds of errors raised by body-parser.

They involve sending bad headers or data that are not accepted by it, or canceling requests before all the data is read.

Various 400 series error status codes will be sent as the response along with the corresponding error messages and stack trace.

Categories
Express JavaScript

Guide to the Express Application Object — Middleware

The core part of an Express app is the Application object. It’s the application itself.

In this article, we’ll look at the methods of the app object and what we can do with it, including using app.use to set middleware.

app.use([path,] callback [, callback…])

We can use the app.use method to mount middleware that’s run app-wide.

They can be run when requests for specific paths are made or for all paths.

It takes the following arguments:

  • path — it can be a string or regex representing paths or patterns of paths. The default is / .
  • callback — a function to handle requests. It can be a middleware function, a series of them, array of them, or a combination of all of the above

We can provide multiple callbacks that behave just like middleware, but they call next('route') to skip the remaining middleware functions.

We can use these to impose preconditions on some routes and pass control to the route if we don’t need to call the remaining middleware.

Since router and app implement the middleware interface, we can use them like any other middleware function.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
app.use((req, res, next) => {  
  console.log(`Request made at ${Date.now()}`)  
  next();  
})

app.get('/', (req, res) => {  
  res.send('hi');  
})

app.listen(3000);

Then we get the request time logged with every request.

We call next to call the route handler.

Middlewares are run sequentially, so the order they’re included in the code is important.

For example, if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
app.use((req, res, next) => {  
  res.send('End with middleware');  
})

app.get('/', (req, res) => {  
  res.send('hi');  
})

app.listen(3000);

Then we get End with middleware instead of hi since we sent our response with the middleware, which we included first.

Error-Handling Middleware

We can create our own middleware for handling errors instead of using Express’s default error handler.

The error handler takes 4 arguments, which are the error object, request object, response object, and next function.

For instance, we can define one as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res, next) => {  
  try {  
    throw new Error('foo');  
  }  
  catch (ex) {  
    next(ex);  
  }  
})

app.use((err, req, res, next) => {  
  res.status(500).send('error');  
})

app.listen(3000);

The error should be displayed since we included the error handler after our GET request handler.

Paths

We can path in a string or regex path or path pattern as the first argument of the use method. This way, the middleware will only be called when the route matches the one specified in string or regex.

To match a constant path, we can write the following:

app.use('/abc', (err, req, res, next) => {  
  res.status(500).send('error');  
})

We can match patterns as follows. A ? will make the character preceding it optional:

app.use('/abc?d', (err, req, res, next) => {  
  res.status(500).send('error');  
})

The when requests made to /abcd or /abd has an error, the middleware above will be called.

The + sign means that one or more characters preceding it will be matched. For example, if we have:

app.use('/abc+d', (err, req, res, next) => {  
  res.status(500).send('error');  
})

The when requests made to /abccd,/abcccd, etc. has an error, the middleware above will be called.

* is a wildcard character. We can use it as follows:

app.use('/ab*cd', (err, req, res, next) => {  
  res.status(500).send('error');  
})

Then we get error when we make a request to /abcccd , /abFoocd , and so on if that has an error.

To match a group of optional characters, we can use parentheses and a question mark after it.

For instance, if we have:

app.use('/a(bcd)?e', (err, req, res, next) => {  
  res.status(500).send('error');  
})

Then we get error when we make a request to /abcde or/ae that has an error.

Passing in Middleware

We can pass in various combinations of middleware in addition to just one middleware.

We can pass in a router object as a middleware as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));const router = express.Router();  
router.get('/', (req, res, next) => {  
  res.send();  
});

app.use(router);app.listen(3000);

An Express app is also valid middleware:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
const subApp = express();
subApp.get('/', (req, res, next) => {  
  res.send();  
});

app.use(subApp);app.listen(3000);

We can also pass in a series of middleware as arguments:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});  
app.use(r1, r2);
app.listen(3000);

Then they’ll be called in order, so we get:

r1 called  
r2 called

in the console.

Likewise, we can pass in an array of middlewares as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});  
app.use([r1, r2]);
app.listen(3000);

Then we get the same output since they’re called in the same order that they’re listed.

A combination of them also works:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
const foo = (req, res, next) => {  
  console.log('foo called');  
  next();  
}

const r1 = express.Router();  
r1.get('/', (req, res, next) => {  
  console.log('r1 called');  
  next();  
});

const r2 = express.Router();  
r2.get('/', (req, res, next) => {  
  console.log('r2 called');  
  next();  
});

const subApp = express();  
subApp.get('/', (req, res, next) => {  
  console.log('subApp called');  
  next();  
})

app.use(foo, [r1, r2], subApp);app.listen(3000);

Then we get:

foo called  
r1 called  
r2 called  
subApp called

from the console. They’re still called in the order they’re listed.

Static Files

We can expose static folders with the express.static middleware. For example, if we want to expose the public folder in the project folder, we can write:

app.use(express.static(path.join(__dirname, 'public')));

Conclusion

app.use can be used to one more middleware in various combinations, including a single middleware, an array of middlewares, a comma-separated list of middlewares or a combination of them.

They’re called in the order that they’re included, and the next one can be called by calling next .

Error handlers are also middleware functions. The only difference is that they have an error parameter before the request and response parameters.

Finally, we can expose static folders with the express.static middleware.

Categories
Express JavaScript Nodejs

Guide to the Express Router Object — Request and Parameter Handling

The Expressrouter object is a collection of middlewares and routes. It a mini-app within the main app.

It can only perform middleware and routing functions and can’t stand on its own.

It also behaves like middleware itself, so we can use it with app.use or as an argument to another route’s use method.

In this article, we’ll look at the router object’s methods, including all , param , and methods for listening to specific kinds of requests.

Methods

router.all(path, [callback, …] callback)

The router.all method takes a callback for handling all kinds of requests.

We can pass in a constant path, or a string with the path pattern or a regex.

For example, we can pass in middleware that’s run for all routes attached to the router as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const fooRouter = express.Router();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
const mw1 = (req, res, next) => {  
  console.log('middleware 1 called');  
  next();  
}
const mw2 = (req, res, next) => {  
  console.log('middleware 2 called');  
  next();  
}
fooRouter.all('*', mw1, mw2);
fooRouter.get('/', (req, res) => {  
  res.send('foo');  
})
app.use('/foo', fooRouter);app.listen(3000, () => console.log('server started'));

Then we get:

middleware 1 called  
middleware 2 called

if we make a request to /foo since anything that starts with /foo routes through the fooRouter , and we have the fooRouter.all method call with the middlewares passed in.

Equivalently, we can write:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const fooRouter = express.Router();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
const mw1 = (req, res, next) => {  
  console.log('middleware 1 called');  
  next();  
}
const mw2 = (req, res, next) => {  
  console.log('middleware 2 called');  
  next();  
}
fooRouter.all('*', mw1);  
fooRouter.all('*', mw2);
fooRouter.get('/', (req, res) => {  
  res.send('foo');  
})
app.use('/foo', fooRouter);
app.listen(3000, () => console.log('server started'));

They’re the same as long as the order of fooRouter.all is called in the same as the order the callbacks are passed in.

router.METHOD(path, [callback, …] callback)

router.METHOD is for handling requests with the given method. For example, router.get for handling GET requests, router.post for handling POST requests, etc.

router.get also automatically calls for the HTTP HEAD in addition to the GET method if router.head wasn’t called.

We can provide multiple callbacks and they’re all treated equally. These callbacks may invoke the next('route') call to bypass the remaining route callbacks.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const fooRouter = express.Router();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
fooRouter.get('/', (req, res) => {  
  res.send('foo');  
})
app.use('/foo', fooRouter);
app.listen(3000, () => console.log('server started'));

Then when we make a request to /foo , we get back foo .

We can also pass in a regex for the route path . For example, we can write:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const fooRouter = express.Router();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
fooRouter.get('/ab+c/', (req, res) => {  
  res.send('foo');  
})
app.use('/foo', fooRouter);
app.listen(3000, () => console.log('server started'));

to listen to requests for paths /foo/abc , /foo/abbc , /foo/abbbc , etc., since we specified in the regex that we look for any number of the character b in the path.

router.param(name, callback)

router.param lets us trigger a callback function call when a specific parameter is passed in when the request is made from the client.

name is the parameter placeholder name that we look for.

The parameters of the callback function are:

  • req, the request object.
  • res, the response object.
  • next, indicating the next middleware function.
  • The value of the name parameter.
  • The name of the parameter.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const fooRouter = express.Router();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
fooRouter.param('name', (req, res, next, name) => {  
  req.name = name;  
  next();  
})
fooRouter.get('/:name', (req, res) => {  
  res.send(req.name);  
})
app.use('/foo', fooRouter);
app.listen(3000, () => console.log('server started'));

Then we make a request to /foo/abc then we get abc since the fooRouter.param found the name parameter was passed in with the URL.

The name parameter has the value 'abc' because it grabbed the part after /foo/ and then we assigned name to req.name and called next .

After that, the route handler we passed into foorRouter.get is called, then we passed req.name into res.send and sent it as the response.

Conclusion

The Express router lets us create sub-apps of an Express app so we don’t have to add all the route handlers and middlewares to the main app.

With the all method, we can listen to all kinds requests. We can also listen to specific kinds of requests like GET or POST requests with the respective methods. They all take a string or regex path and a route handler callback.

Finally, we have the param method to get route parameters and do what we want with it.

Categories
JavaScript Nodejs

Node.js FS Module — Changing Ownership and Copying Files

Manipulating files and directories are basic operations for any program. Since Node.js is a server-side platform and can interact with the computer that it’s running on directly, being able to manipulate files is a basic feature.

Fortunately, Node.js has an fs module built into its library. It has many functions that can help with manipulating files and folders. File and directory operations that are supported include basic ones like manipulating and opening files in directories.

Likewise, it can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API that has functions that support promises.

Also, it can show statistics for a file. Almost all file operations that we can think of can be done with the built-in fs module.

In this article, we will change ownership of a file with the chown function and copy files with the copyFile function.


Changing File Ownership With the fs.chown and fs.chownSync Functions

The chown function lets us change the ownership of a file asynchronously. It takes four arguments.

  1. The first argument is the path object which can be in the form of a string, a Buffer object, or a URL object.
  2. The second argument is the UID, which is the user ID of the user.
  3. The third argument is the GID, which is the group ID.
  4. The fourth argument is a callback function that has an err parameter which is null if the chown operation is successful, otherwise err will be an error object. The callback function is called when the chown operation is finished, whether it’s successful or not.

To use the chown function, we can write something like the following code:

In the example above, we change the ownership of the file file.txt to the user with UID 1000 and group with GID 1000. To find out the UID and GID of the user and group of the current user in Linux systems, we can use the id command.

The chown function has a synchronous version called chownSync. It takes three arguments.

  1. The first argument is the path object which can be in the form of a string, a Buffer object, or a URL object.
  2. The second argument is the UID, which is the user ID of the user.
  3. The third argument is the GID, which is the group ID. It returns undefined.

We can use it as in the following code:

const fs = require("fs");  
const file = "./files/file.txt";
fs.chownSync(file, 1000, 1000);  
console.log("File ownership changed");

It does the same thing as chown, but does it synchronously. If we just want to do the chown operation sequentially, we don’t have to use chown, a better alternative is to use the promise version of the chown function.

The promise version of the chown function takes three arguments.

  1. The first argument is the path object which can be in the form of a string, a Buffer object, or a URL object.
  2. The second argument is the UID, which is the user ID of the user.
  3. The third argument is the GID, which is the group ID. It returns a promise that resolves with no arguments when the chown operation is successfully done.

We can use it as in the following code:

To confirm that the chown worked as we expected, we can run the following commands in Linux systems. One way is to run ls -l , as in the command below:

ls -l ./files/file.txt

We can also use the stat command.

For example, we can run stat ./files/file.txt to get the ownership information of ./files/file.txt from the Uid and Gid output. With the stat command, we should get output that looks something like this:

File: './files/file.txt'  
  Size: 16              Blocks: 0          IO Block: 512    regular file  
Device: eh/14d  Inode: 22799473115106242  Links: 1  
Access: (0555/-r-xr-xr-x)  Uid: ( 1000/hauyeung)   Gid: ( 1000/hauyeung)  
Access: 2019-11-02 12:26:47.996290200 -0700  
Modify: 2019-11-02 12:26:47.996290200 -0700  
Change: 2019-11-02 12:44:45.037581600 -0700  
 Birth: -

Also, we can filter out the information we don’t need and just get the UID and GID with the "%U %G" format sequence. For example, we can run stat -c “%U %G” ./files/file.txt to get the UID and GID only for file.txt.


Copy Files With fs.copyFile and fs.copyFileSync

To copy files in a Node.js program, we can use the copyFile function. The copyfile function takes four arguments.

  1. The first argument is the path of the source file, which can be a string, a Buffer object, or a URL object.
  2. The second argument is the path of the destination file, which can also be a string, a Buffer object, or a URL object.
  3. The third argument is a numeric flag argument that specifies the behavior of the copy operation. The default value of the flag is 0.

The flag can be one of the following values or a bitwise combination of them:

  • fs.constants.COPYFILE_EXCL — The copy operation will fail if the destination file already exists.
  • fs.constants.COPYFILE_FICLONE — The copy operation will attempt to create a copy-on-write reflink. Copy-on-write means that if a file is copied but not modified then it will just reference the original file. The only time that it will do the actual copy is when we first write to a file. If the platform does not support copy-on-write, then a fallback copy mechanism is used.
  • fs.constants.COPYFILE_FICLONE_FORCE — The copy operation will attempt to create a copy-on-write reflink. If the platform does not support copy-on-write, then the operation will fail.

We can combine the constants above with the bitwise OR operator, for example like fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE.

4. The fourth argument is a callback function that is called when the file copy operation is complete. It has an err parameter which is null when the copy file operation succeeded and an object with the error information otherwise.

There’s no guarantee of the atomicity of the copy operation. If an error occurs after the destination file is opened for writing, then Node.js will attempt to remove the destination.

By default, if no flags are set, the destination file will overwrite the existing file with the same name in the same location if it exists.

To use the copyFile function, we can write something like the following code:

In the code above, we set the source of the source file to be file.txt, the destination file to be fileCopy.txt.

Also, we specified that the copy operation will fail if the file exists with the fs.constants.COPYFILE_EXCL constant and we specify that the copy file operation will be done via the copy-on-write method with the fs.constants.COPYFILE_FICLONE flag.

The synchronous version of the copyFile function is the copyFileSync function. It takes the same arguments as the copyFile function except the callback.

  1. The first argument is the path of the source file, which can be a string, a Buffer object, or a URL object.
  2. The second argument is the path of the destination file, which can also be a string, a Buffer object, or a URL object.
  3. The third argument is a numeric flag argument that specifies the behavior of the copy operation. The default value of the flag is 0. The flags are the same ones as the copyFile function, and we can also combine them the same way.

There’s no guarantee of the atomicity of the copy operation. If an error occurs after the destination file is opened for writing, then Node.js will attempt to remove the destination.

By default, if no flags are set, the destination file will overwrite the existing file with the same name in the same location if it exists.

To use the copyFileSync function, we can write some code like the following example:

In the code above, we set the source of the source file to be file.txt, the destination file to be fileCopy.txt.

Also, we specified that the copy operation will fail if the file exists with the fs.constants.COPYFILE_EXCL constant and we specify that the copy file operation will be done via the copy-on-write method with the fs.constants.COPYFILE_FICLONE flag.

We catch any exceptions that are thrown by surrounding the code with the try...catch block.

There’s also a promise version of the copyFile function. It asynchronously copies the file from the source to the destination.

  1. The first argument is the path of the source file, which can be a string, a Buffer object, or a URL object.
  2. The second argument is the path of the destination file, which can also be a string, a Buffer object, or a URL object.
  3. The third argument is a numeric flag argument that specifies the behavior of the copy operation. The default value of the flag is 0. The flags are the same ones as the copyFile function, and we can combine them the same way.

There’s no guarantee of the atomicity of the copy operation. If an error occurs after the destination file is opened for writing, then Node.js will attempt to remove the destination.

By default, if no flags are set, the destination file will overwrite the existing file with the same name in the same location if it exists.

The promise version of copyFile resolves with no argument if the copy operation is successful.

We can use the promise of copyFile as in the following code:

In the code above, we set the source of the source file to be file.txt, the destination file to be fileCopy.txt.

Also, we specified that the copy operation will fail if the file exists with the fs.constants.COPYFILE_EXCL constant and we specify that the copy file operation will be done via the copy-on-write method with the fs.constants.COPYFILE_FICLONE flag.

We catch any exceptions that are thrown by using surround the code with the try...catch block.


Conclusion

With the chown function, we can change the ownership of a file. There are two asynchronous versions of the function, one is the regular asynchronous version and one is the promise version.

There’s also one synchronous version of the chown function, which is the chownSync function. They both change the owner by passing in the UID and GID of the user of your choice. UID is the user ID and GID is the group ID. We can copy files with the copyFile function.

There are two asynchronous versions of the copyFile function, one is the regular asynchronous version and one is the promise version.

There’s also a synchronous version of the copyFile function called the copyFileSync function. We can change how files are copied with flags that are available as constants in the fs module.