Creating your first npm package

Creating your first npm package


This weekend I started working on my first ever npm package. I can’t believe for how long I have been writing code that I never bothered to create my own npm package but here we are. I built my newest site using Gridsome and markdown and you can read all about that here. In the markdown files, I wanted an easy way to insert a twitter status and embed the tweet.

I will tell you more about that Gridsome plugin in a future blog post but for now, I want to show you how you can create your very first npm package. I learned a few things while working on this and I would like to share them with you.


Prerequisites

I am going to assume you at least know what node & npm is and have written JavaScript before. If you don’t know either of these and want me to write something up on getting started with those please let me know.

There are a few things that you’re going to need before we dive in and start writing some code.

Creating your npm package

The first thing you are going to do is create a new folder to hold your npm package. For this example, I am going to create a new directory called wrap-with-poo. Yes, you read that correctly.

Go into that folder and type the following:

<pre class="ql-syntax" spellcheck="false">npm init

</pre>

This will ask you a bunch of questions and then create a package.json. If you don’t know answers to certain questions just yet don’t worry, you can come back and answer them later.

<pre class="ql-syntax" spellcheck="false">This utility will walk you through creating a package.json file.
It only covers the most common items and tries to guess sensible defaults.

See npm help json for definitive documentation on these fields
and exactly what they do.

Use npm install &lt;pkg&gt; afterward to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (wrap-with-poop)
version: (1.0.0) 0.0.1
description: This package will take any string you give it and wrap it with the poop emjoi
entry point: (index.js)
test command:
git repository:
keywords: node,npm
author: Dan Vega
license: (ISC) MIT
About to write to /Users/vega/dev/npm/wrap-with-poop/package.json:

{
"name": "wrap-with-poop",
"version": "0.0.1",
"description": "This package will take any string you give it and wrap it with the poop emjoi",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [
"node",
"npm"
],
"author": "Dan Vega",
"license": "MIT"
}

Is this OK? (yes) yes

</pre>

Writing your plugin

Next open this project up in Visual Studio Code and create index.js. The reason you’re creating this file is that in your package.json you said that this was your entry point. In your index.js add the following code:

<pre class="ql-syntax" spellcheck="false">module.exports = (str) => {
return 💩${str}💩;
}

</pre>

The module.exports object allows us to organize some related code and then expose it as a module. This means that when we are done we could import this module into another application. In this case, we are assigning an arrow function which means we are exposing a single function that takes an argument called str and returns that string wrapped with the poo emoji. That is all you need to do with this project. It is a pretty simple package but it will help walk through a few things.


npm local development

Now that you have our package ready to go you need to test it in another project. In the real world, you should be writing some unit tests against it but I want to save that for another article & screencast.

Next, create a new directory (outside of your package) called wrap-with-poo-testing. You will again need to run npm init but this time you can add the -y argument to skip all of the questions, they are less important this time.

<pre class="ql-syntax" spellcheck="false">npm init -y

Wrote to /Users/vega/dev/npm/wrap-with-poo/package.json:

{
"name": "wrap-with-poop",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

</pre>

NPM Install

In this project create a new file called app.js. This is where you are going to use your new wrap-with-poo package. This is normally where you would normally install the npm package you needed by running the following command.

<pre class="ql-syntax" spellcheck="false">npm install wrap-with-poo

</pre>

The problem with this is that you haven’t published your new plugin yet so it isn’t in npm. You need a way to reference the package locally while you’re developing it. You could run npm install with the absolute path to the package.

<pre class="ql-syntax" spellcheck="false">npm install /Users/vega/dev/npm/wrap-with-poo

</pre>

Which would update your package.json to look like this

<pre class="ql-syntax" spellcheck="false">{
"name": "npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"wrap-with-poo": "file:../wrap-with-poo"
}
}

</pre>

If you need to test out pre-install/post-install hooks in your package then you will want to use this approach. If you don’t care about the best way to develop NPM projects locally is by using npm link.


NPM Link

npm link is a process that allows you to create a symbolic link between your project and the dependency. First, you need to move into the directory wrap-with-poo and run the following command.

<pre class="ql-syntax" spellcheck="false">npm link

</pre>

This will take your package and create a symbolic link in the npm global folder to it.

/Users/vega/.nvm/versions/node/v10.15.0/lib/node_modules/wrap-with-poo -> /Users/vega/dev/npm/wrap-with-poo

This means that your project can be used in any project with one more simple step. The next thing you need to do is to move into the project wrap-with-poo-testing and run the following command.

<pre class="ql-syntax" spellcheck="false">npm link wrap-with-poo

</pre>

This will output the following: /Users/vega/dev/npm/wrap-with-poo-testing/node_modules/wrap-with-poo -> /Users/vega/.nvm/versions/node/v10.15.0/lib/node_modules/wra

p-with-poo -> /Users/vega/dev/npm/wrap-with-poo

That is all there is to it, no need to install the dependency. You are ready to begin writing some code to play with you new plugin. Open up the app.js and add the following code.

<pre class="ql-syntax" spellcheck="false">const poo = require('wrap-with-poo');
const boring = 'This is a boring string';
const fun = poo(boring);

console.log(fun);

</pre>

And run the following command from the integrated terminal

<pre class="ql-syntax" spellcheck="false">node app.js

</pre>

And you will get the following output

<pre class="ql-syntax" spellcheck="false">💩This is a boring string💩

</pre>

Publish Source Code

Now that we know our project works its time to make it public for everyone to use. If you haven’t done so yet I would add your project to Github or whatever source code hosting you prefer.

<pre class="ql-syntax" spellcheck="false">git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/cfaddict/wrap-with-poo.git
git push -u origin master

</pre>

Now that it is on Github go back and add an entry to your package.json so everyone knows where to find the source code using the homepage key.

<pre class="ql-syntax" spellcheck="false">{
"name": "wrap-with-poo",
"version": "0.0.1",
"description": "This package will wrap any string you give it with the poop emoji",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [
"node",
"npm",
"poop"
],
"author": "Dan Vega",
"license": "MIT",
"homepage": "https://github.com/cfaddict/wrap-with-poo"
}

</pre>

Publishing NPM Package

It is now time to publish our project to npm so that anyone can use it. If this is your first time publishing a package open up a terminal in the wrap-with-poo directory and type the following command.

<pre class="ql-syntax" spellcheck="false">npm adduser

</pre>

This will ask you for your npm account information such as username, password, and email.

<pre class="ql-syntax" spellcheck="false">vega wrap-with-poo (master) $ npm adduser
Username: therealdanvega
Password:
Email: (this IS public) [email protected]
Logged in as therealdanvega on https://registry.npmjs.org/.

</pre>

Now you’re ready to publish but there are a couple of things you need to remember. First every npm package must have a unique name. I would head over to npm and do a quick search for your package. I have already published the package wrap-with-poo so yours will need a new unique name.

The next thing you need to know is that your version number matters. I start with 0.0.1 and work my way up from there. Once you publish a specific version you can’t publish the same version again. It is a good idea to build a number of features into a version and then publish that. If you run

<pre class="ql-syntax" spellcheck="false">npm version

