# One handler, several commands

One handler class can answer several slash commands and branch on the route that ran. Covers subcommands, the options every route shares, keeping both lists on the same routes, and the arm every route needs.

Two commands that share most of their work become two handler classes, plus a helper you import into both. Instead, just handle both in a single handler class. The decorator and the generic both take the list.

```ts title="src/handlers/Moderation.ts"
import { SlashHandler, SlashRoute } from '@seedcord/gateway';

@SlashRoute('ban', 'kick')
export class Moderation extends SlashHandler<'ban' | 'kick'> {
    public async execute(): Promise<void> {
        await this.match({
            ban: async (options) => {
                const reason = options.getString('reason');
                await this.reply(`Banned. ${reason ?? ''}`);
            },
            kick: async () => {
                await this.reply('Kicked.');
            }
        });
    }
}
```

Each arm receives that route's own options, so `reason` is there in the `ban` arm even though `kick` never declares it.

## Subcommands work the same way

A route with a slash in it goes in the list like any other. One handler can answer every leaf of a command.

```ts title="src/handlers/Role.ts"
@SlashRoute('role/add', 'role/remove')
export class Role extends SlashHandler<'role/add' | 'role/remove'> {
    public async execute(): Promise<void> {
        await this.match({
            'role/add': (options) =>
                this.reply(`Gave ${options.getRole('role').name}.`),
            'role/remove': (options) =>
                this.reply(`Took ${options.getRole('role').name}.`)
        });
    }
}
```

## Options every route shares

`this.options` on the class holds the options every route in the list declares. `ban` and `kick` both take `target`, so it's there.

```ts
const target = this.options.getUser('target');
```

An option only one route declares stays out.

```ts
@SlashRoute('ban', 'kick')
class Moderation extends SlashHandler<'ban' | 'kick'> {
    public async execute(): Promise<void> {
        const reason = this.options.getString('reason');
    }
}
```

`reason` belongs to `ban` alone, so you read it inside that arm.

## Both lists must carry the same routes

Adding a route to one list and forgetting the other is a compile error on the decorator line. TypeScript opens that error on a generic line about the decorator, and the sentence you want sits a couple of lines further in:

```txt output
SlashHandler declares a route that the SlashRoute decorator does not list
```

Most seedcord type errors carry a plain sentence like that one somewhere in the type noise. Read past the first line and look for it.

```ts
@SlashRoute('ban')
class Moderation extends SlashHandler<'ban' | 'kick'> {
    public async execute(): Promise<void> {}
}
```

## Every route needs an arm

Your editor lists the routes still waiting for one.

```ts
await this.match({ '
```

Leaving one out is a compile error.

```ts
@SlashRoute('ban', 'kick')
class Moderation extends SlashHandler<'ban' | 'kick'> {
    public async execute(): Promise<void> {
        await this.match({
            ban: async () => {
                await this.reply('Banned.');
            }
        });
    }
}
```
