Deepkit App

Arguments & Flags

Command arguments in the terminal of your command are just regular arguments of the execute method or the function. They are automatically mapped to the command line arguments. If you mark a parameter optional, it is not required to be passed. If you have a default value, it is also not required to be passed.

Depending on the type (string, number, union, etc) the passed value is automatically deserialized and validated.

import { cli } from '@deepkit/app';

//functional
new App().command('test', (name: string) => {
    console.log('Hello', name);
});

//class
@cli.controller('test')
class TestCommand {
    async execute(name: string) {
        console.log('Hello', name);
    }
}

If you execute this command now without specifying the name parameter, an error will be issued:

$ ts-node app.ts test
RequiredArgsError: Missing 1 required arg:
name

By using --help you will get more information about the required arguments:

$ ts-node app.ts test --help
USAGE
  $ ts-node-script app.ts test NAME

Once the name is passed as an argument, the command is executed and the name is passed correctly.

$ ts-node app.ts test "beautiful world"
Hello beautiful world

Every primitive parameter type like string, number, boolean, string literals, union of them, as well as arrays of them, are automatically used as CLI arguments and are automatically validated and deserialized. The order of the parameters dictates the order of the CLI arguments. You can add as many parameters as you want.

As soon as a complex object (interface, class, object literal) is defined, it is treated as a service dependency and the Dependency Injection Container tries to resolve it. See the chapter Dependency Injection for more information.

Flags

Flags are another way to pass values to your command. Mostly these are optional, but they don't have to be. Parameters decorated with the Flag type can be passed via --name value or --name=value.

import { Flag } from '@deepkit/app';

//functional
new App().command('test', (id: number & Flag) => {
    console.log('id', name);
});

//class
class TestCommand {
    async execute(id: number & Flag) {
        console.log('id', id);
    }
}
$ ts-node app.ts test --help
USAGE
  $ ts-node app.ts test

OPTIONS
  --id=id  (required)

In the help view you can see in the "OPTIONS" that a --id flag is necessary. If you enter this flag correctly, the command will receive this value.

$ ts-node app.ts test --id 23
id 23

$ ts-node app.ts test --id=23
id 23

Boolean Flags

Flags have the advantage that they can also be used as a valueless flag, for example to activate a certain behavior. As soon as a parameter is marked as an optional Boolean, this behavior is activated.

import { Flag } from '@deepkit/app';

//functional
new App().command('test', (remove: boolean & Flag = false) => {
    console.log('delete?', remove);
});

//class
class TestCommand {
    async execute(remove: boolean & Flag = false) {
        console.log('delete?', remove);
    }
}
$ ts-node app.ts test
delete? false

$ ts-node app.ts test --remove
delete? true

Multiple Flags

To pass multiple values to the same flag, a flag can be marked as an array.

import { Flag } from '@deepkit/app';

//functional
new App().command('test', (id: number[] & Flag = []) => {
    console.log('ids', id);
});

//class
class TestCommand {
    async execute(id: number[] & Flag = []) {
        console.log('ids', id);
    }
}
$ ts-node app.ts test
ids: []

$ ts-node app.ts test --id 12
ids: [12]

$ ts-node app.ts test --id 12 --id 23
ids: [12, 23]

Single Character Flags

To allow a flag to be passed as a single character as well, Flag<{char: 'x'}> can be used.

import { Flag } from '@deepkit/app';

//functional
new App().command('test', (output: string & Flag<{char: 'o'}>) => {
    console.log('output: ', output);
});

//class
class TestCommand {
    async execute(output: string & Flag<{char: 'o'}>) {
        console.log('output: ', output);
    }
}
$ ts-node app.ts test --help
USAGE
  $ ts-node app.ts test

OPTIONS
  -o, --output=output  (required)


$ ts-node app.ts test --output test.txt
output: test.txt

$ ts-node app.ts test -o test.txt
output: test.txt

Optional / Default

The signature of the method/function defines which arguments or flags are optional. If the parameter is optional in the type system, the user does not have to provide it.

//functional
new App().command('test', (name?: string) => {
    console.log('Hello', name || 'nobody');
});

//class
class TestCommand {
    async execute(name?: string) {
        console.log('Hello', name || 'nobody');
    }
}
$ ts-node app.ts test
Hello nobody

The same for parameters with a default value:

//functional
new App().command('test', (name: string = 'body') => {
    console.log('Hello', name);
});

//class
class TestCommand {
    async execute(name: string = 'body') {
        console.log('Hello', name);
    }
}
$ ts-node app.ts test
Hello nobody

This also applies to flags in the same way.

Serialization / Validation

All arguments and flags are automatically deserialized based on its types, validated and can be provided with additional constraints.

Thus, arguments defined as numbers are always guaranteed to be real numbers in the controller, even though the command-line interface is based on text and thus strings.

//functional
new App().command('test', (id: number) => {
    console.log('id', id, typeof id);
});

//class
class TestCommand {
    async execute(id: number) {
        console.log('id', id, typeof id);
    }
}
$ ts-node app.ts test 123
id 123 number

Additional constraints can be defined with the type annotations from @deepkit/type.

import { Positive } from '@deepkit/type';
//functional
new App().command('test', (id: number & Positive) => {
    console.log('id', id, typeof id);
});

//class
class TestCommand {
    async execute(id: number & Positive) {
        console.log('id', id, typeof id);
    }
}

The type Postive in id indicates that only positive numbers are allowed. If the user now passes a negative number, the code will not be executed at all and an error message will be presented.

$ ts-node app.ts test -123
Validation error in id: Number needs to be positive [positive]

This additional validation, which is very easy to do, makes the command much more robust against wrong entries. See the chapter Validation for more information.

Description

To describe a flag or argument, use @description comment decorator.

import { Positive } from '@deepkit/type';

class TestCommand {
    async execute(
        /** @description The users identifier */
        id: number & Positive,
        /** @description Delete the user? */
        remove: boolean = false
    ) {
        console.log('id', id, typeof id);
    }
}

In the help view, this description appears after the flag or argument:

$ ts-node app.ts test --help
USAGE
  $ ts-node app.ts test ID

ARGUMENTS
  ID  The users identifier

OPTIONS
  --remove  Delete the user?