</pre>

It will tell you what your current version is.

<pre class="ql-syntax" spellcheck="false">{ 'wrap-with-poo': '0.0.1',
npm: '6.7.0',
ares: '1.15.0',
cldr: '33.1',
http_parser: '2.8.0',
icu: '62.1',
modules: '64',
napi: '3',
nghttp2: '1.34.0',
node: '10.15.0',
openssl: '1.1.0j',
tz: '2018e',
unicode: '11.0',
uv: '1.23.2',
v8: '6.8.275.32-node.45',
zlib: '1.2.11' }

</pre>

If everything looks good you can publish your new project by running

<pre class="ql-syntax" spellcheck="false">npm publish

</pre>

This might take a few seconds but if everything went ok your package should now be live on npm.

Congrats on publishing your first npm package!!!

Now you can go into any project that already has a package.json and type the following

<pre class="ql-syntax" spellcheck="false">npm install wrap-with-poo

</pre>

And use it just like we did in our testing example above.


Documentation

I know some people say that you should create documentation from the beginning but I am not always sure what my code is going to end up looking like so I usually wait on this. Create a README.md in the root of your project and add some information about your project.

  • What does your npm package do?
  • Why did you create it.
  • How do you install it?
  • Are there any configuration options?

Conclusion

As I said at the beginning of this article I can’t believe I published my first npm package this weekend. I just never really had a need to do so until now but I was really excited to learn how easy it was. If this is your first npm package please leave me some comments or tweet at me when yours is live!

Happy Coding!


Learn More

The Complete Node.js Developer Course (2nd Edition)

Learn and Understand NodeJS

Node JS: Advanced Concepts

GraphQL: Learning GraphQL with Node.Js

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Beginner Full Stack Web Development: HTML, CSS, React & Node

Node with React: Fullstack Web Development

MERN Stack Front To Back: Full Stack React, Redux & Node.js

Originally published at https://www.danvega.me

How to setting up Node API with Typescript

How to setting up Node API with Typescript

Note: You should have Nodejs installed on your machine.

First thing is to create our project folder and initialize it with npm to generate the package.json file.

Install dependencies

npm i express --save
npm i @types/node @types/express ts-node typescript nodemon --save-dev

Create a tsconfig.json file in the root of your application or run npx tsc --init on your terminal and add the configuration below.

{ 
"compilerOptions":
  {
  "target": "es6",
  "module": "commonjs",
  "allowJs": true,
  "outDir": "./build",
  "rootDir": "./src",
  "esModuleInterop": true
  }
}

Note: More options can be added to the tsconfig.json file.
Find out more here.

Add scripts to package.json file.

"scripts": 
  {
    "dev": "nodemon src/app.ts",
    	"start": "tsc && node build/app"
    }

Create a src directory where our application would be built. Inside the src directory, create an app.ts file.

Inside the app.ts file, add the code below.

import express, { Application, Request, Response, NextFunction } from "express";

const app: Application = express();

app.use(express.json());

app.get("/", (req: Request, res: Response): object => {
    return res.json({ status: "success", message: "Welcome to API Service" });
  }
);

app.use((req: Request, res: Response, next: NextFunction) => {
  const error = new Error("Route Not found");
  next(error);
});

app.use((error: { message: string; status: number }, req: Request, res: Response,next: NextFunction
  ) => {
    res.status(error.status || 500);
    res.json({
      status: "error",
      message: error.message
    });
    next();
  }
);

const PORT: any = process.env.PORT || 3000;

app.listen(PORT, () => console.log(`app listening on port ${PORT}`));

At this point, your project structure should look like the image below.

This is image title

Development

To run the application on the development environment, run the command below

npm run dev

Note: The above command compiles the files found in the src directory in memory.

Production

To run the application on the production environment, run the command below

npm start

Note: The above command compiles the files found in the src directory to a build directory and runs the app.js file in the build directory, as specified above in the start script in our package.json file.

The project used in this article can be found here.

Thanks for reading.

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

<span class="ql-cursor"></span>Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

<pre class="ql-syntax" spellcheck="false">sudo npm install -g @angular/cli </pre>

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

<pre class="ql-syntax" spellcheck="false">ng new angular7-crud </pre>

then go to the newly created folder of angular application with **cd /angular7-crud ** and type **ng serve. **Now, open the browser then go to <a href="http://localhost:4200" title="" target="_blank">http://localhost:4200</a> you should see this page.

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

<pre class="ql-syntax" spellcheck="false">let app = require('express')(), server = require('http').Server(app), bodyParser = require('body-parser') express = require('express'), cors = require('cors'), http = require('http'), path = require('path');   let articleRoute = require('./Routes/article'), util = require('./Utilities/util');   app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false }));   app.use(cors());   app.use(function(err, req, res, next) { return res.send({ "statusCode": util.statusCode.ONE, "statusMessage":util.statusMessage.SOMETHING_WENT_WRONG }); });   app.use('/article', articleRoute);   // catch 404 and forward to error handler app.use(function(req, res, next) { next(); });   /*first API to check if server is running*/ app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../server/client/dist/index.html')); })     server.listen(3000,function(){ console.log('app listening on port: 3000'); }); </pre>

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

<pre class="ql-syntax" spellcheck="false">let express = require('express'), router = express.Router(), util = require('../Utilities/util'), articleService = require('../Services/article');   /**Api to create article */ router.post('/create-article', (req, res) => { articleService.createArticle(req.body, (data) => { res.send(data); }); });   // /**Api to update article */ router.put('/update-article', (req, res) => { articleService.updateArticle(req.body, (data) => { res.send(data); }); });   // /**Api to delete the article */ router.delete('/delete-article', (req, res) => { articleService.deleteArticle(req.query, (data) => { res.send(data); }); });   /**Api to get the list of article */ router.get('/get-article', (req, res) => { documentService.getArticle(req.query, (data) => { res.send(data); }); });   // /**API to get the article by id... */ router.get('/get-article-by-id', (req, res) => { articleService.getArticleById(req.query, (data) => { res.send(data); }); });   module.exports = router; </pre>


Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

<pre class="ql-syntax" spellcheck="false">let environment = "dev";   let serverURLs = { "dev": { "NODE_SERVER": "http://localhost", "NODE_SERVER_PORT": "3000", "MYSQL_HOST": 'localhost', "MYSQL_USER": 'root', "MYSQL_PASSWORD": 'password', 'MYSQL_DATABASE': 'demo_angular7_crud', } }   let config = { "DB_URL_MYSQL": { "host": `${serverURLs[environment].MYSQL_HOST}`, "user": `${serverURLs[environment].MYSQL_USER}`, "password": `${serverURLs[environment].MYSQL_PASSWORD}`, "database": `${serverURLs[environment].MYSQL_DATABASE}` }, "NODE_SERVER_PORT": { "port": `${serverURLs[environment].NODE_SERVER_PORT}` }, "NODE_SERVER_URL": { "url": `${serverURLs[environment].NODE_SERVER}` } };   module.exports = { config: config }; </pre>

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

 

