paint-brush
Using Core Node JS Modules in React Native Appsby@suprith461
1,160 reads
1,160 reads

Using Core Node JS Modules in React Native Apps

by Suprith HattikalJune 25th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The React Native packager can’t package crypto module into React Native. This makes core modules like the crypto, stream, etc, and the thousands of npm modules that depend on them unusable from React Native. Fortunately, there’s a solution to this problem, but it takes some work. Using core Node JS modules like crypto in the browser using polyfills allows you to use core Node. JS modules in React Native is given special treatment and you can redefine it to mean something else.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Using Core Node JS Modules in React Native Apps
Suprith Hattikal HackerNoon profile picture

Sometimes while creating advanced apps in react-native you may want to encrypt the data you are sending to the web for secured web communication. You may also want to encrypt the payment-related data that is sent to the payment gateway.

Let’s say you want to use the crypto module to create some encrypted data. It might seem natural to do something like this:

There are many alternate modules out there namely

expo-crypto , react-native-crypto and many others but none of these have all the methods of crypto module ,especially if you want to use createCipheriv method you wont find it in any of those modules out there.
var crypto = require('crypto');

var mykey = crypto.createCipher('aes-128-cbc', 'mypassword');
var mystr = mykey.update('abc', 'utf8', 'hex')
mystr += mykey.final('hex');

console.log(mystr); //34feb914c099df25794bf9ccb85bea72

Just import a module and create an encrypted string, right? Right? Nope!

But this doesn’t work, because crypto is a core Node JS module, which means it’s probably C++ code bundled with the Node JS binary, not Javascript. The React Native packager can’t package it[1] along with your app’s Javascript bundle, so you get a runtime error:

Unable to resolve module 'crypto' 

This makes core modules like the crypto, stream, etc, and the thousands of npm modules that depend on them unusable from React Native. Fortunately, there’s a solution to this problem, but it takes some work.

Solution

If you’re familiar with the module bundler Browserify, you might know that it allows you to use core Node JS modules like crypto in the browser using polyfills[2]. So let’s try to create a standalone Javascript file that contains a polyfill for the cryptomodule, and use it within our app:

1. First, install browserify: Here we are installing browserify globally. If you have installed it before globally then the below command need not be executed.

npm install -g browserify

2. Create a file

crypto-in.js 
in your root directory ;

In the file

 crypto-in.js
import the module crypto and export it in the same file. Make sure that you do not create
crypto-in.js
in 
node_modules
 directory. If you create it in 
node_modules
 directory this file will be lost every time
 npm install 
is executed.

var crypto = require("crypto");
module.exports=crypto

3. Create a standalone Javascript bundle 

crypto-custom.js
 using browserify.

browserify crypto-in.js -o crypto-custom.js

This creates a file 

crypto-custom.js
, which contains some 26,660 lines of code (containing the polyfills). The last few lines contain our code from 
crypto-in.js
.

//26,656 lines above..
},{"indexof":100}],153:[function(require,module,exports){
  // Our code from crypto-in.js
  var crypto = require("crypto");
  module.exports = crypto;
},{"crypto":56}]},{},[153]);

At this point, it might seem like we’re done, but we’re not! If you replace the line:

const crypto = require('crypto');

in the app code with:

const crypto = require('./crypto-custom');

//here iam in root directory and crypto-custom.js file is also in 
//rootdirectory

and try to run the app, you get the following error:

Unable to resolve module bn.js

Turns out the word require is given special treatment by the React Native packager, and you can’t redefine it to mean something else, which is what Browserify tries to do within the bundle. If you look carefully inside

crypto-custom.js
, you will notice that it always passes in a custom require to function as a parameter and never actually uses the global require. There is a hacky but simple way to fix this problem, explained below.

4. Open the file 

crypto-custom.js
 in a text editor, and replace all instances of the word require with something else e.g.
 req
. Make sure it’s unique, so it doesn’t conflict with any existing code and mess things up.

If you save

crypto-custom.js
 and reload the app now, the previous error will go away, but a new error will appear:

The packager isn’t able to find the method 

createCipher
, because crypto isn’t exported properly from 
crypto-custom.js
. If you look at the last few lines of the bundle, you’ll find that this is because we’re setting
module.exports
 not on the global modules object, but on something else that’s passed in as a function argument:

//26,656 lines above..
},{}],187:[function(req,module,exports){
var crypto = req("crypto");
module.exports = crypto;
},{"crypto":71}]},{},[187]);

5. To export 

crypto
 properly from 
crypto-custom.js
, we need to make a few more changes to the file:

Add the statement

 var crypto
 at the top of the file 
crypto-custom.js
Replace the statement 
var crypto = require('crypto')
; with 
crypto = require('crypto');
 to set the outer variable, instead of creating a local variable inside the function body. Move the line 
module.exports = crypto;
 outside the function body to the bottom of the file, so that it references the global module object.

After the changes, crypto-custom.js should look something like this:

var crypto;  //at top of the file at line 1
// Some 26,656 lines of polyfills..
},{"indexof":100}],153:[function(req,module,exports){
  crypto = req("crypto");
},{"crypto":56}]},{},[153]);

module.exports = crypto; 

That’s it! If you run the app now, it should work perfectly:

References

  1. — https://github.com/facebook/react-native/issues/1871[2]
  2. — https://github.com/substack/browserify-handbook#builtins[3]
  3. — https://github.com/facebook/react-native/issues/6253