Create posts with HTTP

Let’s create a front-end for our blog application. In this guide we will be writing a client-side application in JavaScript that can create a wallet (public/private key pair), fetch a list of posts from our server, create posts and send to our server.

x/blog/client/rest/rest.go

import (
  // Existing imports...
  "github.com/example/blog/x/blog/types"
)

We’ll be creating posts by sending POST requests to the same endpoint: /blog/posts. To add a handler add the following line to func RegisterRoutes:

    r.HandleFunc("/blog/posts", createPostHandler(cliCtx)).Methods("POST")

Now let’s create createPostHandler.

x/blog/client/rest/tx.go

We will need a createPostReq type that represents the request that we will be sending from the client:

package rest

import (
    "net/http"

    "github.com/cosmos/cosmos-sdk/client/context"
    sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/rest"
    "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
    "github.com/example/blog/x/blog/types"
)

type createPostReq struct {
    BaseReq rest.BaseReq `json:"base_req"`
    Creator string       `json:"creator"`
    Title   string       `json:"title"`
}

createPostHandler first parses request parameters, performs basic validations, converts Creator field from string into SDK account address type, creates MsgCreatePost message.

func createPostHandler(cliCtx context.CLIContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req createPostReq
        if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
            rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
            return
        }
        baseReq := req.BaseReq.Sanitize()
        if !baseReq.ValidateBasic(w) {
            return
        }
        addr, err := sdk.AccAddressFromBech32(req.Creator)
        if err != nil {
            rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
            return
        }
        msg := types.NewMsgCreatePost(addr, req.Title)
        err = msg.ValidateBasic()
        if err != nil {
            rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
            return
        }
        utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
    }
}

Setting up the client-side project

Inside an empty directory create package.json:

npm init -y

Add dependencies:

npm add parcel-bundler local-cors-proxy axios @tendermint/sig

We’ll be using parcel-bundler for bundling our dependencies and development server, local-cors-proxy for providing a CORS proxy server as a development replacement for something like Nginx, axios for HTTP requests and @tendermint/sig for interacting with our application.

Replace "scripts" property in package.json with the following:

"scripts": {
  "preserve": "lcp --proxyUrl <http://localhost:1317> &",
  "serve": "parcel index.html",
  "postserve": "pkill -f lcp"
}