<pre class="ql-syntax" spellcheck="false">var config = require("../Utilities/config").config; var mysql = require('mysql'); var connection = mysql.createConnection({ host: config.DB_URL_MYSQL.host, user: config.DB_URL_MYSQL.user, password: config.DB_URL_MYSQL.password, database: config.DB_URL_MYSQL.database, });   connection.connect(() => { require('../Models/Article').initialize(); });   let getDB = () => { return connection; }   module.exports = { getDB: getDB } </pre>

Now I am creating separate file name util.js to save common methods and common status code/message:

<pre class="ql-syntax" spellcheck="false">// Define Error Codes let statusCode = { OK: 200, FOUR_ZERO_FOUR: 404, FOUR_ZERO_THREE: 403, FOUR_ZERO_ONE: 401, FIVE_ZERO_ZERO: 500 };   // Define Error Messages let statusMessage = { SERVER_BUSY : 'Our Servers are busy. Please try again later.', DATA_UPDATED: 'Data updated successfully.', DELETE_DATA : 'Delete data successfully',   };   module.exports = { statusCode: statusCode, statusMessage: statusMessage } </pre>

Now the next part is model, So create a folder named Models and create a file Article.js and add the below code in it:

<pre class="ql-syntax" spellcheck="false">let mysqlConfig = require("../Utilities/mysqlConfig");   let initialize = () => { mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");   }   module.exports = { initialize: initialize } </pre>

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

<pre class="ql-syntax" spellcheck="false">let dbConfig = require("../Utilities/mysqlConfig");

 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += and aricle_id = '${criteria.aricle_id}' : true;
dbConfig.getDB().query(select * from article where 1,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions +=  and id = '${criteria.id}' : true;
dbConfig.getDB().query(select * from article where 1&nbsp;${conditions}, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions +=  and id = '${criteria.id}' : true;
console.log(delete from article where 1&nbsp;${conditions});
dbConfig.getDB().query(delete from article where 1&nbsp;${conditions}, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions +=  and id = '${criteria.id}' : true;
dataToSet.category ? setData += category = '${dataToSet.category}' : true;
dataToSet.title ? setData += , title = '${dataToSet.title}' : true;
console.log(UPDATE article SET&nbsp;${setData}&nbsp;where 1&nbsp;${conditions});
dbConfig.getDB().query(UPDATE article SET&nbsp;${setData}&nbsp;where 1&nbsp;${conditions}, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}
</pre>

Now one create Services folder and add a file article.js for all the logic of API

<pre class="ql-syntax" spellcheck="false"> let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage":util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage":util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage":util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage":util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage":util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage":util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage":util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage":util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};
</pre>

3. Create angular component for performing CRUD task of article

<pre class="ql-syntax" spellcheck="false">ng g component article
</pre>

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

<pre class="ql-syntax" spellcheck="false">create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)
</pre>

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

<pre class="ql-syntax" spellcheck="false"> import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
</pre>

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

<pre class="ql-syntax" spellcheck="false">import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}
</pre>

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

 

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

<pre class="ql-syntax" spellcheck="false">import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}
</pre>

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<pre class="ql-syntax" spellcheck="false"><h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'">Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button"(click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button"(click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>
</pre>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link <a href="http://localhost:4200." target="_blank">http://localhost:4200.</a> App will look like below

 

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.



================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on <a href="https://l.morioh.com/b0a3f595aa?r=https://www.facebook.com/angular4u" title="" target="_blank">Facebook</a> | <a href="https://l.morioh.com/b0a3f595aa?r=https://twitter.com/codek_tv" title="" target="_blank">Twitter</a>

Learn More

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/H1jE_tD3l" title="" target="_blank">Angular 8 (formerly Angular 2) - The Complete Guide</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/HJigQzgZx" title="" target="_blank">Learn and Understand AngularJS</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/ry7Ey9yKW" title="" target="_blank">The Complete Angular Course: Beginner to Advanced</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/rkjpGfa5W" title="" target="_blank">Angular Crash Course for Busy Developers</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/SkdU19JFZ" title="" target="_blank">Angular Essentials (Angular 2+ with TypeScript)</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/B1oHaY8cM" title="" target="_blank">Angular (Full App) with Angular Material, Angularfire & NgRx</a>

☞ <a href="https://l.morioh.com/b0a3f595aa?r=http://learnstartup.net/p/Skf7ILFw3l" title="" target="_blank">Angular & NodeJS - The MEAN Stack Guide</a>



The perfect architecture flow for your next Node.js project

The perfect architecture flow for your next Node.js project

A good start is half the battle, said someone wiser than me. And I can’t think of any quote that would better describe the situation every developer gets into whenever starting a new project. Laying out a project’s structure in a practical way is one of the hardest points of the development process and, indeed, a delicate one.

We can define a path about discussing Node.js technologies, how to choose what front-end framework to use, and now we can try to dig deeper on how to structure our web apps once we have decided on the tech stack to use.

The importance of good architecture

Having a good starting point when it comes to our project architecture is vital for the life of the project itself and how you will be able to tackle changing needs in the future. A bad, messy project architecture often leads to:

  • Unreadable and messy code, making the development process longer and the product itself harder to test

  • Useless repetition, making code harder to maintain and manage

  • Difficulty implementing new features. Since the structure can become a total mess, adding a new feature without messing up existing code can become a real problem

With these points in mind, we can all agree that our project architecture is extremely important, and we can also declare a few points that can help us determine what this architecture must help us do:

  • Achieve clean and readable code

  • Achieve reusable pieces of code across our application

  • Help us to avoid repetitions

  • Make life easier when adding a new feature into our application

Establishing a flow

Now we can discuss what I usually refer to as the application structure flow. The application structure flow is a set of rules and common practices to adopt while developing our applications. These are the results of years of experience working with a technology and understanding what works properly and what doesn’t.

The goal of this article is to create a quick reference guide to establishing the perfect flow structure when developing Node.js applications. Let’s start to define our rules:

Rule #1: Correctly organize our files into folders

Everything has to have its place in our application, and a folder is the perfect place to group common elements. In particular, we want to define a very important separation, which brings us to rule number #2:

Rule #2: Keep a clear separation between the business logic and the API routes

See, frameworks like Express.js are amazing. They provide us with incredible features for managing requests, views, and routes. With such support, it might be tempting for us to put our business logic into our API routes. But this will quickly make them into giant, monolithic blocks that will reveal themselves to be unmanageable, hard to read, and prone to decomposition.

Please also don’t forget about how the testability of our application will decrease, with consequently longer development times. At this point, you might be wondering, “How do we solve this problem, then? Where can I put my business logic in a clear and intelligent way?” The answer is revealed in rule number #3.

Rule #3: Use a service layer

This is the place where all our business logic should live. It’s basically a collection of classes, each with its methods, that will be implementing our app’s core logic. The only part you should ignore in this layer is the one that accesses the database; that should be managed by the data access layer.

Now that we have defined these three initial rules, we can graphically represent the result like this:
This is image title
Separating our business logic from our API routes.

And the subsequent folder structure sending us back to rule #1 can then become:
This is image title

By looking at this last image, we can also establish two other rules when thinking about our structure.

Rule #4: Use a config folder for configuration files

