List posts

To list created posts we will be using blogcli query blog list-posts command. list-posts subcommand hasn’t been defined yet, so let’s do it now. Query commands from the CLI are handled by query.go.

x/blog/client/cli/query.go

import (
  // Existing imports...
  "github.com/cosmos/cosmos-sdk/client/context"
)

Function GetQueryCmd is used for creating a list of query subcommands, it should already be defined. Edit the function to add GetCmdListPosts as a subcommand:

  blogQueryCmd.AddCommand(
    flags.GetCommands(
      GetCmdListPosts(queryRoute, cdc),
    )...,
  )

Now let’s define GetCmdListPosts:

func GetCmdListPosts(queryRoute string, cdc *codec.Codec) *cobra.Command {
  return &cobra.Command{
    Use:   "list-posts",
    Short: "list all posts",
    RunE: func(cmd *cobra.Command, args []string) error {
      cliCtx := context.NewCLIContext().WithCodec(cdc)
      res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/"+types.QueryListPosts, queryRoute), nil)
      if err != nil {
        fmt.Printf("could not list posts\\n%s\\n", err.Error())
        return nil
      }
      var out []types.Post
      cdc.MustUnmarshalJSON(res, &out)
      return cliCtx.PrintOutput(out)
    },
  }
}

GetCmdListPosts runs an ABCI query to fetch the data, unmarshals it back form binary to JSON and returns it to the console. ABCI is an interface between your app and Tendermint (a program responsible for replicating the state across machines). ABCI queries look like paths on a hierarchical filesystem. In our case, the query is custom/blog/list-posts. Before we continue, we need to define QueryListPosts.

x/blog/types/querier.go

const (
  QueryListPosts = "list-posts"
)

x/blog/keeper/querier.go

import (
  // Existing imports ...
  "github.com/example/blog/x/blog/types"
  "github.com/cosmos/cosmos-sdk/codec"
)

NewQuerier acts as a dispatcher for query functions, it should already be defined. Modify the switch statement to include listPosts:

    switch path[0] {
    case types.QueryListPosts:
      return listPosts(ctx, k)
    default:

Now let’s define listPosts:

func listPosts(ctx sdk.Context, k Keeper) ([]byte, error) {
  var postList []types.Post
  store := ctx.KVStore(k.storeKey)
  iterator := sdk.KVStorePrefixIterator(store, []byte(types.PostPrefix))
  for ; iterator.Valid(); iterator.Next() {
    var post types.Post
    k.cdc.MustUnmarshalBinaryLengthPrefixed(store.Get(iterator.Key()), &post)
    postList = append(postList, post)
  }
  res := codec.MustMarshalJSONIndent(k.cdc, postList)
  return res, nil
}

This function uses an prefix iterator to loop through all the keys with a given prefix (in our case PostPrefix is "post-"). We’re getting values by key with store.Get and appending them to postList. Finally, we unmarshal bytes back to JSON and return the result to the console.

Now let’s see how it works. Run the following command to recompile your app, clear the data and relaunch the chain:

starport serve