> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Module from Scratch

> Build a simple counter module from scratch in minutes

In [quickstart](/sdk/latest/tutorials/example/02-quickstart), you started a chain and submitted a transaction to increase the counter. In this tutorial, you'll build a simple counter module from scratch. It follows the same overall structure as the full `x/counter`, but uses a stripped-down version so you can focus on the core steps of building and wiring a module yourself.

By the end, you'll have built a working module and wired it into a running chain. For a deeper dive into how modules work in the Cosmos SDK, see [Intro to Modules](/sdk/latest/learn/concepts/modules).

<Note>
  Before continuing, you must follow the [Prerequisites guide](/sdk/latest/tutorials/example/01-prerequisites) to make sure everything is installed.
</Note>

## Making modules

The Cosmos SDK makes it easy to build custom business logic directly into your chain through modules. Every module follows the same overall pattern:

```text theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
proto files → code generation → keeper → msg server → query server → module.go → app wiring
```

First, you'll define what the module does:

* Define messages: users can send `Add` to increase the counter
* Define queries: users can query `Count` to read the current value
* Define genesis state: the module starts with a count of `0`

Then you'll wire that behavior into the SDK:

* Run `proto-gen` to generate the Go types and interfaces
* Implement your business logic in a `keeper` to store the count and update it
* Implement `MsgServer` and `QueryServer` to pass messages and queries into the keeper
* Register the module in `module.go`
* Wire it into the chain in `app.go`

You'll build the following module structure:

```text theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
proto/example/counter/v1/
├── tx.proto            # Transaction message and Msg service definition
├── query.proto         # Query message and Query service definition
└── genesis.proto       # Genesis state definition

x/counter/
├── keeper/
│   ├── keeper.go         # Keeper struct and state methods
│   ├── msg_server.go     # MsgServer implementation
│   └── query_server.go   # QueryServer implementation
├── types/
│   ├── keys.go           # Module name and store key constants
│   ├── codec.go          # Interface registration
│   └── *.pb.go           # Generated from proto — do not edit
├── module.go             # AppModule wiring
└── autocli.go            # CLI command definitions
```

## Step 1: Setup

This tutorial uses the `tutorial/start` branch, which is a blank template for you to create the module from scratch and wire it into `app.go`.

1. Clone the repo if you haven't already:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
git clone https://github.com/cosmos/example
cd example
```

2. Check out the `tutorial/start` branch and make the new module directories:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
git checkout tutorial/start
mkdir -p x/counter/keeper x/counter/types proto/example/counter/v1
```

You should see empty placeholder directories at `x/counter/` and `proto/example/counter/v1/`.

## Step 2: Proto files

Proto files are the source of truth for the module's public API. You define messages and services here. For a deeper look at how protobuf is used across modules, see [Encoding and Protobuf](/sdk/latest/learn/concepts/encoding#how-protobuf-is-used-in-modules).

In this tutorial, the counter module stores one number, `Add` increases it by the amount the user submits, and the query returns the current value.

First, create the three proto files:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch proto/example/counter/v1/tx.proto \
  proto/example/counter/v1/query.proto \
  proto/example/counter/v1/genesis.proto
```

Then add the following contents to each file.

### tx.proto

This is the first module file you define. It declares the transaction message shape for `Add`: what the user sends to increment the counter, and what the module returns after handling it. To learn more about how messages are defined and routed, see [Messages](/sdk/latest/learn/concepts/transactions#messages). Add the following code to `tx.proto`.

```proto theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
syntax = "proto3";

// Matches the module's protobuf namespace.
package example.counter;

// Provides Cosmos SDK message annotations like signer and service markers.
import "cosmos/msg/v1/msg.proto";

// Generated Go types are written into x/counter/types.
option go_package = "github.com/cosmos/example/x/counter/types";

service Msg {
  // Marks this as a transaction service, not a normal gRPC service.
  option (cosmos.msg.v1.service) = true;
  // Add is the one transaction this minimal module supports.
  rpc Add(MsgAddRequest) returns (MsgAddResponse);
}

message MsgAddRequest {
  // The sender signs this message.
  option (cosmos.msg.v1.signer) = "sender";
  string sender = 1;
  uint64 add    = 2;
}