This is image title

Rule #5: Have a scripts folder for long npm scripts

This is image title

Rule #6: Use dependency injection

Node.js is literally packed with amazing features and tools to make our lives easier. However, as we know, working with dependencies can be quite troublesome most of the time due to problems that can arise with testability and code manageability.

There is a solution for that, and it’s called dependency injection.

Dependency injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object.

By using this inside our Node applications, we:

  • Have an easier unit testing process, passing dependencies directly to the modules we would like to use instead of hardcoding them

  • Avoid useless modules coupling, making maintenance much easier

  • Provide a faster git flow. After we defined our interfaces, they will stay like that, so we can avoid any merge conflicts.
    This is image title
    Using Node.js without dependency injection.

Simple but still not very flexible as an approach to our code. What happens if we want to alter this test to use an example database? We should alter our code to adapt it to this new need. Why not pass the database directly as a dependency instead?
This is image title

Rule #7: Use unit testing

Now that we know we have got dependency injection under our belt, we can also implement unit testing for our project. Testing is an incredibly important stage in developing our applications. The whole flow of the project — not just the final result — depends on it since buggy code would slow down the development process and cause other problems.

A common way to test our applications is to test them by units, the goal of which is to isolate a section of code and verify its correctness. When it comes to procedural programming, a unit may be an individual function or procedure. This process is usually performed by the developers who write the code.

Benefits of this approach include:

Improved code quality

Unit testing improves the quality of your code, helping you to identify problems you might have missed before the code goes on to other stages of development. It will expose the edge cases and makes you write better overall code

Bugs are found earlier

Issues here are found at a very early stage. Since the tests are going to be performed by the developer who wrote the code, bugs will be found earlier, and you will be able to avoid the extremely time-consuming process of debugging

Cost reduction

Fewer flaws in the application means less time spent debugging it, and less time spent debugging it means less money spent on the project. Time here is an especially critical factor since this precious unit can now be allocated to develop new features for our product

Rule #8: Use another layer for third-party services calls

Often, in our application, we may want to call a third-party service to retrieve certain data or perform some operations. And still, very often, if we don’t separate this call into another specific layer, we might run into an out-of-control piece of code that has become too big to manage.

A common way to solve this problem is to use the pub/sub pattern. This mechanism is a messaging pattern where we have entities sending messages called publishers, and entities receiving them called subscribers.

Publishers won’t program the messages to be sent directly to specific receivers. Instead, they will categorize published messages into specific classes without knowledge of which subscribers, if any, may be dealing with them.

In a similar way, the subscribers will express interest in dealing with one or more classes and only receive messages that are of interest to them — all without knowledge of which publishers are out there.

The publish-subscribe model enables event-driven architectures and asynchronous parallel processing while improving performance, reliability, and scalability.

Rule #9: Use a linter

This simple tool will help you to perform a faster and overall better development process, helping you to keep an eye on small errors while keeping the entire application code uniform.
This is image title
Example of using a linter.

Rule #10: Use a style guide

Still thinking about how to properly format your code in a consistent way? Why not adapt one of the amazing style guides that Google or Airbnb have provided to us? Reading code will become incredibly easier, and you won’t get frustrated trying to understand how to correctly position that curly brace.
This is image title
Google’s JavaScript style guide.

Rule #11: Always comment your code

Writing a difficult piece of code where it’s difficult to understand what you are doing and, most of all, why? Never forget to comment it. This will become extremely useful for your fellow developers and to your future self, all of whom will be wondering why exactly you did something six months after you first wrote it.

Rule #12: Keep an eye on your file sizes

Files that are too long are extremely hard to manage and maintain. Always keep an eye on your file length, and if they become too long, try to split them into modules packed in a folder as files that are related together.

Rule #13: Always use gzip compression

The server can use gzip compression to reduce file sizes before sending them to a web browser. This will reduce latency and lag.
This is image title
An example of using gzip compression with Express.

Rule #14: Use promises

Using callbacks is the simplest possible mechanism for handling your asynchronous code in JavaScript. However, raw callbacks often sacrifice the application control flow, error handling, and semantics that were so familiar to us when using synchronous code. A solution for that is using promises in Node.js.

Promises bring in more pros than cons by making our code easier to read and test while still providing functional programming semantics together with a better error-handling platform.
This is image title
A basic example of a promise.

Rule #15: Use promises’ error handling support

Finding yourself in a situation where you have an unexpected error or behavior in your app is not at all pleasant, I can guarantee. Errors are impossible to avoid when writing our code. That’s simply part of being human.

Dealing with them is our responsibility, and we should always not only use promises in our applications, but also make use of their error handling support provided by the catch keyword.
This is image title

Conclusion

Creating a Node.js application can be challenging, I hope this set of rules helped you to put yourself in the right direction when establishing what type of architecture you are going to use, and what practices are going to support that architecture.

Originally published by Piero Borrelli at blog.logrocket.com

How to build a JavaScript Command Line Interface (CLI) with Node.js

How to build a JavaScript Command Line Interface (CLI) with Node.js

As great as Node.js is for “traditional” web applications, its potential uses are far broader. Microservices, REST APIs, tooling, working with the Internet of Things and even desktop applications: it’s got your back.

Another area where Node.js is really useful is for building command-line applications — and that’s what we’re going to be doing in this article. We’re going to start by looking at a number of third-party packages designed to help work with the command line, then build a real-world example from scratch.

What we’re going to build is a tool for initializing a Git repository. Sure, it’ll run git init under the hood, but it’ll do more than just that. It will also create a remote repository on GitHub right from the command line, allow the user to interactively create a .gitignore file, and finally perform an initial commit and push.

As ever, the code accompanying this tutorial can be found on our GitHub repo.

Build a Node CLI

Why Build a Command-line Tool with Node.js?

Before we dive in and start building, it’s worth looking at why we might choose Node.js to build a command-line application.

The most obvious advantage is that, if you’re reading this, you’re probably already familiar with it — and, indeed, with JavaScript.

Another key advantage, as we’ll see as we go along, is that the strong Node.js ecosystem means that among the hundreds of thousands of packages available for all manner of purposes, there are a number which are specifically designed to help build powerful command-line tools.

Finally, we can use npm to manage any dependencies, rather than have to worry about OS-specific package managers such as Aptitude, Yum or Homebrew.

Tip: that isn’t necessarily true, in that your command-line tool may have other external dependencies.

What We’re Going to Build: ginit

Ginit, our Node CLI in action

For this tutorial, We’re going to create a command-line utility which I’m calling ginit. It’s git init, but on steroids.

You’re probably wondering what on earth that means.

As you no doubt already know, git init initializes a Git repository in the current folder. However, that’s usually only one of a number of repetitive steps involved in the process of hooking up a new or existing project to Git. For example, as part of a typical workflow, you may well:

  1. initialise the local repository by running git init
  2. create a remote repository, for example on GitHub or Bitbucket — typically by leaving the command line and firing up a web browser
  3. add the remote
  4. create a .gitignore file
  5. add your project files
  6. commit the initial set of files
  7. push up to the remote repository.

