How to Build a Web Service in 5 Minutes with Go


There are lots of very good Web frameworks for the Go language. In this article, you learn how to use one to create a fully-working Web service, faster than you might have imagined.

The Go Programming Language (Go) has many interesting features, with plentyof third-party packages that make use of them. Today I take you through the journey of implementing a web service for an existing API in Go using the power of one of those libraries… in five minutes.

I got your attention, right?Good.

First, let’s get a few definitions out of the way. A Web service is nothing more than a type of Web server that receives requests, processes them, and return responses. At a high level, everything works exactly the way it does when the same server is serving a webpage; but the client request is usually more complex and the response is not meant to be directly rendered in the browser (both request and response data are usually encoded in some agreed-upon format, such as JSON). This graph shows how it works.


Web Services Architecture (source: Wikipedia)

Simple, right? Another thing that becomes glaringly obvious from this explanation is that you have to learn a lot of acronyms when dealing with Web services.

This article assumes the reader has a passing familiarity with Go, so I do not give many details about the language itself. If you do not know Go at all but are interested, A Tour of Go is a good place to start. You also might like my earlier article, An Introduction to the Go Language: Boldly Going Where No Man Has Ever Gone Before.

Go has an extensive standard library that supports virtually everything one would expect from a systems programming language. The way it is laid out makes it easier for developers to extend it. One example of such package is net/http and it is the basis of what we do today.

To create a Web service, you can simply use the net/http package, which works fine. However, this package was created to generally support HTTP (both servers and clients); HTTP is used mainly as the Web services transport so the higher-level abstraction needs to be implemented by hand.

But there is no need to do that. You can simply use one of the very good Web-related Go third-party packages. Here is a non-extensive list:

  • Gorilla: A Web toolkit that extends the net/http package.
  • Goweb: A framework for Web services, with several advanced  features.
  • Martini: A very nice framework for Web services that makes use of reflection and dependency injection to simplify several aspects of developing them. Martini is a promising newcomer to the Go universe.

For our example, we use Martini to do the heavy lifting. It may not be the most established option but it has the most elegant API from my point of view (and it plays nicely with the net/http package). Martini does most of its stuff under the hood, so you only see hints of its workings in most of the relevant code provided in this article.

With all that out of the way, let’s start.

Imagine you have an existing API without Web support. For our purposes, we take a simple non-Web Guest Book API and make it work as a Web service. We implement our guest book in memory to simplify the example, but even if it was backed by a full fledged database, the amount of work to transform it to a Web service would be the same.

Here is what the API looks like:

