My first serious project in NodeJS: ToRead CLI
Tags: node, cli, typescript
Published: 2018-11-02 15:56:03

---
title: My first serious project in NodeJS: ToRead CLI
published: true
description: Organizing your readings through a CLI interface.
tags: nodejs, cli, typescript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ccqs4awnynt3cadousog.png
---

As an avid reader, I always have a big list of articles, principally about development, that I intend to read. Development is a fast world and every day more articles pile up coming from newsletters, Twitter, etc. I always looked for a tool where I could put my readings. Some of the apps I tried to use was Pocket, Flipboard, Feedly and other less specialized such as Trello, Google Keep, etc. None of them really satisfied me, the features I wanted to have such as searching by tags and title, archive articles, etc. were offered by these services, but under subscription. As a developer I understand the costs related to an application, but it was not an application important enough to make me subscribe it. Then, I resorted to the greatest advantage of a developer: if you don't like the applications in the market, build your own! 

The project is still in an early stage, the features I planned are not all developed yet, **all contributions are welcome on [Github](https://github.com/Leoat12/toread-cli)**! :D

Here I will explain a little bit about the structure of the code. This is my first "serious" project in NodeJS, before that I only wrote some scripts to learn and practice the language. It was also the first time I was able to decently unite NodeJS and TypeScript, a language I am also learning that I appreciate very much. Besides TypeScript, the project has the following main dependencies:

+ Babel
+ Jest
+ Rx-Http-Request
+ JSDOM
+ opn
+ Commander.js
+ RxJS
+ chalk

Some of them are very straight-forward and others I will explain my decision throughout the text. Two projects helped me a lot: [Taskbook](https://github.com/klauscfhq/taskbook) and [TypeScript Babel Starter](https://github.com/Microsoft/TypeScript-Babel-Starter#readme). The first one was the inspiration for this project and some dependencies and design decisions were made based on it. The second was very helpful to me to understand the structure of the project and how to configure Babel to do the job. Many thanks for both! 

The project so far has been divided in 5 .ts files each having a separate role. I am trying to divide the responsibilities as much as possible to facilitate expansion and understandability. The first file is **index.ts**, the main entrance of the application. Using Commander.js I describe all commands in this file, for example the command to list all articles:

```typescript
Commander
    .command('list')
    .alias('ls')
    .description('List all articles')
    .action(() => {
        Actions.getArticles();
    });
```
Some of the commands, of course, are more complex and have arguments, but the structure is basically the same and all lead to a method in the `Actions` class, which leads us to the next file: **actions.ts**

The **actions.ts** has the static class `Actions`, which, as the name implies, implements all the actions of applications such as get the articles, open an article, save an article, etc. For example, above we have `Actions.getArticles()`, which we can see in detail below:

```typescript
static storage:Storage = new Storage();

static getArticles() : void{
        let articles:Article[] = this.storage.getArticles();
        articles.forEach(a => {
            Display.printArticle(a, PresentationMode.LIST);            
        });
    }

```
Generally a method in the `Actions` class figures classes from the other three files that compose the application: **article.ts**, **storage.ts** and **display.ts**, all of them have very straightforward names. First, the easiest one, **article.ts** just contains the interface representing an article:

```typescript
export interface Article{
    id?:number,
    title:string,
    url:string,
    description?:string,
    tags?:string[],
}
```
The **storage.ts** is where the Storage class stays, this class is responsible to write the data in a JSON file, my intention was to do something very lightweight, also inspired on the Taskbook project I mentioned. Below a snippet of the class:
```typescript
    prepareDB(){
        if(!fs.existsSync("file.json")){
            let file : FileStructure = {articles: [], index: 0}
            fs.writeFileSync("file.json", JSON.stringify(file));
        }
    }

    getArticles(): Article[] {
        this.prepareDB();

        let file:FileStructure = JSON.parse(fs.readFileSync("file.json", "utf8"));
        return file.articles;
    }
```
`prepareDB()` is always called to create the JSON file if it doesn't exist. And the the rest of the class has methods to do CRUD, for example the `getArticles()` method above. The entire Storage class is basically depended upon `fs` library and the `JSON` constant. Not a single fancy outside dependency is necessary, really, although I plan to improve it, put cryptography if necessary, among other things. 


Finally, the **display.ts** contains the `Display` class, responsible for everything related to printing on the screen. It uses `chalk` to get it colorful. As a simple example here is the method that prints an error message:
```typescript
static printOpenErrorMessage(){
        let message = 'The article was not found. Verify the ID of the article.';
        console.info(chalk`{red.bold ${message}}`);
    }
```

As I've said before, separation of concerns was the main goal in the infrastructure and sometimes I think that I separated way too much, but I'm good with the way it is going right now. As for the classes and methods itself, I tried to write the code with as less dependencies as possible and as simple as possible, even more so when I'm still learning. Now is a great time to explain some of the dependencies that are still lacking explanation. RxJS and JSDOM, for example, are used when saving a new article in the code below:

```typescript
static saveArticle(url: string, description: string, tags?: string) : void{
        
        RxHR.get(url).subscribe(
            (data:any) => {
                if (data.response.statusCode === 200) {
                    let window = (new JSDOM(data.body)).window;
                    let title = window.document.title;
                    let article:Article = {
                        title: title, 
                        url: url,
                        description: description,
                        tags: tags ? tags.split(',') : []
                    };

                    Actions.storage.saveArticle(article);

                    Display.printSaveArticleMessage(data.response.statusCode);
                    Display.printArticle(article, PresentationMode.ONE);
                } else {
                    Display.printSaveArticleMessage(data.response.statusCode);
                }
            },
            (err:any) => console.error(err) // Show error in console
        );
    }
```
As depicted above, I use RxJS, RxHR and JDOM to make a request to the URL given by the user, get the title of the page and store the article with these information. For me it was the only time it was necessary to RxJS in the entire application, but other opportunities may arise. 

Finally, on the testing end I'm using Jest, which I discovered while developing the application and I found if very straightforward in the way the tests and conducted. Maybe it is more functional than what I'm used to in Java, but it still reminds me the way JUnit is used, so it was a smooth sailing using it. An example of test is below:

```typescript
test('write', () => {    
    let storage = new Storage();
    storage.saveArticle({title: "Teste", url: "http://www.example.com", description: "Description test"})
    expect(fs.existsSync("file.json")).toBe(true);
    fs.unlinkSync("file.json");
});
```

It has been a great experience to develop this project and I'm looking forward to the opinions of everybody on how I can improve it. Since it was developed as practice in mind I really didn't think about publishing it on NPM, but who knows what the future holds... What do you guys think? Let me know everything!