Custom CLI Commands (flask CLI)

A custom CLI command lets you run app tasks — seeding data, creating an admin, clearing a cache — from the terminal with flask <name> , with full access to your app.

Learn Custom CLI Commands (flask CLI) in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll build Click commands with arguments and options, run them in Python, register them on a Flask app with access to its config, and meet flask db .

Flask's CLI is powered by Click . A command is just a function decorated with @click.command() . You add arguments (required, positional) with @click.argument and options (optional, named, with defaults) with @click.option . Click handles parsing and help text for you.

The runnable example below defines a hello command and invokes it with Click's test runner — no real terminal needed. Notice how the option's default applies when you don't pass --greeting .

hello Ada uses the default greeting; hello Bob --greeting Hi overrides it. The argument is required, the option is not.

A classic use is a seed command that fills your database with sample data for development. Options can declare a type so Click converts and validates the input — passing --count 5 arrives as the integer 5 , and a non-number is rejected with a helpful error.

The runnable example seeds an in-memory list. Run it twice with different counts to see the typed option in action.

The big payoff of @app.cli.command is that Flask runs your command inside an application context . That means current_app , its config, and app-bound extensions (like the database) are available automatically — exactly as they are in a request.

The runnable example registers a create-admin command that reads current_app.config , and invokes it with Flask's own CLI test runner (which pushes the app context for you).

Complete the command below. Replace each ___ so it takes a required word and an optional --times .

The CLI doesn't know where your app is. Set FLASK_APP=app.py (or use an app factory in create_app ) so flask <command> can import it.

You accessed current_app or db in a plain @click.command not registered on the app. Register it with @app.cli.command so Flask pushes the context for you.

Build a command that reports user counts, with a flag to filter.

Lesson complete — your app has its own toolbox!

You built Click commands with arguments, typed options, and flags, registered them on a Flask app with @app.cli.command , used current_app.config inside a command, and met flask db .

🚀 Up next: Signals (Blinker) — decouple parts of your app with publish/subscribe events.

Practice quiz

Which library powers the flask CLI under the hood?

  • argparse
  • Click
  • Typer
  • docopt

Answer: Click. Flask's CLI is built on Click.

How do you register a custom command on a Flask app?

  • @app.command('name')
  • @click.flask('name')
  • @app.cli.command('name')
  • @app.register('name')

Answer: @app.cli.command('name'). Decorate with @app.cli.command('name') to register a command.

Which Click decorator declares a required positional input?

  • @click.argument('x')
  • @click.option('--x')
  • @click.flag('x')
  • @click.input('x')

Answer: @click.argument('x'). @click.argument('x') is a required positional argument.

Which Click decorator declares an optional named input?

  • @click.argument('--x')
  • @click.required('x')
  • @click.positional('x')
  • @click.option('--x', default=...)

Answer: @click.option('--x', default=...). @click.option('--x', default=...) is an optional named input.

Why can a @app.cli command use current_app and db directly?

  • Flask runs it inside an application context
  • Click imports them globally
  • It runs in a request context
  • Flask disables contexts for the CLI

Answer: Flask runs it inside an application context. Flask pushes an application context before running an @app.cli command.

Which call prints output from a Click command?

  • print()
  • click.echo('...')
  • sys.stdout('...')
  • click.print('...')

Answer: click.echo('...'). click.echo() prints output from a command.

How do you make an option a boolean flag?

  • @click.option('--v', bool=True)
  • @click.flag('--v')
  • @click.option('--v', is_flag=True)
  • @click.bool('--v')

Answer: @click.option('--v', is_flag=True). @click.option('--v', is_flag=True) makes a boolean flag.

Which command group is added by the Flask-Migrate extension?

  • flask run
  • flask shell
  • flask routes
  • flask db

Answer: flask db. flask db comes from Flask-Migrate (built on Alembic).

What error appears if Flask can't find your app for the CLI?

  • Could not locate a Flask application
  • 404 Not Found
  • Working outside of request context
  • Module not found: click

Answer: Could not locate a Flask application. Set FLASK_APP (or use a create_app factory) so the CLI can import the app.

What does @click.option('--count', type=int) do with '--count 5'?

  • Keeps it the string '5'
  • Raises an error
  • Converts it to the integer 5
  • Ignores the value

Answer: Converts it to the integer 5. Click converts the input to the declared type, so it becomes int 5.