There are often more steps involved, but we’ll stick to those for the purposes of our app. Nevertheless, these steps are pretty repetitive. Wouldn’t it be better if we could do all this from the command line, with no copy-pasting of Git URLs and such like?

So what ginit will do is create a Git repository in the current folder, create a remote repository — we’ll be using GitHub for this — and then add it as a remote. Then it will provide a simple interactive “wizard” for creating a .gitignore file, add the contents of the folder and push it up to the remote repository. It might not save you hours, but it’ll remove some of the initial friction when starting a new project.

With that in mind, let’s get started.

The Application Dependencies

One thing is for certain: in terms of appearance, the console will never have the sophistication of a graphical user interface. Nevertheless, that doesn’t mean it has to be plain, ugly, monochrome text. You might be surprised by just how much you can do visually, while at the same time keeping it functional. We’ll be looking at a couple of libraries for enhancing the display: chalk for colorizing the output and clui to add some additional visual components. Just for fun, we’ll use figlet to create a fancy ASCII-based banner and we’ll also use clear to clear the console.

In terms of input and output, the low-level Readline Node.js module could be used to prompt the user and request input, and in simple cases is more than adequate. But we’re going to take advantage of a third-party package which adds a greater degree of sophistication — Inquirer. As well as providing a mechanism for asking questions, it also implements simple input controls: think radio buttons and checkboxes, but in the console.

We’ll also be using minimist to parse command-line arguments.

Here’s a complete list of the packages we’ll use specifically for developing on the command line:

  • chalk — colorizes the output
  • clear — clears the terminal screen
  • clui — draws command-line tables, gauges and spinners
  • figlet — creates ASCII art from text
  • inquirer — creates interactive command-line user interface
  • minimist — parses argument options
  • configstore — easily loads and saves config without you having to think about where and how.

Additionally, we’ll also be using the following:

  • @octokit/rest — a GitHub REST API client for Node.js
  • lodash — a JavaScript utility library
  • simple-git — a tool for running Git commands in a Node.js application
  • touch — a tool for implementating the Unix touch command.

Getting Started

Although we’re going to create the application from scratch, don’t forget that you can also grab a copy of the code from the repository which accompanies this article.

Create a new directory for the project. You don’t have to call it ginit, of course:

mkdir ginit
cd ginit

Create a new package.json file:

npm init -y

And edit it to look like so:

{
  "name": "ginit",
  "version": "1.0.0",
  "description": "'git init' on steroids",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Git",
    "CLI"
  ],
  "author": "<YOUR NAME>",
  "license": "ISC"
}

Now install the dependencies:

npm install chalk clear clui figlet inquirer minimist configstore @octokit/rest lodash simple-git touch

Now create an index.js file in the same folder and require the following dependencies:

const chalk = require('chalk');
const clear = require('clear');
const figlet = require('figlet');

Adding Some Helper Methods

We’re going to create a lib folder where we’ll split our helper code into modules:

  • files.js — basic file management
  • inquirer.js — command-line user interaction
  • github.js — access token management
  • repo.js — Git repository management.

Let’s start with lib/files.js. Here, we need to:

  • get the current directory (to get a default repo name)
  • check whether a directory exists (to determine whether the current folder is already a Git repository by looking for a folder named .git).

This sounds straightforward, but there are a couple of gotchas to take into consideration.

Firstly, you might be tempted to use the fs module’s realpathSync method to get the current directory:

path.basename(path.dirname(fs.realpathSync(__filename)));

This will work when we’re calling the application from the same directory (e.g. using node index.js), but bear in mind that we’re going to be making our console application available globally. This means we’ll want the name of the directory we’re working in, not the directory where the application resides. For this purpose, it’s better to use process.cwd:

path.basename(process.cwd());

Secondly, the preferred method of checking whether a file or directory exists keeps changing. The current way is to use existsSync. This returns true if the path exists, false otherwise.

Finally, it’s worth noting that when you’re writing a command-line application, using the synchronous version of these sorts of methods is just fine.

Putting that all together, let’s create a utility package in lib/files.js:

const fs = require('fs');
const path = require('path');

module.exports = {
  getCurrentDirectoryBase: () => {
    return path.basename(process.cwd());
  },

  directoryExists: (filePath) => {
    return fs.existsSync(filePath);
  }
};

Go back to index.js and ensure you require the new file:

const files = require('./lib/files');

With this in place, we can start developing the application.

Initializing the Node CLI

Now let’s implement the start-up phase of our console application.

In order to demonstrate some of the packages we’ve installed to enhance the console output, let’s clear the screen and then display a banner:

// index.js

clear();

console.log(
  chalk.yellow(
    figlet.textSync('Ginit', { horizontalLayout: 'full' })
  )
);

You can run the application using node index.js. The output from this is shown below.

The welcome banner on our Node CLI, created using Chalk and Figlet

Next up, let’s run a simple check to ensure that the current folder isn’t already a Git repository. That’s easy: we just check for the existence of a .git folder using the utility method we just created:

//index.js

if (files.directoryExists('.git')) {
  console.log(chalk.red('Already a Git repository!'));
  process.exit();
}

Tip: notice we’re using the chalk module to show a red-colored message.

Prompting the User for Input

The next thing we need to do is create a function that will prompt the user for their GitHub credentials.

We can use Inquirer for this. The module includes a number of methods for various types of prompts, which are roughly analogous to HTML form controls. In order to collect the user’s GitHub username and password, we’re going to use the input and password types respectively.

First, create lib/inquirer.js and insert this code:

const inquirer = require('inquirer');

const files = require('./files');

module.exports = {
  askGithubCredentials: () => {
    const questions = [
      {
        name: 'username',
        type: 'input',
        message: 'Enter your GitHub username or e-mail address:',
        validate: function( value ) {
          if (value.length) {
            return true;
          } else {
            return 'Please enter your username or e-mail address.';
          }
        }
      },
      {
        name: 'password',
        type: 'password',
        message: 'Enter your password:',
        validate: function(value) {
          if (value.length) {
            return true;
          } else {
            return 'Please enter your password.';
          }
        }
      }
    ];
    return inquirer.prompt(questions);
  },
};

As you can see, inquirer.prompt() asks the user a series of questions, provided in the form of an array as the first argument. Each question is made up of an object which defines the name of the field, the type (we’re just using input and password respectively here, but later we’ll look at a more advanced example), and the prompt (message) to display.

The input the user provides will be passed in to the calling function as a Promise. If successful, we’ll end up with a simple object with two properties; username and password.

You can test all of this out by adding the following to index.js:

const inquirer  = require('./lib/inquirer');

const run = async () => {
  const credentials = await inquirer.askGithubCredentials();
  console.log(credentials);
};

run();

Then run the script using node index.js.

Getting user input with Inquirer

Tip: When you’re done testing, don’t forget to remove the line const inquirer = require('./lib/inquirer'); from index.js, as we won’t actually need it in this file.

Dealing With GitHub Authentication

The next step is to create a function to retrieve an OAuth token for the GitHub API. Essentially, we’re going to “exchange” the username and password for a token.

