{"id":4598,"date":"2022-11-26T10:01:10","date_gmt":"2022-11-26T16:01:10","guid":{"rendered":"https:\/\/dev.iachieved.it\/iachievedit\/?p=4598"},"modified":"2024-01-18T14:16:56","modified_gmt":"2024-01-18T20:16:56","slug":"developing-on-a-mac-part-iii-nodejs","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/developing-on-a-mac-part-iii-nodejs\/","title":{"rendered":"Developing on a Mac &#8211; Part III &#8211; NodeJS"},"content":{"rendered":"<p>I wrote <a href=\"https:\/\/dev.iachieved.it\/iachievedit\/developing-on-a-mac-part-i\/\">Part I<\/a> of this series to provide a foundation upon which to build a collection of minimal guides to developing software on a Mac. In this post let&#8217;s look at what needs to be installed on your Mac for developing <a href=\"https:\/\/rubyonrails.org\/\">NodeJS applications<\/a>.  If you haven&#8217;t read <a href=\"https:\/\/dev.iachieved.it\/iachievedit\/developing-on-a-mac-part-i\/\">Part I<\/a>, make sure you do and install:<\/p>\n<ul>\n<li>Homebrew<\/li>\n<li>Developer Tools<\/li>\n<\/ul>\n<h2>NodeJS<\/h2>\n<p>NodeJS can be installed and managing using a number of package managers, just like we saw with Ruby. Or, you can install it directly from <a href=\"https:\/\/nodejs.org\/en\/download\/\">nodejs.org<\/a>. You can even install it with Homebrew!<\/p>\n<h3>Homebrew Install<\/h3>\n<p>Installing NodeJS with Homebrew is as simple as running <code>brew install node<\/code> in your terminal.<\/p>\n<p>Both <code>node<\/code> (the NodeJS engine) and <code>npm<\/code> (Node Package Manager) are installed:<\/p>\n<pre class=\"lang:default decode:true \" >% node --version\r\nv19.1.0\r\n\r\n% npm --version\r\n8.19.3\r\n<\/pre>\n<p>Now to install a few node packages in the global environment:<\/p>\n<pre class=\"lang:default decode:true \" >\r\nnpm install -g dotenv openssl\r\n<\/pre>\n<p>Where&#8217;d they get installed?<\/p>\n<pre class=\"lang:default decode:true \" >\r\n$ npm list -g --depth 0 -p\r\n\/opt\/homebrew\/lib\r\n\/opt\/homebrew\/lib\/node_modules\/dotenv\r\n\/opt\/homebrew\/lib\/node_modules\/npm\r\n\/opt\/homebrew\/lib\/node_modules\/openssl\r\n<\/pre>\n<h4>Changing versions<\/h4>\n<p>It&#8217;s not uncommon to be developing with multiple NodeJS versions, especially in a professional environment.  Installing and managing Node versions with Homebrew isn&#8217;t too difficult, <i>if<\/i> you make the decision to only have one version installed at a time.  No joke.<\/p>\n<pre class=\"lang:default decode:true \" >% brew search node\r\n==&gt; Formulae\r\nlibbitcoin-node     node-build          node@14             nodebrew\r\nlinode-cli          node-sass           node@16             nodeenv\r\nllnode              node@10             node@18             nodenv\r\nnode                node@12             node_exporter       ode\r\n<\/pre>\n<p>Recall that we installed v19.1.10.  As it turns out that isn&#8217;t a Long Term Support (LTS) release.  Let&#8217;s install v14.<\/p>\n<pre class=\"lang:default decode:true \" >\r\nbrew uninstall node\r\nbrew install node@14\r\n<\/pre>\n<p>You are undoubtedly protesting:  &#8220;What about all of the cool command line tools I installed through npm?!&#8221;  You should probably be keeping a list of those and reinstall them.<\/p>\n<h3>Using NodeJS.org<\/h3>\n<p>NodeJS.org hosts <a href=\"https:\/\/nodejs.org\/en\/download\/\">binary package installers<\/a> for Node.  You can install these packages by downloading and then double-clicking on them or via the command-line.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2022\/11\/Screenshot-2022-11-26-at-7.39.53-AM.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2022\/11\/Screenshot-2022-11-26-at-7.39.53-AM.png\" alt=\"\" width=\"620\" height=\"445\" class=\"aligncenter size-full wp-image-4617\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2022\/11\/Screenshot-2022-11-26-at-7.39.53-AM.png 620w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2022\/11\/Screenshot-2022-11-26-at-7.39.53-AM-300x215.png 300w\" sizes=\"(max-width: 620px) 100vw, 620px\" \/><\/a><\/p>\n<p>Installing via the command line uses the <code>installer<\/code> application:<\/p>\n<pre class=\"lang:default decode:true \" >\r\nsudo installer -pkg node-v18.12.1.pkg -target \/\r\n<\/pre>\n<p>If you&#8217;ve never used <code>pkgutil<\/code> before on your Mac, try it out!<\/p>\n<pre class=\"lang:default decode:true \" >\r\n% pkgutil --packages\r\ncom.apple.pkg.CLTools_SDK_macOS13\r\ncom.apple.pkg.CLTools_SDK_macOS12\r\ncom.apple.pkg.CLTools_Executables\r\ncom.apple.files.data-template\r\ncom.apple.pkg.MRTConfigData_10_15.16U4211\r\ncom.apple.pkg.CLTools_SwiftBackDeploy\r\ncom.apple.pkg.XProtectPayloads_10_15.16U4232\r\ncom.apple.pkg.CLTools_macOS_SDK\r\norg.nodejs.npm.pkg\r\norg.nodejs.node.pkg\r\n<\/pre>\n<p>As promised, the NodeJS.org installs <code>node<\/code> in <code>\/usr\/local\/bin<\/code>:<\/p>\n<pre class=\"lang:default decode:true \" >% which node\r\n\/usr\/local\/bin\/node\r\n<\/pre>\n<p>Let&#8217;s see where our modules are installed:  <code>npm install -g dotenv<\/code><\/p>\n<pre class=\"lang:default decode:true \" >\r\nnpm ERR! Error: EACCES: permission denied, mkdir &#039;\/usr\/local\/lib\/node_modules\/dotenv&#039;\r\n<\/pre>\n<p>Oh! <code>sudo<\/code> access is required.  And that&#8217;s why I&#8217;d stop right here.  NodeJS installed in this manner is not going to support multiple versions and will make your development experience miserable.<\/p>\n<h3>Using nvm<\/h3>\n<p>A third method of installing NodeJS is through <a href=\"https:\/\/github.com\/nvm-sh\/nvm\">NVM<\/a>, the Node Version Manager.  Of course you can install <code>nvm<\/code> using their instructions, or use Homebrew.<\/p>\n<pre class=\"lang:default decode:true \" >\r\n% brew install nvm\r\nPlease note that upstream has asked us to make explicit managing\r\nnvm via Homebrew is unsupported by them and you should check any\r\nproblems against the standard nvm install method prior to reporting.\r\n\r\nYou should create NVM&#039;s working directory if it doesn&#039;t exist:\r\n\r\nmkdir ~\/.nvm\r\n\r\nAdd the following to ~\/.zshrc or your desired shell\r\nconfiguration file:\r\n\r\nexport NVM_DIR=&quot;$HOME\/.nvm&quot;\r\n[ -s &quot;\/opt\/homebrew\/opt\/nvm\/nvm.sh&quot; ] &amp;&amp; \\. &quot;\/opt\/homebrew\/opt\/nvm\/nvm.sh&quot; # This loads nvm\r\n[ -s &quot;\/opt\/homebrew\/opt\/nvm\/etc\/bash_completion.d\/nvm&quot; ] &amp;&amp; \\. &quot;\/opt\/homebrew\/opt\/nvm\/etc\/bash_completion.d\/nvm&quot; # This loads nvm bash_completion\r\n\r\nYou can set $NVM_DIR to any location, but leaving it unchanged from\r\n\/opt\/homebrew\/opt\/nvm will destroy any nvm-installed Node installations\r\nupon upgrade\/reinstall.\r\n\r\nnvm list\r\nN\/A\r\niojs -&gt; N\/A (default)\r\nnode -&gt; stable (-&gt; N\/A) (default)\r\nunstable -&gt; N\/A (default)\r\n<\/pre>\n<p>Once you&#8217;ve installed a NVM you can use <code>nvm install<\/code> to install a specific version.<\/p>\n<pre class=\"lang:default decode:true \" >\r\nnvm install 18\r\nDownloading and installing node v18.12.1...\r\nDownloading https:\/\/nodejs.org\/dist\/v18.12.1\/node-v18.12.1-darwin-arm64.tar.xz...\r\n######################################################################### 100.0%\r\nComputing checksum with shasum -a 256\r\nChecksums matched!\r\nNow using node v18.12.1 (npm v8.19.2)\r\nCreating default alias: default -&gt; 18 (-&gt; v18.12.1)\r\n\r\n% which node\r\n\/Users\/joe\/.nvm\/versions\/node\/v18.12.1\/bin\/node\r\n\r\n% which npm\r\n\r\n<\/pre>\n<p>And installing the <code>dotenv<\/code> package.<\/p>\n<pre class=\"lang:default decode:true \" >% npm install -g dotenv\r\n\r\n% npm list -g --depth 0 -p\r\n\/Users\/joe\/.nvm\/versions\/node\/v18.12.1\/lib\r\n\/Users\/joe\/.nvm\/versions\/node\/v18.12.1\/lib\/node_modules\/corepack\r\n\/Users\/joe\/.nvm\/versions\/node\/v18.12.1\/lib\/node_modules\/dotenv\r\n\/Users\/joe\/.nvm\/versions\/node\/v18.12.1\/lib\/node_modules\/npm\r\n<\/pre>\n<h2>Creating a Basic Application<\/h2>\n<p>With NodeJS installed, let&#8217;s take it for a spin and write a &#8220;simple&#8221; application that reads data from a Postgres database.<\/p>\n<pre class=\"lang:default decode:true \" >\r\n% mkdir ~\/projects\/nodejs\/simple_node_app\r\n% cd ~\/projects\/nodejs\/simple_node_app\r\n% npm init -y\r\nWrote to \/Users\/joe\/projects\/nodejs\/simple_node_app\/package.json\r\n<\/pre>\n<p>We&#8217;ll be using the <code>pg<\/code>, <code>dotenv<\/code>, and <code>yargs<\/code> packages.<\/p>\n<pre class=\"lang:default decode:true \" >\r\nnpm install pg dotenv yargs --save-deps\r\n<\/pre>\n<p>Of course, to communicate with a Postgres database we&#8217;ll need one!  Here we&#8217;ll install Postgres, start it, and then create a new database, user, and grants for this example.<\/p>\n<pre class=\"lang:default decode:true \" >\r\n% brew install postgresql\r\n% brew services start postgresql\r\n% psql postgres\r\npsql (14.6 (Homebrew))\r\nType &quot;help&quot; for help.\r\n\r\npostgres=# create database simple_node_app_db;\r\npostgres=# create user simple_node_app_user with password &#039;simple_node_app_pw&#039;;\r\npostgres=# grant all privileges on database simple_node_app_db to simple_node_app_user;\r\npostgres=# \\q\r\n<\/pre>\n<p>Back to our Node code.  We&#8217;ll put the following in <code>index.js<\/code>:<\/p>\n<pre class=\"lang:default decode:true \" >require(\"dotenv\").config();\r\n\r\nvar argv = require('yargs\/yargs')(process.argv.slice(2)).argv\r\n\r\nconst { Client } = require(\"pg\");\r\n\r\nconst database = new Client();\r\n\r\nasync function createUser(username) {\r\n  await database.connect();\r\n\r\n  await database.query(\"CREATE TABLE users (username VARCHAR(32))\", (err, res) =&gt; {\r\n    \/\/ Ignore errors\r\n  });\r\n\r\n  console.log(`Inserting user ${username}`);\r\n  await database.query(\"INSERT INTO users (username) values ($1)\", [username]);\r\n\r\n  database.end().then(() =&gt; {\r\n    console.log(\"Exiting...\");\r\n    process.exit();\r\n  });\r\n\r\n}\r\n\r\nif (argv.username) {\r\n\r\n  let username = argv.username;\r\n\r\n  createUser(username);\r\n\r\n} else {\r\n  console.log(\"Usage:  node index.js --username USERNAME\");\r\n}<\/pre>\n<p>You can try running this as is, but the connection to the database will fail.  That can be fixed with a <code>.env<\/code> file which utilizes the <code>pg<\/code> default environment variables for the database, username, password, etc.<\/p>\n<pre class=\"lang:default decode:true \" >\r\nPGUSER=simple_node_app_user\r\nPGPASSWORD=simple_node_app_password\r\nPGHOST=localhost\r\nPGDATABASE=simple_node_app_db\r\n<\/pre>\n<pre class=\"lang:default decode:true \" >\r\n% node index.js --username joe\r\nInserting user joe\r\nExiting...\r\n<\/pre>\n<h2>Which Installation Method to Use?<\/h2>\n<p>&nbsp;<\/p>\n<p>I recommend using either Homebrew or NVM for installing and managing versions of NodeJS.  Uninstalling the version provided by NodeJS.org is a pain in the ass, but can be done.  I wouldn&#8217;t recommend the method<br \/>\n<a href=\"https:\/\/macpaw.com\/how-to\/uninstall-node-mac\">here<\/a>, but rather use this method.<\/p>\n<p><strong>WARNING<\/strong>:  <code>sudo<\/code> and <code>rm -f<\/code> ahead.  Always read and get a feeling for what commands do before running them.  You may consider commenting out the <code>rm<\/code> command to see what files are going to be removed.<\/p>\n<pre>\r\n% for f in `pkgutil --only-files --files org.nodejs.node.pkg`;\r\ndo\r\necho \"Removing \/$f\"\r\nsudo rm -f \/$f\r\ndone\r\n<\/pre>\n<p>followed by<\/p>\n<pre>\r\nfor f in `pkgutil --only-files --files org.nodejs.npm.pkg`;\r\ndo\r\necho \"Removing \/$f\"\r\nsudo rm -f \/$f\r\ndone\r\n<\/pre>\n<p>and finally,<\/p>\n<pre>\r\n% sudo pkgutil --forget org.nodejs.node.pkg\r\nForgot package 'org.nodejs.node.pkg' on '\/'.\r\n% sudo pkgutil --forget org.nodejs.npm.pkg\r\nForgot package 'org.nodejs.npm.pkg' on '\/'.\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I wrote Part I of this series to provide a foundation upon which to build a collection of minimal guides to developing software on a Mac. In this post let&#8217;s look at what needs to be installed on your Mac for developing NodeJS applications. If you haven&#8217;t read Part I, make sure you do and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3747,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,14],"tags":[],"class_list":["post-4598","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-apple","category-node-js"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4598"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=4598"}],"version-history":[{"count":28,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4598\/revisions"}],"predecessor-version":[{"id":5031,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4598\/revisions\/5031"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media\/3747"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=4598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=4598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=4598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}