// GuestBookEntry represents a single entry in a Guest Book. It contains the
// usual fields.
type GuestBookEntry struct {
        Id      int
        Email   string
        Title   string
        Content string
// GuestBook represents a Guest Book instance. It holds the associated
// GuestBookEntries.
type GuestBook struct {
        guestBookData []*GuestBookEntry
// NewGuestBook returns a new empty GuestBook instance.
func NewGuestBook() *GuestBook
// AddEntry adds a new GuestBookEntry with the provided data.
func (g *GuestBook) AddEntry(email, title, content string) int
// RemoveEntry removes the entry with the given id. Return nil in case of
// success or a specific error in case of failure.
func (g *GuestBook) RemoveEntry(id int) error
// GetEntry returns the entry identified by the given id or an error if it can
// not find it.
func (g *GuestBook) GetEntry(id int) (*GuestBookEntry, error)
// GetAllEntries returns all non-nil entries in the Guest Book.
func (g *GuestBook) GetAllEntries() []*GuestBookEntry
// RemoveAllEntries removes all entries from the Guest Book.
func (g *GuestBook) RemoveAllEntries()

We have a struct that represents a single guest book entry, the container for those entries, and methods that act on this container (you can see the complete source code here). As can be seen, there is nothing Web-specific in this API.

Go incorporates the concepts of duck-typing and interfaces. This means that a method can take an interface (which boils down to a list of methods) as a parameter and any types that implement the interface (i.e. has the required methods) can be passed to it. This is how we define our Web service:

// WebService is the interface that should be implemented by types that want to
// provide web services.
type WebService interface {
        // GetPath returns the path to be associated with the service.
        GetPath() string
         // WebDelete wraps a DELETE method request. The given params might be
        // empty, in case it was applied to the collection itself (i.e. all
        // entries instead of a single one) or will have a “id” key that will
        // point to the id of the entry being deleted.
        WebDelete(params martini.Params) (int, string)
        // WebGet is Just as above, but for the GET method. If params is empty,
        // it returns all the entries in the collection. Otherwise it returns
        // the entry with the id as per the “id” key in params.
        WebGet(params martini.Params) (int, string)
        // WebPost wraps the POST method. Again an empty params means that the
        // request should be applied to the collection. A non-empty param will
        // have an “id” key that refers to the entry that should be processed
        // (note this specific case is usually not supported unless each entry
        // is also a collection).
        WebPost(params martini.Params, req *http.Request) (int, string)

The interface above contains the methods that map to the HTTP methods that are used for Web services. Here we only add support to the two most common methods (GET and POST) and one extra method (DELETE) so we can fully exercise the existing API. Adding support for other methods (PUT, PATCH) is trivial.

We also have a function that registers Web services that looks like this:

// RegisterWebService adds Martini routes to the relevant webservice methods
// based on the path returned by GetPath. Each method is registered once for
// the collection and once for each id in the collection.
func RegisterWebService(webService WebService, classicMartini *martini.ClassicMartini)

Any type that implements the WebService interface can be passed to this method (full source code here). The method also takes a ClassicMartini instance pointer as it does most of the work under the hood.

Our Guest Book API does not implement any of these methods so we can not use it as-is, but it is trivial to do so. Here starts our five minutes. (What? You expected the clock to be ticking already?)Any type that implements the WebService interface can be passed to this method (full source code here). The method also takes a ClassicMartini instance pointer as it does most of the work under the hood.

To implement the new methods, we do not even need to change the actual file that implements our API. Instead we simply create a new file in the same package that only contains the new methods being implemented for our GuestBook type. For example, here is what the WebPost method would look like:

// WebPost implements webservice.WebPost.
func (g *GuestBook) WebPost(params martini.Params,
        req *http.Request) (int, string) {
        // Make sure Body is closed when we are done.
        defer req.Body.Close()
        // Read request body.
        requestBody, err := ioutil.ReadAll(req.Body)
        if err != nil {
                return http.StatusInternalServerError, “internal error”
        if len(params) != 0 {
                // No keys in params. This is not supported.
                return http.StatusMethodNotAllowed, “method not allowed”
        // Unmarshal entry sent by the user.
        var guestBookEntry GuestBookEntry
        err = json.Unmarshal(requestBody, &guestBookEntry)
        if err != nil {
                // Could not unmarshal entry.
                return http.StatusBadRequest, “invalid JSON data”
        // Add entry provided by the user.
        g.AddEntry(guestBookEntry.Email, guestBookEntry.Title,
        // Everything is fine.
        return http.StatusOK, “new entry created”

Both parameters our method receives, params and req (which, by the way, is an http.Request pointer from the net/http package), are automatically injected by Martini. Martini is also responsible for calling the correct methods of our Web API based on the method used for the request and the URL used.

Another point worth mentioning is that the encoding/json package in the standard library makes it a breeze to convert JSON to Go structs and vice-versa, removing another item from the list of things you have to worry about when creating a Web service.

That is it! With that (and the other methods; full source here) our service is ready to be used. All that is left to do is write the code to put it all together and actually run the service. In our example, it would be as simple as this:

func main() {
        martiniClassic := martini.Classic()
        guestBook := guestbook.NewGuestBook()
        webservice.RegisterWebService(guestBook, martiniClassic)

We create a new Martini instance, create a new guest book, register it as a Web service, and start our server to handle requests. To see it working (assuming you have Go installed; if not, you can see how to install it here) just do this at the command line:

go get

Switch to the directory where the code was fetched and run the server code:

cd $GOPATH/src/
go run server.go

In case you did not do this yet, the full source code for this example can be found here. This also includes a simple Web service client that allows you to test Web services that accept JSON data. To run it, you can do something like this at the command line.The server starts running and accepts requests on port 3000. Add entry:

go run client.go –request_url=”” –request_method=post \

Get single entry:

go run client.go –request_url=”″

Get all entries:

go run client.go –request_url=””

Delete single entry:

go run client.go –request_url=”″ –request_method=delete

Delete all entries:

go run client.go –request_url=”” –request_method=delete

Note that although we now have a fully-working Web service, there are some desirable functionalities that we did not implement. For example, access to the API is not controlled in any way, and anyone can send requests. Martini makes it a breeze to add authentication as a middleware; check its documentation to see how it is done.

The best thing about having a solid framework to work with is that you can focus on writing and perfecting your actual API without having to worry about the details of the Web side of things, making you more productive. Good languages make writing good frameworks easier. In this respect, Go passes with flying colors.

See also:


  1. Thanks for the article.But the code format is all messed up.

    • Bruno Albuquerque says:

      I just noticed that. Thanks for the heads up anyway. I will let people know about it to try to get it fixed.

  2. Thanks for the article and code, very helpful. How can I use curl to send message to the server? I tried curl -v -H “Content-type: application/json” -X POST -d ’{“Id”:0,”Email”:”EMAIL”,”Title”:”TITLE”,”Content”:”CONTENT”}’, but it didn’t work!

    Thanks in advance

  3. I didn’t try very hard, but the repository cod doesn’t work w/ go 1.2.1, print statements peppered about show the errors are not coming from the code, so something is being called improperly. For anyone who wants a debug project, here you go.

  4. web design service says:

    I just noticed that. Thanks for the heads up anyway. I will let people know about it to try to get it fixed.

  5. There’ll little doubt be those that will criticise the Tate extension for precisely the contrary factors
    that I’m criticising it for.

  6. Again in October final year, following the discharge of HTML 5.0 as a Recommendation, I wrote about
    Streaming video on the Web as a superb instance of
    extra work to do. However that’s just one among many: persistent background processing , body fee performance data , metadata related to an internet
    software , or mitigating cross-web site assaults are among many additions we’re working on to push the envelop.

  7. Hey there, You’ve done an excellent job. I’ll definitely digg it and personally suggest to my friends.

    I am sure they’ll be benefited from this website.

  8. I would like to thank you for the efforts you have put
    in penning this site. I’m hoping to see the same high-grade blog posts from you in the future as well.
    In truth, your creative writing abilities has encouraged me to get my own, personal blog now 😉

  9. Seriously? How could I potentially say that?
    Well, there is an amazing chance today to put your business opportunity in front of millions of targeted prospects.
    and it is due to the amazing power of the web.
    Whatever type of celebration you may wish to hold, you will always need a professional
    catering service. You can have a household gathering, an office-related affair or perhaps just a common celebration such as graduation, baptism or birthday.
    For all sort of parties, you will certainly always need a proper catering
    service that will take care of the foods.

    First on your list is the headline/subject line.

    The headline is your store window, it exists to get the interest and make
    them read on or open the e-mail. Instead of, Use Joe’s Marvel Book To Enhance Your Company, you require to develop a need in your reader to keep on checking out so try something
    like 3 Simple Modifications That Could Double Your Company In One Month.
    See the distinction, currently they can see in their mind what doubling their company would suggest to them and if they can do it in one month just by doing
    3 basic things it should deserve having a look at.
    Mission one completed.
    This time, Mike works together with Chris Farrell who is less well understood in the IM world.
    Chris is also a really successful online marketing professional however he has simply been involved in web marketing for
    2 years; that is why his name is not so renowneded yet.

    The greatest problem with the 2nd and third choices
    is that if you can’t write good advertising copy, you may not understand exactly what to
    change to stamp your character on the sales letter. Likewise, you might produce something
    that does not make sense or, even worse still, you may produce something accidentally funny and either of these can destroy the reliability you have striven to establish.

    How: When somebody buys through empower network it’s connected
    to your paypal or merchant account. For example, when I bought
    the system and I clicked “pay”, I was redirected to
    the paypal internet marketing site which provided the product price and was setup to pay the person whose
    blog/article referred me. It was automatic,
    simple and just made sense.
    Is your market niche enough? In truth, specific
    niche marketing is the only advertising that works wonder in web marketing.
    You need to concentrate your market really particularly in order to construct much targeted leads.
    Much like if you are targeting sport market, which sport
    are you targeting? Is it tennis? And even if it is tennis, you need to still even more concentrate your market, like lady’s tennis and so on.
    So exactly what does this mean for the market? Has the end
    finally come and the entire market has now been shot to you
    understand what as the standard tattoo artist would state?
    Or is this just all fantastic for company and we should
    simply pocket the cash and go to the bank and not fret about
    the artistic value of the piece and even if the customer should truly be getting a
    tattoo done or not?

  10. Good write-up. I certainly love this site. Stick with it!

  11. My relatives every time say that I am wasting my time here at web, but I know I am getting know-how daily by reading thes pleasant articles or reviews.

  12. Nice post. I learn something new and challenging
    on websites I stumbleupon everyday. It’s always interesting to read articles from other authors and use something from
    their websites.

  13. Great information really useful for me and i learn lot of information about this article….

  14. Wonderful blog! Do you have any tips for aspiring writers?

    I’m planning to start my own site soon but I’m a llittle lost on everything.
    Would you recommend starting with a free platform like WordPress or go for a
    paid option? There are so many options out there that
    I’m completely confused ..Any suggestions? Thanks!

  15. I visit everyday some blogs and blogs to read content, except this web site presents quality based writing.

Speak Your Mind