Of course, we don’t want users to have to enter their credentials every time they use the tool. Instead, we’ll store the OAuth token for subsequent requests. This is where the configstore package comes in.

Storing Config

Storing config is outwardly quite straightforward: you can simply read and write to/from a JSON file without the need for a third-party package. However, the configstore package provides a few key advantages:

  1. It determines the most appropriate location for the file for you, taking into account your operating system and the current user.
  2. There’s no need to explicitly read or write to the file. You simply modify a configstore object and that’s taken care of for you in the background.

To use it, simply create an instance, passing it an application identifier. For example:

const Configstore = require('configstore');
const conf = new Configstore('ginit');

If the configstore file doesn’t exist, it’ll return an empty object and create the file in the background. If there’s already a configstore file, the contents will be decoded into JSON and made available to your application. You can now use conf as a simple object, getting or setting properties as required. As mentioned above, you don’t need to worry about saving it afterwards. That gets taken care of for you.

Tip: on macOS, you’ll find the file in /Users/[YOUR-USERNME]/.config/configstore/ginit.json, on Linux it’s in /home/[YOUR-USERNME]/.config/configstore/ginit.json

Communicating with the GitHub API

Let’s create a library for handling the GitHub token. Create the file lib/github.js and place the following code inside it:

const CLI = require('clui');
const Configstore = require('configstore');
const Octokit = require('@octokit/rest');
const Spinner = CLI.Spinner;

const inquirer = require('./inquirer');
const pkg = require('../package.json');

const conf = new Configstore(pkg.name);

Now let’s add the function that checks whether we’ve already got an access token. We’ll also add a function that allows other libs to access octokit(GitHub) functions:

module.exports = {
  getInstance: () => {
    return octokit;
  },

  getStoredGithubToken: () => {
    return conf.get('github.token');
  },
};

If a conf object exists and it has github.token property, this means that there’s already a token in storage. In this case, we return the token value back to the invoking function. We’ll get to that later on.

If no token is detected, we need to fetch one. Of course, getting an OAuth token involves a network request, which means a short wait for the user. This gives us an opportunity to look at the clui package which provides some enhancements for console-based applications, among them an animated spinner.

Creating a spinner is easy:

const status = new Spinner('Authenticating you, please wait...');
status.start();

Once you’re done, simply stop it and it will disappear from the screen:

status.stop();

Tip: you can also set the caption dynamically using the update method. This could be useful if you have some indication of progress, for example displaying the percentage complete.

Here’s the code to authenticate with GitHub:

...

let octokit;

module.exports = {

  ...

  setGithubCredentials: async () => {
    const credentials = await inquirer.askGithubCredentials();
    octokit = new Octokit({
      auth: {
        username: credentials.username,
        password: credentials.password,
      }
    });
   },

  registerNewToken: async () => {
    const status = new Spinner('Authenticating you, please wait...');
    status.start();

    try {
      const response = await octokit.oauthAuthorizations.createAuthorization({
        scopes: ['user', 'public_repo', 'repo', 'repo:status'],
        note: 'ginit, the command-line tool for initalizing Git repos'
      });
      const token = response.data.token;
      if(token) {
        conf.set('github.token', token);
        return token;
      } else {
        throw new Error("Missing Token","GitHub token was not found in the response");
      }
    } catch (err) {
      throw err;
    } finally {
      status.stop();
    }
  },
};

Let’s step through this:

  1. we declare an octokit variable in the module’s upper scope
  2. we prompt the user for their credentials using the askGithubCredentials method we defined earlier
  3. we pass an auth option to the Octokit constructor to enable authenticated requests
  4. the result is assigned to the octokit variable so that it is available throughout the module
  5. we attempt to register a new access token for our application
  6. if we manage to get an access token, we set it in the configstore for next time.
  7. we then return the token.

Any access tokens you create, whether manually or via the API as we’re doing here, you’ll be able to see them here. During the course of development, you may find you need to delete ginit’s access token — identifiable by the note parameter supplied above — so that you can re-generate it.

Tip: if you have two-factor authentication enabled on your GitHub account, the process is slightly more complicated. You’ll need to request the confirmation code — for example, one sent via SMS — then supply it using the X-GitHub-OTP header. See the documentation for further information.

If you’ve been following along and would like to try out what we have so far, you can update index.js as follows:

const github = require('./lib/github');

...

const run = async () => {
  let token = github.getStoredGithubToken();
  if(!token) {
    await github.setGithubCredentials();
    token = await github.registerNewToken();
  }
  console.log(token);
}:

Please note you may get a Promise error if something goes wrong, such as inputting the wrong password. I’ll show you the proper way of handling such errors later.

Creating a Repository

Once we’ve got an OAuth token, we can use it to create a remote repository with GitHub.

Again, we can use Inquirer to ask a series of questions. We need a name for the repo, we’ll ask for an optional description, and we also need to know whether it should be public or private.

We’ll use minimist to grab defaults for the name and description from optional command-line arguments. For example:

ginit my-repo "just a test repository"

This will set the default name to my-repo and the description to just a test repository.

The following line will place the arguments in an array indexed by an underscore:

const argv = require('minimist')(process.argv.slice(2));
// { _: [ 'my-repo', 'just a test repository' ] }

Tip: this only really scratches the surface of the minimist package. You can also use it to intepret flags, switches and name/value pairs. Check out the documentation for more information.

We’ll write code to parse the command-line arguments and ask a series of questions. First, update lib/inquirer.js by inserting the following code right after askGithubCredentials function:

askRepoDetails: () => {
  const argv = require('minimist')(process.argv.slice(2));

  const questions = [
    {
      type: 'input',
      name: 'name',
      message: 'Enter a name for the repository:',
      default: argv._[0] || files.getCurrentDirectoryBase(),
      validate: function( value ) {
        if (value.length) {
          return true;
        } else {
          return 'Please enter a name for the repository.';
        }
      }
    },
    {
      type: 'input',
      name: 'description',
      default: argv._[1] || null,
      message: 'Optionally enter a description of the repository:'
    },
    {
      type: 'list',
      name: 'visibility',
      message: 'Public or private:',
      choices: [ 'public', 'private' ],
      default: 'public'
    }
  ];
  return inquirer.prompt(questions);
},

Next, create the file lib/repo.js and add this code:

const CLI = require('clui');
const fs = require('fs');
const git = require('simple-git/promise')();
const Spinner = CLI.Spinner;
const _ = require('lodash');

const inquirer = require('./inquirer');
const gh = require('./github');

module.exports = {
  createRemoteRepo: async () => {
    const github = gh.getInstance();
    const answers = await inquirer.askRepoDetails();

    const data = {
      name: answers.name,
      description: answers.description,
      private: (answers.visibility === 'private')
    };

    const status = new Spinner('Creating remote repository...');
    status.start();

    try {
      const response = await github.repos.createForAuthenticatedUser(data);
      return response.data.ssh_url;
    } catch(err) {
      throw err;
    } finally {
      status.stop();
    }
  },
};

Once we have that information, we can simply use the GitHub package to create a repo, which will give us a URL for the newly created repository. We can then set that up as a remote in our local Git repository. First, however, let’s interactively create a .gitignore file.

