megaphone icon Learn how to streamline incident response with Mattermost & ServiceNow on February 16th ยป
Edit on GitHub

Golang

The Golang driver is a package which wraps HTTP REST calls, making it more convenient to access the Apps API and the Mattermost REST API. Use this package if you do not want to make HTTP REST calls yourself.

Installation 

Create a new directory for your App, and then create a new Golang module go mod init:

mkdir my-app
cd my-app
go mod init github.com/MY_USERNAME/MY_APP_REPO

Install the driver using go get. The version number v1.1.0 in the command below can be updated to reference newer releases.

go get github.com/mattermost/mattermost-plugin-apps@v1.1.0

Main package 

At its core, an App is a collection of HTTP REST endpoints. The Apps framework includes an HTTP REST handler (router) implementation for convenience. The HTTP handler can be swapped out for any handler that implements the Golang http.Handler interface.

Using the provided HTTP handler, a main.go file for an App would look like the following:

package main

import (
    "net/http"
    "github.com/mattermost/mattermost-plugin-apps/utils/httputils"
)

func main() {
    // Create an instance of the endpoint handler
    handler := httputils.NewHandler()
    
    // HTTP endpoints will be defined here
    
    // Start the HTTP server using the endpoint handler
    server := http.Server{
		Addr:    "my-app-hostname:4000",
		Handler: handler,
	}
	_ = server.ListenAndServe()
}

Manifest 

The App manifest is an instance of the apps.Manifest (godoc) struct.

var (
    appManifest = apps.Manifest{
		AppID:       apps.AppID("hello-world"),
		Version:     apps.AppVersion("0.1.0"),
		HomepageURL: "https://github.com/MY_USERNAME/MY_APP_REPO",
		DisplayName: "Hello world!",
		Description: "Example Golang App for Mattermost",
		Icon:        "icon.png",
		RequestedPermissions: apps.Permissions{
			apps.PermissionActAsBot,
		},
		RequestedLocations: apps.Locations{
			apps.LocationChannelHeader,
			apps.LocationCommand,
		},
		Deploy: apps.Deploy{
			HTTP: &apps.HTTP{
				RootURL: "http://my-app-hostname:4000",
			},
		},
	}
)

A utility method, httputils.DoHandleJSON (godoc), simplifies the manifest.json handler by automating the conversion of the response data to JSON and sending the response.

func main() {
    // ...
    handler.HandleFunc("/manifest.json", httputils.DoHandleJSON(appManifest))
    // ...
}

The httputils package can be referenced by adding "github.com/mattermost/mattermost-plugin-apps/utils/httputils" to the import statement.

Bindings and forms 

App bindings and forms use the apps.Binding (godoc) and apps.Form (godoc) structs, respectively.

var (
    // A form with a single text input field and a Submit button
    appForm = apps.Form{
		Title: "I'm a form!",
		Icon:  "icon.png",
		Fields: []apps.Field{
			{
				Type:                 apps.FieldTypeText,
				Name:                 "message",
				Label:                "message",
				AutocompletePosition: 1,
			},
		},
		Submit: apps.NewCall("/submit"),
	}

    // Bind a button that shows a Form to the Channel Header
	channelHeaderBinding = apps.Binding{
		Location: apps.LocationChannelHeader,
		Bindings: []apps.Binding{
			{
				Location: "send-button",
				Icon:     "icon.png",
				Label:    "send hello message",
				Form:     &appForm,
			},
		},
	}

    // Bind a slash command using a Form for input
	commandBinding = apps.Binding{
		Location: apps.LocationCommand,
		Bindings: []apps.Binding{
			{
				Location:    "send-command",
				Label:       "send hello message",
				Description: appManifest.Description,
				Hint:        "[send]",
				Bindings: []apps.Binding{
					{
						Location: "send",
						Label:    "send",
						Form:     &appForm,
					},
				},
			},
		},
	}

    // Collect all App bindings into a slice
	bindings = []apps.Binding{
		channelHeaderBinding,
		commandBinding,
	}
)