message MsgAddResponse {
  // Return the new counter value after the add succeeds.
  uint64 updated_count = 1;
}
```

### query.proto

This file defines the read-only gRPC query service and the response type for fetching the current count. To learn more about how queries differ from transactions, see [Queries](/sdk/latest/learn/concepts/transactions#queries). Add the following code to `query.proto`.

```proto theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
syntax = "proto3";

// Matches the module's protobuf namespace.
package example.counter;

// Enables the REST gateway route annotation below.
import "google/api/annotations.proto";

// Generated Go types are written into x/counter/types.
option go_package = "github.com/cosmos/example/x/counter/types";

service Query {
  rpc Count(QueryCountRequest) returns (QueryCountResponse) {
    // Exposes this query over the HTTP API as well as gRPC.
    option (google.api.http).get = "/example/counter/v1/count";
  }
}

// Empty because this query only needs the module's current state.
message QueryCountRequest  {}

message QueryCountResponse {
  // The current counter value.
  uint64 count = 1;
}
```

### genesis.proto

This file defines the data the module stores in genesis so the counter can be initialized when the chain starts. Add the following code to `genesis.proto`.

```proto theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
syntax = "proto3";

// Matches the module's protobuf namespace.
package example.counter;

// Generated Go types are written into x/counter/types.
option go_package = "github.com/cosmos/example/x/counter/types";

message GenesisState {
  // The counter value to load when the chain initializes.
  uint64 count = 1;
}
```

## Step 3: Generate Code

1. Make sure Docker is running.

2. The first time you run proto-gen you need to build the builder image. Run the following commands:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
make proto-image-build
make proto-gen
```

This compiles the proto files using [buf](https://buf.build) inside Docker to produce the Go interfaces you will then implement.

The generated files will appear in `x/counter/types/`:

```text theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
x/counter/types/
├── tx.pb.go         # MsgAddRequest, MsgAddResponse, MsgServer interface
├── query.pb.go      # QueryCountRequest, QueryCountResponse, QueryServer interface
├── query.pb.gw.go   # REST gateway registration
└── genesis.pb.go    # GenesisState
```

> **Do not edit generated files.** Changes to public types belong in the proto files. Re-run `make proto-gen` after any proto change.

The most important generated output is the `MsgServer` and `QueryServer` interfaces. In Steps 5 and 6, you'll implement them in `keeper/msg_server.go` and `keeper/query_server.go`.

## Step 4: Types

Next, you'll define the module types and identifiers in `x/counter/types` that the rest of the module depends on.

Create the two files for this section:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/types/keys.go \
  x/counter/types/codec.go
```

Then add the following contents to each file.

### keys.go

This file defines the module's basic identifiers: the module name used throughout the SDK, and the store key used to claim the module's KV store namespace. For more on how modules access state through store keys, see [How modules access state](/sdk/latest/learn/concepts/store#how-modules-access-state).

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/types/keys.go
package types

const (
    // ModuleName is the name the SDK uses to refer to this module.
    ModuleName = "counter"
    // StoreKey is the key for this module's KV store.
    StoreKey   = ModuleName
)
```

`ModuleName` identifies the module throughout the SDK (routing, events, governance). `StoreKey` is the key used to claim the module's isolated namespace in the chain's KV store (set equal to `ModuleName` by convention).

### Interface Registration

This file registers your generated message types with the SDK interface registry so the application can decode and route your module's transactions correctly.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/types/codec.go
package types

import (
    codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    sdk        "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/msgservice"
)

func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
    // Register MsgAddRequest as an sdk.Msg so the app can decode it from transactions.
    registry.RegisterImplementations((*sdk.Msg)(nil),
        &MsgAddRequest{},
    )
    // Register the generated Msg service description for routing.
    msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}
```

`_Msg_serviceDesc` is generated by `make proto-gen` — it describes the `Msg` gRPC service defined in `tx.proto`.

## Step 5: Keeper

In this step, you create the keeper, which is the part of the module that owns the counter state and provides the methods the rest of the module will call. For a conceptual overview of the keeper's role, see [Keeper](/sdk/latest/learn/concepts/modules#keeper).

Create the keeper file:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/keeper/keeper.go
```

Then add the following contents.

This file defines the keeper struct, sets up the counter's storage item, and implements the core state methods for reading, updating, and loading the counter at genesis.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/keeper/keeper.go
package keeper

