Core Nx Tutorial - Step 2: Create Go CLI

Great! you now have a simple blog set up.

Next, you're going to create a CLI written in Go.

Install Go Locally

Make sure you have Go installed locally by following these instructions.

You can verify that Go is installed correctly by running:

go version

Create the CLI Project

Create a project.json file for your Go CLI.

packages/cli/project.json:

1{
2  "root": "packages/cli",
3  "sourceRoot": "packages/cli/src",
4  "projectType": "application",
5  "targets": {
6    "build": {
7      "executor": "@nrwl/workspace:run-commands",
8      "options": {
9        "command": "go build -o='../../dist/packages/cli/' ./src/ascii.go",
10        "cwd": "packages/cli"
11      }
12    },
13    "serve": {
14      "executor": "@nrwl/workspace:run-commands",
15      "options": {
16        "command": "go run ./src/ascii.go",
17        "cwd": "packages/cli"
18      }
19    }
20  }
21}

You could have the exact same functionality with a package.json file with a scripts section like this:

1{
2  "scripts": {
3    "build": "go build -o='../../dist/packages/cli/' ./src/ascii.go",
4    "serve": "go run ./src/ascii.˙go"
5  }
6}

There are a few reasons to choose project.json for the CLI project.

  1. The presence of package.json might cause other developers to think there is javascript code in this project.
  2. As the scripts in the project get more complex, project.json tends to have a flatter structure - rather than a long horizontal line in package.json with all the cli flags.
  3. The easiest method to run scripts provided in Nx plugins is to use a project.json file.

All of these reasons are matters of preference. After this tutorial, you should have enough of a taste of both styles to make an informed decision about which format you prefer. Read more about package.json configuration and project.json configuration in their respective guides.

Project.json syntax

  • root, sourceRoot and application are properties that help Nx know more about your project.
  • targets is similar to the scripts property in package.json.
  • Just as in package.json, build and serve can be any string you pick.
  • The executor is the code that runs the target. In this case, @nrwl/workspace:run-commands launches a terminal process to execute whatever command you pass in.
  • options contains whatever configuration properties the executor needs to run.

Create the CLI

This CLI will display some ASCII art in the terminal. Create the following files:

packages/cli/src/ascii.go:

1package main
2
3import (
4  "fmt"
5  "os"
6)
7
8func check(e error) {
9  if e != nil {
10      panic(e)
11  }
12}
13
14func main() {
15    fmt.Println("Hello, World!")
16    dat, err := os.ReadFile("src/cow.txt")
17    check(err)
18    fmt.Print(string(dat))
19}

packages/cli/src/cow.txt:

 _____
< moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Run the CLI

Now if you run nx serve cli, you'll see a friendly message:

❯ nx serve cli

> nx run cli:serve


> cli@ serve /Users/isaac/Documents/code/myorg/packages/cli
> go run ./src/ascii.go

Hello, World!
 _____
< moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

 —————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Successfully ran target serve for project cli (2s)

   See Nx Cloud run details at https://nx.app/runs/THRW7SDRL9S

Nx only caches the targets that you tell it to cache. serve is not in the default list of cached targets, so running this command a second time will take the same amount of time. You can see the current list of cacheableOperations in nx.json.

1{
2  //...
3  "cacheableOperations": ["build", "lint", "test", "e2e"]
4}

If you run nx build cli twice, you'll see that Nx is able to cache the commands even though they're entirely written in Go.