A utility method, apps.NewDataResponse (godoc), along with apps.DoHandleJSON, simplifies the bindings handler by wrapping the bindings in an apps.CallResponse (godoc) struct, converting it to JSON, and sending the response.

func main() {
    // ...
    handler.HandleFunc("/bindings", httputils.DoHandleJSON(apps.NewDataResponse(bindings)))
    // ...
}

Call handlers 

The call handler’s request body contains a JSON-marshalled version of the apps.CallRequest (godoc) struct. The response body is an instance of the apps.CallResponse struct marshalled to JSON. Several helper functions are available to construct common call responses such as text, form, or lookup.

func submitHandler(w http.ResponseWriter, r *http.Request) {
	// Unmarshal the request body into an apps.CallRequest struct
	callRequest := new(apps.CallRequest)
	err := json.NewDecoder(r.Body).Decode(callRequest)
	if err != nil {
		// handle the error
		return
	}

	// Construct the response message using the input form's `message` value
	message := "Hello, world!"
	submittedMessage, ok := callRequest.Values["message"].(string)
	if ok {
		message += " ...and " + submittedMessage + "!"
	}

	// Create an instance of the API Client
	botClient := appclient.AsBot(callRequest.Context)

	// Post a DM
	channel, _, err := botClient.CreateDirectChannel(
	    callRequest.Context.BotUserID,
	    callRequest.Context.ActingUser.Id,
	)
	if err != nil {
		// handle the error
		return
	}
	post := &model.Post{
		ChannelId: channel.Id,
		Message:   message,
	}
	_, err = botClient.CreatePost(post)
	if err != nil {
		// handle the error
		return
	}

	// Construct the call response and send it
	callResponse := apps.NewTextResponse("Created a post in your DM channel.")
	err = httputils.WriteJSON(w, callResponse)
	if err != nil {
		// handle the error
		return
	}
}

The call handler is registered with the HTTP router in a similar way to the bindings and manifest endpoints.

func main() {
    // ...
    handler.HandleFunc("/submit", submitHandler)
    // ...
}

Static assets 

Apps usually contain a number of static assets such as images that are collected in a single location, such as a disk or compiled into the App binary. Golang provides a helper function, http.FileServer (godoc), which creates a static asset handler suitable for serving assets from a disk. In the example below, a handler for static assets in the static directory is registered for all endpoint paths beginning with /static/.

func main() {
    // ...
    handler.
        PathPrefix("/static/").
        Handler(http.FileServer(http.Dir("static")))
    // ...
}

Example 

Full example code
package main

import (
	"encoding/json"
	"io"
	"net/http"

	"github.com/mattermost/mattermost-plugin-apps/apps"
	"github.com/mattermost/mattermost-plugin-apps/apps/appclient"
	"github.com/mattermost/mattermost-plugin-apps/utils/httputils"
	"github.com/mattermost/mattermost-server/v6/model"
)

var (
	// App manifest
	appManifest = apps.Manifest{
		AppID:       apps.AppID("hello-world"),
		Version:     apps.AppVersion("0.1.0"),
		HomepageURL: "https://github.com/MY_USERNAME/MY_APP_REPO",
		DisplayName: "Hello world!",
		Description: "Example Golang App for Mattermost",
		Icon:        "icon.png",
		RequestedPermissions: apps.Permissions{
			apps.PermissionActAsBot,
		},
		RequestedLocations: apps.Locations{
			apps.LocationChannelHeader,
			apps.LocationCommand,
		},
		Deploy: apps.Deploy{
			HTTP: &apps.HTTP{
				RootURL: "http://my-app-hostname:4000",
			},
		},
	}

    // A form with a single text input field and a Submit button
    appForm = apps.Form{
		Title: "I'm a form!",
		Icon:  "icon.png",
		Fields: []apps.Field{
			{
				Type:                 apps.FieldTypeText,
				Name:                 "message",
				Label:                "message",
				AutocompletePosition: 1,
			},
		},
		Submit: apps.NewCall("/submit"),
	}

    // Bind a button that shows a Form to the Channel Header
	channelHeaderBinding = apps.Binding{
		Location: apps.LocationChannelHeader,
		Bindings: []apps.Binding{
			{
				Location: "send-button",
				Icon:     "icon.png",
				Label:    "send hello message",
				Form:     &appForm,
			},
		},
	}

    // Bind a slash command using a Form for input
	commandBinding = apps.Binding{
		Location: apps.LocationCommand,
		Bindings: []apps.Binding{
			{
				Location:    "send-command",
				Label:       "send hello message",
				Description: appManifest.Description,
				Hint:        "[send]",
				Bindings: []apps.Binding{
					{
						Location: "send",
						Label:    "send",
						Form:     &appForm,
					},
				},
			},
		},
	}

    // Collect all App bindings into a slice
	bindings = []apps.Binding{
		channelHeaderBinding,
		commandBinding,
	}
)