import (
    "context"
    "errors"

    "cosmossdk.io/collections"
    "cosmossdk.io/core/store"
    "github.com/cosmos/cosmos-sdk/codec"
    "github.com/cosmos/example/x/counter/types"
)

type Keeper struct {
    Schema  collections.Schema
    counter collections.Item[uint64]
}

func NewKeeper(storeService store.KVStoreService, cdc codec.Codec) *Keeper {
    sb := collections.NewSchemaBuilder(storeService)
    k := Keeper{
        // Store the counter under prefix 0 in this module's KV store.
        counter: collections.NewItem(sb, collections.NewPrefix(0), "counter", collections.Uint64Value),
    }
    schema, err := sb.Build()
    if err != nil {
        panic(err)
    }
    k.Schema = schema
    return &k
}

func (k *Keeper) GetCount(ctx context.Context) (uint64, error) {
    count, err := k.counter.Get(ctx)
    // Treat missing state as zero so a fresh chain starts cleanly.
    if err != nil && !errors.Is(err, collections.ErrNotFound) {
        return 0, err
    }
    return count, nil
}

func (k *Keeper) AddCount(ctx context.Context, amount uint64) (uint64, error) {
    count, err := k.GetCount(ctx)
    if err != nil {
        return 0, err
    }
    // Increment the current count and write it back to state.
    newCount := count + amount
    return newCount, k.counter.Set(ctx, newCount)
}

func (k *Keeper) InitGenesis(ctx context.Context, gs *types.GenesisState) error {
    return k.counter.Set(ctx, gs.Count)
}

func (k *Keeper) ExportGenesis(ctx context.Context) (*types.GenesisState, error) {
    count, err := k.GetCount(ctx)
    if err != nil {
        return nil, err
    }
    return &types.GenesisState{Count: count}, nil
}
```

`collections.Item[uint64]` is a typed KV store entry; the `collections` package handles encoding and namespacing. `GetCount` treats `ErrNotFound` as zero so the counter starts at zero without explicit initialization.

> **State layout**
>
> * `StoreKey` (`"counter"`) is the module's isolated namespace within the chain's global KV store. No other module can read or write this namespace.
> * `collections.NewPrefix(0)` is a single-byte prefix that identifies the `counter` item within the module's namespace. A module with multiple items would use `NewPrefix(0)`, `NewPrefix(1)`, etc. to keep them separate.
> * `ErrNotFound` treated as zero means the keeper never needs to explicitly set an initial value — the first `GetCount` call on a fresh chain returns `0` by convention.

## Step 6: MsgServer

In this step, you implement the transaction handler for the generated `MsgServer` interface. This is the code path that runs when a user submits `tx counter add`. For a conceptual overview of message execution, see [Message execution](/sdk/latest/learn/concepts/modules#message-execution-msgserver).

Create the message server file:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/keeper/msg_server.go
```

Then add the following contents.

This file implements the generated `MsgServer` interface and forwards the `Add` transaction to the keeper's `AddCount` method.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/keeper/msg_server.go
package keeper

import (
    "context"

    "github.com/cosmos/example/x/counter/types"
)

type msgServer struct {
    *Keeper
}

func NewMsgServerImpl(k *Keeper) types.MsgServer {
    return &msgServer{k}
}

func (m msgServer) Add(ctx context.Context, req *types.MsgAddRequest) (*types.MsgAddResponse, error) {
    // Delegate the state update to the keeper.
    newCount, err := m.AddCount(ctx, req.GetAdd())
    if err != nil {
        return nil, err
    }
    // Return the updated count back to the caller.
    return &types.MsgAddResponse{UpdatedCount: newCount}, nil
}
```

`msgServer` embeds `*Keeper` and delegates directly to `AddCount`. The handler itself contains no business logic.

## Step 7: QueryServer

In this step, you implement the read-only query handler for the generated `QueryServer` interface. This is the code path that runs when someone queries the current counter value. For more on how modules expose queries, see [Queries](/sdk/latest/learn/concepts/modules#queries).

Create the query server file:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/keeper/query_server.go
```

Then add the following contents.

This file implements the generated `QueryServer` interface and returns the current counter value from the keeper.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/keeper/query_server.go
package keeper

