In this tutorial, you learn how to use oclif to create a CLI that lists the articles in your DEV account.

The goal is to show how to create a CLI that interacts with an authenticated API. We will only add one single command, (list), however extending the CLI should be pretty straightforward.

This tutorial documents the first steps I took when I built dev-to-sync, take a look at that repo to see how I added some more commands.

Requirements

  • Node.js and NPM, visit the homepage for installation instructions.

    • Run node -v to verify you have version 12 or higher.
    • Run npm -v to verify you have version 6 or higher.
  • A DEV API key

Install the oclif cli

npm install -g oclif

Setup a new project

oclif multi dev-to-sync
cd dev-to-sync 

Initialize git repo

Even though it's not essential for the working of the CLI, you probably want to initialize a git repo and do an initial commit.

git init
git commit -m "Initial commit"

Link local project

In order to make our new CLI available for running it locally while we're developing it, it needs to be linked:

npm link

Run it

Now that that's done, we can give it a run to see if it all works.

If all went well, running the command dev-to-sync should output something like this:

$ dev-to-sync
Sync your DEV account with a local folder

VERSION
  dev-to-sync/0.0.0 darwin-x64 node-v12.18.1

USAGE
  $ dev-to-sync [COMMAND]

COMMANDS
  hello  describe the command here
  help   display help for dev-to-sync

This shows us there is a hello command, which we can execute like this:

$ dev-to-sync hello
hello world from ./src/commands/hello.ts

Creating alias

In this step we give our CLI a shortcut dts so we can invoke it with less keystrokes.

Open package.json and add a key dts with value ./bin/run to the bin object.

  "bin": {
    "dev-to-sync": "./bin/run",
    "dts": "./bin/run"
  }

After this you need to run npm link again to make sure your operating system is aware of the new command.

Create a wrapper for the DEV api

To access the DEV api we create a wrapper class called DevtoClient . We initialize the class with the API key and use that to set up the header, adding the api-key property to each request.

We implement a get method to execute authenticated requests to the DEV api and a getArticles method that retreives the articles.

Install the dependencies

Install node-fetch and it's types:

npm install node-fetch
npm install -D @types/node-fetch

Create the client

Create src/lib/devto-client.ts and add the following snippet:

import fetch from "node-fetch";

export class DevtoClient {
  private readonly baseUrl = "https://dev.to/api";

  private readonly headers: { "api-key": string; "content-type": string };

  constructor(private readonly apiKey: string) {
    this.headers = {
      "api-key": this.apiKey,
      "content-type": "application/json",
    };
  }

  get(url: string) {
    return fetch(this.baseUrl + url, { headers: this.headers }).then((res) =>
      res.json()
    );
  }

  async getArticles(): Promise<any[]> {
    const result = await this.get("/articles/me/all");

    if (!result) {
      throw new Error("Error retrieving articles");
    }
    return result;
  }
}

Create src/lib/utils.ts and add the following snippet:

import { DevtoClient } from "./devto-client";

if (!process.env.DEV_TO_TOKEN) {
  throw new Error(`
Environment variable DEV_TO_TOKEN not found
`);
}

export const client = new DevtoClient(process.env.DEV_TO_TOKEN);

We now can import the client at any of the commands and use it.

Implementing the list command

Create the command

Run the following command to create a new command to the CLI:

oclif command list

Implement the command

Open src/commands/list.ts and replace the content with the following snippet:

import { Command } from "@oclif/command";
import { client } from "../lib/utils";

export default class List extends Command {
  static description = "List articles in a DEV account";

  async run() {
    const articles = await client.getArticles();

    for (const article of articles) {
      console.log(article.title);
    }
  }
}

Test the command

$ dts list
Tutorial: Styling Angular CLI Apps with Bootstrap
Automate your DEV Posts using GitHub Actions
Using yarn with Angular CLI

Where to go from here?

This should be a steady basis to quickly build more functionality. You can, for example:

  • Use oclif command to add more commands, check the DEV api documentation for the available API methods.
  • Add colors to the output using chalk.
  • Make the CLI interactive using inquirer.
  • Use oclif plugins if you want to add additional functionality to your CLI like autoupdate, autocomplete or update warnings.
  • Download beeman/dev-to-sync for a more complete implementation.

Conclusion

In this tutorial, we created a CLI from scratch by using oclif. After setting up the project, we implemented an API client that communicates with the DEV api, and a small method that wraps the API client and takes the API key from the environment. The last step was implementing the list command to retrieve the articles from the list, and show them in the console.

Thanks!

Thanks for reading my article. Feel free to reach out if you have any questions! Follow me on Twitter or leave a comment on DEV! 🐝

This post is also available on DEV.