// Handlers

func submitHandler(w http.ResponseWriter, r *http.Request) {
	// Unmarshal the request body into an apps.CallRequest struct
	callRequest := new(apps.CallRequest)
	err := json.NewDecoder(r.Body).Decode(callRequest)
	if err != nil {
		// handle the error
		return
	}

	// Construct the response message using the input form's `message` value
	message := "Hello, world!"
	submittedMessage, ok := callRequest.Values["message"].(string)
	if ok {
		message += " ...and " + submittedMessage + "!"
	}

	// Create an instance of the API Client
	botClient := appclient.AsBot(callRequest.Context)

	// Post a DM
	channel, _, err := botClient.CreateDirectChannel(
	    callRequest.Context.BotUserID,
	    callRequest.Context.ActingUser.Id,
	)
	if err != nil {
		// handle the error
		return
	}
	post := &model.Post{
		ChannelId: channel.Id,
		Message:   message,
	}
	_, err = botClient.CreatePost(post)
	if err != nil {
		// handle the error
		return
	}

	// Construct the call response and send it
	callResponse := apps.NewTextResponse("Created a post in your DM channel.")
	err = httputils.WriteJSON(w, callResponse)
	if err != nil {
		// handle the error
		return
	}
}

func main() {
	// Create an instance of the endpoint handler
	handler := httputils.NewHandler()

	// Manifest
	handler.HandleFunc("/manifest.json", httputils.DoHandleJSON(appManifest))

	// Bindings and forms
	handler.HandleFunc("/bindings", httputils.DoHandleJSON(apps.NewDataResponse(bindings)))

	// Handlers
	handler.HandleFunc("/submit", submitHandler)

	// Static assets
	handler.
		PathPrefix("/static/").
		Handler(http.FileServer(http.Dir("static")))

	// Start the HTTP server using the endpoint handler
	server := http.Server{
		Addr:    "my-app-hostname:4000",
		Handler: handler,
	}
	_ = server.ListenAndServe()
}

Function reference 

Call response helpers 

The following call response helpers are part of the apps.CallResponse type (godoc):

Function name Description
NewErrorResponse Creates a new apps.CallResponse struct that wraps an error variable.
NewDataResponse Creates a new apps.CallResponse struct using the supplied object as the value of the data field.
NewTextResponse Creates a new apps.CallResponse struct using the supplied string as the value of the text field.
NewFormResponse Creates a new apps.CallResponse struct using the supplied apps.Form struct as the value of the form field.
NewLookupResponse Creates a new apps.CallResponse struct using the supplied slice of select field options ([]apps.SelectOption godoc).

Utility functions 

The following utility functions are part of the httputils package (godoc):

Function name Description
WriteJSONStatus Marshalls the supplied object into JSON and sends the JSON data with a custom HTTP status code.
WriteJSON Marshalls the supplied object into JSON and sends the JSON data with a 200 OK HTTP status code.
DoHandleJSONData Creates a handler function that responds with JSON-encoded data in the form of a byte slice.
DoHandleData Creates a handler function that responds with the supplied byte slice and HTTP Content-Type header.
DoHandleJSON Creates a handler function that responds with the JSON-encoded version of the supplied object.

Did you find what you were looking for?

Thank you! We appreciate your feedback.
ร—

Tell us more

Your feedback helps us improve the Mattermost developer documentation.

Have a feature request? Share it here.

Having issues? Join our Community server.