Using oclif to create a CLI for the DEV API
Tags: api, cli, devto, oclif
Published: 2020-07-09 02:42:53

---
title: "Using oclif to create a CLI for the DEV API"
published: true
description: In this tutorial, you learn how to use [oclif](https://oclif.io) to create a CLI that lists the articles in your DEV account.
tags: api, cli, devto, oclif
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/lxyptw233vnkudfrh07g.png
---

In this tutorial, you learn how to use [oclif](https://oclif.io) 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](http://npm.im/dev-to-sync), take a look at [that repo](https://github.com/beeman/dev-to-sync) to see how I added some more commands.

## Requirements

- Node.js and NPM, visit the [homepage](https://nodejs.org) 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
  - Generate one here [https://dev.to/settings/account](https://dev.to/settings/account).

## Install the oclif cli

```bash
npm install -g oclif
```

## Setup a new project

```bash
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.

```bash
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:

```bash
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:

```bash
$ 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:

```bash
$ 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.

```json
  "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:

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

### Create the client

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

```typescript
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:

```typescript
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:

```typescript
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

```bash
$ 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](http://dev.to/api) for the available API methods.
- Add colors to the output using [chalk](http://npm.im/chalk).
- Make the CLI interactive using [inquirer](http://npm.im/inquirer).
- Use [oclif plugins](https://oclif.io/docs/plugins) if you want to add additional functionality to your CLI like autoupdate, autocomplete or update warnings.
- Download [beeman/dev-to-sync](https://github.com/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](https://twitter.com/beeman_nl/) or leave a comment on DEV! 🐝