Creating a .gitignore File

For the next step, we’ll create a simple command-line “wizard” to generate a .gitignore file. If the user is running our application in an existing project directory, let’s show them a list of files and directories already in the current working directory, and allow them to select which ones to ignore.

The Inquirer package provides a checkbox input type for just that.

Inquirer’s checkboxes in action

The first thing we need to do is scan the current directory, ignoring the .git folder and any existing .gitignore file (we do this by making use of lodash’s without method):

const filelist = _.without(fs.readdirSync('.'), '.git', '.gitignore');

If there’s nothing to add, there’s no point in continuing, so let’s simply touch the current .gitignore file and bail out of the function:

if (filelist.length) {
  ...
} else {
  touch('.gitignore');
}

Finally, let’s utilize Inquirer’s checkbox “widget” to list the files. Insert the following code in lib/inquirer.js:

askIgnoreFiles: (filelist) => {
  const questions = [
    {
      type: 'checkbox',
      name: 'ignore',
      message: 'Select the files and/or folders you wish to ignore:',
      choices: filelist,
      default: ['node_modules', 'bower_components']
    }
  ];
  return inquirer.prompt(questions);
},

Notice that we can also provide a list of defaults. In this case, we’re pre-selecting node_modules and bower_components, should they exist.

With the Inquirer code in place, we can now construct the createGitignore() function. Insert this code in lib/repo.js:

createGitignore: async () => {
  const filelist = _.without(fs.readdirSync('.'), '.git', '.gitignore');

  if (filelist.length) {
    const answers = await inquirer.askIgnoreFiles(filelist);

    if (answers.ignore.length) {
      fs.writeFileSync( '.gitignore', answers.ignore.join( '\n' ) );
    } else {
      touch( '.gitignore' );
    }
  } else {
    touch('.gitignore');
  }
},

Once “submitted”, we then generate a .gitignore by joining up the selected list of files, separated with a newline. Our function now pretty much guarantees we’ve got a .gitignore file, so we can proceed with initializing a Git repository.

Interacting with Git from Within the App

There are a number of ways to interact with Git, but perhaps the simplest is to use the simple-git package. This provides a set of chainable methods which, behind the scenes, run the Git executable.

These are the repetitive tasks we’ll use it to automate:

  1. run git init
  2. add the .gitignore file
  3. add the remaining contents of the working directory
  4. perform an initial commit
  5. add the newly-created remote repository
  6. push the working directory up to the remote.

Insert the following code in lib/repo.js:

setupRepo: async (url) => {
  const status = new Spinner('Initializing local repository and pushing to remote...');
  status.start();

  return git.init()
    .then(git.add('.gitignore'))
    .then(git.add('./*'))
    .then(git.commit('Initial commit'))
    .then(git.addRemote('origin', url))
    .then(git.push('origin', 'master'))
    .finally(status.stop());
},

Putting It All Together

First, let’s set a helper function in lib/github.js for setting up an oauth authentication:

githubAuth: (token) => {
  octokit = new Octokit({
    auth: token
  });
},

Next, we create a function in index.js for handling the logic of acquiring the token. Place this code before the run() function’:

const getGithubToken = async () => {
  // Fetch token from config store
  let token = github.getStoredGithubToken();
  if(token) {
    return token;
  }

  // No token found, use credentials to access GitHub account
  await github.setGithubCredentials();

  // register new token
  token = await github.registerNewToken();
  return token;
};

Finally, we update the run() function by writing code that will handle the main logic of the app:

const repo = require('./lib/repo');

...

const run = async () => {
  try {
    // Retrieve & Set Authentication Token
    const token = await getGithubToken();
    github.githubAuth(token);

    // Create remote repository
    const url = await repo.createRemoteRepo();

    // Create .gitignore file
    await repo.createGitignore();

    // Set up local repository and push to remote
    await repo.setupRepo(url);
    console.log(chalk.green('All done!'));
  } catch(err) {
      if (err) {
        switch (err.status) {
          case 401:
            console.log(chalk.red('Couldn\'t log you in. Please provide correct credentials/token.'));
            break;
          case 422:
            console.log(chalk.red('There already exists a remote repository with the same name'));
            break;
          default:
            console.log(err);
        }
      }
  }
};

As you can see, we ensure the user is authenticated before calling all of our other functions (createRemoteRepo(), createGitignore(), setupRepo()) sequentially. The code also handles any errors and offers the user appropriate feedback.

You can check out the completed index.js file on our GitHub repo.

At this point you should have a working app. Give it a try and satisfy yourself that it works as expected.

Making the ginit Command Available Globally

The one remaining thing to do is to make our command available globally. To do this, we’ll need to add a shebang line to the top of index.js:

#!/usr/bin/env node

Next, we need to add a bin property to our package.json file. This maps the command name (ginit) to the name of the file to be executed (relative to package.json).

"bin": {
  "ginit": "./index.js"
}

After that, install the module globally and you’ll have a working shell command:

npm install -g

Tip: this will also work on Windows, as npm will helpfully install a cmd wrapper alongside your script.

If you want to confirm the install worked, you can list your globally installed Node modules using:

npm ls --depth=0

Taking it Further

We’ve got a fairly nifty, albeit simple command-line app for initializing Git repositories. But there’s plenty more you could do to enhance it further.

If you’re a Bitbucket user, you could adapt the program to use the Bitbucket API to create a repository. There’s a Node.js API wrapper available to help you get started. You may wish to add an additional command-line option or prompt to ask the user whether they want to use GitHub or Bitbucket (Inquirer would be perfect for just that) or merely replace the GitHub-specific code with a Bitbucket alternative.

You could also provide the facility to specify your own set of defaults for the .gitgnore file, instead of a hardcoded list. The preferences package might be suitable here, or you could provide a set of “templates” — perhaps prompting the user for the type of project. You might also want to look at integrating it with the .gitignore.io command-line tool/API.

Beyond all that, you may also want to add additional validation, provide the ability to skip certain sections, and more. If you have any other ideas, do let us know in the comments.

How to Build a CLI for Node.js application using Typescript

How to Build a CLI for Node.js application using Typescript

Story

One day I wanted to create a CLI tool in Node.js.
It was supposed to be launched from terminal, and it was supposed to accept some CLI arguments and options.

So I could have written something like this:

<div class="highlight">
const [env] = process.argv.slice(2);

function main({ env }) {
    // ...
}

main({ env });

</div>

It would work perfectly fine and I believe such approach is the most appropriate in some cases.
But predictably at some point I needed to support something else except the "env".

<div class="highlight">
const [env, _dryRunFlag] = process.argv.slice(2);

const isDryRun = Boolean(_dryRunFlag);

function main({ env, isDryRun }) {
    // ...
}

main({ env, isDryRun });

</div>

It's not hard to tell how problematic this code is. But there it is not a problem! All I needed is argument parser.

Using libraries

Using commander.js the example above could be rewritten like this:

<div class="highlight">
const program = require('commander');

program
  .option('-e, --env', 'app environment')
  .option('-n, --dry-run', 'pretend to do things')

program.parse(process.argv);

console.log(program);

</div>

