Commands

Finch supports custom CLI commands that you can invoke when running your app from the terminal. Commands let you perform maintenance tasks (e.g., database seeding, cache clearing, data export) without writing a separate script. All commands share the same app instance, so they have access to the database, config, and all app state.

Commands are powered by the Capp package, which handles argument parsing, option flags, and formatted console output.

Defining a Command

Use app.commands.add(CappController(...)) in your app.dart to register a command. Each command has:

  • A name (the verb you type on the command line)
  • Options (optional flags like --test or -t)
  • A run callback that executes when the command is called
app.commands.add(
  CappController(
    'example',
    options: [
      CappOption(
        name: 'test',
        shortName: 't',
        description: 'An example option to demonstrate the command system',
      ),
    ],
    run: (c) async {
      // Check whether the --test flag was passed
      if (c.existsOption('test')) {
        // Print a formatted table to the terminal
        CappConsole.writeTable(
          [
            ['Column 1', 'Column 2', 'Column 3'],
            ...List.filled(5, ['Data 1', 'Data 2', 'Data 3']),
          ],
          dubleBorder: true,
          color: CappColors.warning,
        );
      }

      // The return value is printed as the final line of output
      return CappConsole(
        'Example command completed at ${DateTime.now()}',
        CappColors.success,
      );
    },
  ),
);

Running a Command

Commands are run by passing the command name as an argument after the app entrypoint:

# Run the 'example' command
dart run lib/app.dart example

# Run with the --test option
dart run lib/app.dart example --test

# Short form: -t
dart run lib/app.dart example -t

The command arguments are passed through main([List<String>? args]) to app.start(args). Without this, custom commands won't work.

// app.dart
void main([List<String>? args]) async {
  // ...
  await app.start(args);
}

How Finch Handles Arguments

When app.start(args) receives a known command name as the first argument, it runs that command instead of starting the HTTP server. The process exits when the command finishes.

If no arguments are passed (or the first argument is not a known command), the HTTP server starts normally.

CappConsole Output Helpers

CappConsole provides formatted terminal output:

// Colored text
return CappConsole('Done!', CappColors.success);   // green
return CappConsole('Warning', CappColors.warning); // yellow
return CappConsole('Error', CappColors.error);     // red

// Table output
CappConsole.writeTable(
  [
    ['Name', 'Email', 'Role'],          // header row
    ['Alice', '[email protected]', 'admin'],
    ['Bob',   '[email protected]',   'user'],
  ],
  dubleBorder: true,
  color: CappColors.info,
);

Reading Options in a Command

The run callback receives a CappController context c. Use c.existsOption(name) to check for flags and c.getOption(name) to read option values:

run: (c) async {
  // Boolean flag
  if (c.existsOption('verbose')) {
    // ...
  }

  // Option with a value: --name=Alice or --name Alice
  var name = c.getOption('name') ?? 'World';
  return CappConsole('Hello, $name!', CappColors.success);
}

Built-in Commands

Finch registers several built-in commands automatically:

Command Description
migrate Run database migrations (see Database Migration)
build Build and compile the app
routes List all registered routes

Adding Custom Commands

You can add custom commands to your Finch application. These commands can be used to perform various tasks such as database migration, language management, etc. To add a custom command, you need to use the addCommand method. Here is an example of how to use it:

app.commands.add(
  CappController('example', options: [
    CappOption(
      name: 'test',
      shortName: 't',
      description: 'An example option',
    ),
  ], run: (c) async {
    if (c.existsOption('test')) {
      CappConsole.writeTable(
        [
          ['Column 1', 'Column 2', 'Column 3'],
          ...List.filled(5, ['Data 1', 'Data 2', 'Data 3'])
        ],
        dubleBorder: true,
        color: CappColors.warning,
      );
    }

    return CappConsole(
      'This is an example command from Finch App! Time: ${DateTime.now()}',
      CappColors.success,
    );
  }),
);

Note:

Capp is a package that provides a simple way to manage commands in your application. you can use this package to add commands to your application. for more information please refer to the Capp package.

Run a command after start the server

You can run a command after start the server. for example you can run a database migration command after start the server. to do this you need to use the runCommand method. here is an example of how to use it:

dart run example/lib/app.dart example --test

in this example example is the name of the command and --test is the option that we want to pass to the command.

Note: you have to pass the arguments of main function to the runCommand method. for example if you have a --port option in your main function you have to pass this option to the runCommand method. for example:

/// app.dart

final app = FinchApp(configs: configs);

void main([List<String>? args]) async {
   ...
   app.start(args);
}