import (
    "context"

    "github.com/cosmos/example/x/counter/types"
)

type queryServer struct {
    *Keeper
}

func NewQueryServer(k *Keeper) types.QueryServer {
    return &queryServer{k}
}

func (q queryServer) Count(ctx context.Context, _ *types.QueryCountRequest) (*types.QueryCountResponse, error) {
    // Read the current count from state and return it in the query response.
    count, err := q.GetCount(ctx)
    if err != nil {
        return nil, err
    }
    return &types.QueryCountResponse{Count: count}, nil
}
```

## Step 8: module.go

In this step, you connect your keeper and generated services to the Cosmos SDK module framework so the application knows how to initialize the module, expose its query routes, and register its transaction handlers.

Create the module file:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/module.go
```

Then add the following contents.

This file defines the app module types and wires your keeper into genesis handling, service registration, and gRPC gateway registration.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/module.go
package counter

import (
    "context"
    "encoding/json"

    "cosmossdk.io/core/appmodule"
    "github.com/cosmos/cosmos-sdk/client"
    "github.com/cosmos/cosmos-sdk/codec"
    codecTypes "github.com/cosmos/cosmos-sdk/codec/types"
    sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/module"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"

    "github.com/cosmos/example/x/counter/keeper"
    countertypes "github.com/cosmos/example/x/counter/types"
)

var (
    // Compile-time checks that AppModule implements the required module interfaces.
    _ appmodule.AppModule        = AppModule{}
    _ module.HasConsensusVersion = AppModule{}
    _ module.HasGenesis          = AppModule{}
    _ module.HasServices         = AppModule{}
)

type AppModuleBasic struct {
    cdc codec.Codec
}

func (a AppModuleBasic) Name() string { return countertypes.ModuleName }

func (a AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {}

func (a AppModuleBasic) RegisterInterfaces(registry codecTypes.InterfaceRegistry) {
    countertypes.RegisterInterfaces(registry)
}

func (a AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
    // Start the module with a zero counter by default.
    return cdc.MustMarshalJSON(&countertypes.GenesisState{Count: 0})
}

func (a AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
    gs := countertypes.GenesisState{}
    return cdc.UnmarshalJSON(bz, &gs)
}

func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
    // Expose the Query service through the HTTP gateway.
    if err := countertypes.RegisterQueryHandlerClient(context.Background(), mux, countertypes.NewQueryClient(clientCtx)); err != nil {
        panic(err)
    }
}

type AppModule struct {
    AppModuleBasic
    keeper *keeper.Keeper
}

func NewAppModule(cdc codec.Codec, k *keeper.Keeper) AppModule {
    return AppModule{AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: k}
}

func (a AppModule) IsOnePerModuleType() {}
func (a AppModule) IsAppModule()        {}

func (a AppModule) ConsensusVersion() uint64 { return 1 }

func (a AppModule) RegisterServices(cfg module.Configurator) {
    // Connect the generated service interfaces to your keeper-backed implementations.
    countertypes.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(a.keeper))
    countertypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(a.keeper))
}

func (a AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, bz json.RawMessage) {
    gs := &countertypes.GenesisState{}
    cdc.MustUnmarshalJSON(bz, gs)
    // Load the initial counter value into state at chain start.
    if err := a.keeper.InitGenesis(ctx, gs); err != nil {
        panic(err)
    }
}

func (a AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
    gs, err := a.keeper.ExportGenesis(ctx)
    if err != nil {
        panic(err)
    }
    // Write the current counter value back out for exports.
    return cdc.MustMarshalJSON(gs)
}
```

The `var _ interface = Struct{}` block at the top is a Go compile-time check — if the struct is missing any required method, the build fails immediately.

`RegisterServices` is the most important method. It connects the generated server interfaces to your implementations, making them reachable from the SDK's message and query routers.

## Step 9: AutoCLI

In this step, you define the CLI metadata for your module. AutoCLI reads this configuration together with your proto services and generates the `exampled query counter` and `exampled tx counter` commands automatically.

Create the AutoCLI file:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
touch x/counter/autocli.go
```

Then add the following contents.

This file tells `AutoCLI` how to expose the `Count` query and `Add` transaction as simple command-line commands.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// x/counter/autocli.go
package counter

import (
    autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
)