It will work fine. Let's see how yargs configuration will look like:

<div class="highlight">
const yargs = require('yargs');

const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'],
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        default: 80,
        description: 'port'
    }
  })
    .argv;

console.log(argv);

</div>

Also fine!

But since we are using a third party library, we probably want to check out some features shipped with them.

Features

typescript/flow support - data validation - help generation - completions etc.

For me the cornerstone was the first. Let me show you how it works.

Types

If you want to use Typescript in your project you probably would like to have the data typed. So instead of working with unknown or any you will be able to operate with numbers or booleans etc.

Unfortunately Commander's typings help you to write CLI configuration code but it won't help you to get type of the data a user can pass to the app. So if you are going to use yargs you might want to stick to the yargs. Using yargs and with a few tweaks in the code you can end up with this code:

<div class="highlight">
import * as yargs from 'yargs';

const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        default: 80,
        description: 'port'
    }
  })
    .argv;

console.log(argv);

</div>

Disclaimer: I'm using **yargs* version 14.0.0 and @types/yargs version ^13.0.3*

In this example the type of argv will be resolved to:

<div class="highlight">
const argv: {
    [x: string]: unknown;
    env: "dev" | "prod";
    port: number;
    _: string[];
    $0: string;
}

</div>

Which is quite impressive.
So now you can go on and work with your data accordingly to types... right?
Let's see.

If you call this app with no arguments:

<div class="highlight">
node app.js

</div>

It will output the help text and will complain that you did not provide env option:

<div class="highlight">
Options:
  --help      Show help                                                [boolean]
  --version   Show version number                                      [boolean]
  --env, -e   app environment                [required] [choices: "dev", "prod"]
  --port, -p  port                                                 [default: 80]

Missing required argument: env

</div>

That's nice! So yargs will throw an error when you pass invalid data... kind of...

This command

<div class="highlight">
node app.js --env abc

</div>

will produce the help text and an error message:

<div class="highlight">
Invalid values:
  Argument: env, Given: "abc", Choices: "dev", "prod"

</div>

Also great!

What if I pass some rubbish as port, though?

<div class="highlight">
node app.js -e dev -p abc

</div>

...it will output the following object:

<div class="highlight">
{ _: [], e: 'dev', env: 'dev', p: 'abc', port: 'abc', '$0': 'foo' }

</div>

Whoa! It is not what I expected! The obvious problem here is that I can write something like this:

<div class="highlight">
console.log(argv.port.toFixed(0))

</div>

and it will fail with

<div class="highlight">
TypeError: argv.port.toFixed is not a function

</div>

But the biggest problem is that argv has a wrong type! I'm not only to make that mistake, but
my Typescript compiler will eat it also. But the worst part is that my IDE will show me the type of
args.port as number. As for me, having a wrong type is much worse than having no type at all.

So what exactly went wrong here? Actually I just missed the type of the option:

<div class="highlight">
const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        type: 'number',
        default: 80,
        description: 'port'
    }
  })
    .argv;

</div>

I guess, without explicit type yargs treats the type automatically regardless the default value. While
@types/yargs infers the type from default property:
here

<div class="highlight">
type InferredOptionType<O extends Options | PositionalOptions> =
    O extends { default: infer D } ? D :
    O extends { type: "count" } ? number :
    O extends { count: true } ? number :
    O extends { required: string | true } ? RequiredOptionType<O> :
    O extends { require: string | true } ? RequiredOptionType<O> :
    O extends { demand: string | true } ? RequiredOptionType<O> :
    O extends { demandOption: string | true } ? RequiredOptionType<O> :
    RequiredOptionType<O> | undefined;

</div>

Okay, so I will fix that:

<div class="highlight">
import * as yargs from 'yargs';

const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        type: 'number', // added the type
        default: 80,
        description: 'port'
    }
  })
    .argv;

console.log(argv);

console.log(argv.port.toFixed(0));

</div>

Now I expect to receive either number or to see help text once again and the error message.

<div class="highlight">
node app.js -e dev -p e

</div>

We-e-ell. Literally speaking it meets my expectations:

<div class="highlight">
{ _: [], e: 'dev', env: 'dev', p: NaN, port: NaN, '$0': 'foo' }
NaN

</div>

I did not get the error message because I got the number, as long as you define a number
as

<div class="highlight">
const isNumber = value => typeof value === 'number';

</div>

But nevertheless I expected an error here. Can we fix that? Yes, we can! Yargs supports data validation

So I will fix the code example:

<div class="highlight">
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        type: 'number',
        default: 80,
        description: 'port'
    }
  })
  .check(data => { // data is actually typed here, which is also nice
      // at this point data.port is already NaN so you can not use typeof
      return !isNaN(data.port);
  })
    .argv;

</div>

Now if I pass any inappropriate value I will get an error:

<div class="highlight">
Argument check failed: ...

</div>

Which is nice! You have to operate with whole data, though.
So if you have 10 options needing validation you will have to
(unless I miss something of course) declare these 10 options in one place
and validate in one .check(...) call containing 10 checks.

Also you can use .coerce(...)

<div class="highlight">
const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        type: 'number',
        default: 80,
        description: 'port'
    }
  })
    .coerce('port', port => { // port is not typed, but it is fine
        // at this point port is actual string you passed to the app
        // or the default value so it should be `string | number`
        // in this case
        const result = Number(port);
        if (isNaN(result)) {
            throw new Error('port is not a number');
        }
        return result;
    })
    .argv;

console.log(argv);

</div>

.coerce(...) is used to transform provided options, but also it allows to throw errors,
so you can validate data using it. I'm not sure whether you supposed to though.

Final version

The final version of the app looks like this:

<div class="highlight">
import * as yargs from 'yargs';

const argv = yargs.options({
    env: {
        alias: 'e',
        choices: ['dev', 'prod'] as const,
        demandOption: true,
        description: 'app environment'
    },
    port: {
        alias: 'p',
        type: 'number',
        default: 80,
        description: 'port'
    }
  })
  .check(data => {
      return !isNaN(data.port);
  })
    .argv;

console.log(argv);

</div>

Features: safely typed - validate user input and provide error messages - generate help text with --help flag

Nullability

I should say that yargs (and @types/yargs)
handles typing optional/required options quite good out of the box.
So if you neither provide the default value nor mark
the option as required the option value will be
nullable:

<div class="highlight">
const argv = yargs.options({
    optional: {
        type: 'string'
    }
  })
    .argv;

args.optional // is `string | undefined`

</div>

So: optional => T | undefined in result types, required => either it is provided or an error will be thrown, has default value => if option is not provided - the default value will be used

Disclaimer

Actually I'm impressed by both yargs it-self and @types/yargs.

  • yargs supports huge amount of features, including: input validation, help generation, tab completions, data transformations, commands etc.

More than that yargs has one of the best external typing I ever seen. The types covers not only the library interface but also
the result data.

Conclusion

If you are creating a Typescript application that should support CLI, yargs is one of the best tools you can use.

I hope the article was useful for you and you enjoyed reading it. Please let me know if you have any feedback to the article or typed-cli.

And thank you for your time! :)

This article was originally published on dev.to