func (a AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
    return &autocliv1.ModuleOptions{
        Query: &autocliv1.ServiceCommandDescriptor{
            Service: "example.counter.Query",
            RpcCommandOptions: []*autocliv1.RpcCommandOptions{
                // exampled query counter count
                {RpcMethod: "Count", Use: "count", Short: "Query the current counter value"},
            },
        },
        Tx: &autocliv1.ServiceCommandDescriptor{
            Service: "example.counter.Msg",
            RpcCommandOptions: []*autocliv1.RpcCommandOptions{
                // exampled tx counter add 4 --from alice
                {RpcMethod: "Add", Use: "add [amount]", Short: "Add to the counter",
                    PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "add"}}},
            },
        },
    }
}
```

`PositionalArgs` maps the first CLI argument to the `add` field in `MsgAddRequest`, so `add 4` works instead of `add --add 4`.

## Step 10: Wire into app.go

In this step, you wire your new module into the application so the chain creates its store, constructs its keeper, and includes it in module startup and genesis handling. For a full explanation of what `app.go` does and why the wiring order matters, see [app.go Overview](/sdk/latest/learn/concepts/app-go).

Open `app.go` and find each marker comment. Paste the code directly below it.

### 1. Imports

Add the counter module, keeper, and shared types imports to `app.go`.

Find the comment in `app.go` and add the code directly below it.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 1: add counter imports below
	counter       "github.com/cosmos/example/x/counter"
	counterkeeper "github.com/cosmos/example/x/counter/keeper"
	countertypes  "github.com/cosmos/example/x/counter/types"
```

### 2. Keeper Field

Store the counter keeper on `ExampleApp` so the rest of the app can reference it.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 2: add the counter keeper field below
CounterKeeper         *counterkeeper.Keeper
```

### 3. Store Key

Give the counter module its own KV store namespace.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 3: add the counter store key below
countertypes.StoreKey,
```

### 4. Keeper Instantiation

Construct the counter keeper using the module store and app codec.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 4: create the counter keeper below
app.CounterKeeper = counterkeeper.NewKeeper(
	runtime.NewKVStoreService(keys[countertypes.StoreKey]),
	appCodec,
)
```

### 5. Module Manager

Register the counter module with the app's module manager.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 5: register the counter module below
counter.NewAppModule(appCodec, app.CounterKeeper),
```

### 6. Genesis Order

Include the counter module when the app initializes state from genesis.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 6: add the counter module to genesis order below
countertypes.ModuleName,
```

### 7. Export Order

Include the counter module when the app exports state back out to genesis.

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// counter tutorial app wiring 7: add the counter module to export order below
countertypes.ModuleName,
```

## Step 11: Build

Run the following to compile the app and make sure the new module wiring is valid before you try to run the chain.

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
go build ./...
```

Fix any compilation errors before continuing.

## Step 12: Test your module

Now you'll run the app locally and use one transaction plus one query to confirm the module works end-to-end.

### Start the chain

First, install the binary and start the demo chain.

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
make install
make start
```

This builds and installs `exampled` and then runs `scripts/local_node.sh`, which:

* resets the local chain data
* initializes genesis
* creates and funds the `alice` and `bob` test accounts
* creates a validator transaction
* starts the chain

You'll see the chain running and it should start producing blocks.

### Submit a transaction

Open a second terminal and submit a transaction that adds `4` to the counter:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
exampled tx counter add 4 --from alice --chain-id demo --yes
```

If the transaction succeeds, the response should include `code: 0`, which means the chain accepted and executed the transaction without an application error:

```
code: 0
```

### Query the chain

Query the counter to confirm the stored value changed using the query command that `AutoCLI` generated earlier:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
exampled query counter count
```

You should see the following output:

```text theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
count: "4"
```

Congratulations, you've just created a Cosmos module from scratch and wired it into a real chain!

If you are planning to build a production module, see [Module Design Considerations](/sdk/latest/guides/module-design/module-design-considerations) for guidance on state structure, message surface, dependencies, and upgrade planning before you ship.

## Next steps

The simple counter module you built here follows the same structure as the full `x/counter` example in the `main` branch. Next, you'll see how the full module extends that foundation with features like params, fee collection, tests, and more.

Next: [Full Counter Module Walkthrough →](/sdk/latest/tutorials/example/04-counter-walkthrough)
