- Lab
- Core Tech

Guided: Build a Simple REST API in Go
This Code Lab will guide you through implementing key features of REST APIs in Go. You'll create an HTTP server, implement request handlers, and make your application production ready. By the end of this lab, you'll be equipped to build your own REST APIs from scratch.

Path Info
Table of Contents
-
Challenge
Introduction
In this Guided Code Lab, you are responsible for developing the API server for an application called Globotickets. This is a platform used to buy and sell tickets for local events. You will have to create REST API endpoints to purchase tickets and record transactions.
Each task can be validated individually by clicking on the Validate button next to it.
If you want to test the application code at any time, you can follow these steps:
- Click on the Run button at the bottom right of the Terminal.
- To test a particular REST API route, you can use the second Terminal tab after completing the previous step and use the
curl
command, along with the route you want to test. You will see examples of how to use this command the the next steps.
-
Challenge
Step 1: Creating a New HTTP Server
Currently, you have an empty application, with the package name and
main
function defined withinmain.go
.You will need to implement an empty HTTP server that listens on port 8000 using the native
"net/http"
package. Once you complete the above task, you'll have a server running on port8000
.The
ListenAndServer
method actually blocks while the server is running so that the program doesn't halt. This means that when you run the application the program will not exit until you interrupt it.To try this yourself, run the application in the Terminal. It will run indefinitely, until you quit the application using Ctrl+C.
info> Note: Make sure to quit the application when validating your tasks in each step.
Remember to quit the terminal application when you want to test any new code.
You can use the other Terminal to test the server, as you'll see in the next steps.
-
Challenge
Step 2: Creating a POST Route to Add Ticket Purchases
Now that you've created a server, let's add a handler to add ticket purchases for various events.
First, you'll need a place to store this information. To do this, you will need to create a variable that can store event names and the number of tickets purchased for each event name,
The easiest way to do this is to use a
map
type. Maps are declared using the syntax:var mapVariableName = make(map[KeyType]ValueType)
Maps are useful because they provide a convenient and efficient way to store and retrieve data associated with unique keys. They are similar to dictionaries in Python or objects in JavaScript. They allow for fast lookups, additions, and deletions, making them ideal for tasks that involve a mutable, unordered collection of unique elements, such as our ticket purchases. Next, you need to create a handler for the
POST /ticket
HTTP route.You can create an empty handler for now. The only important thing to check here is that only incoming requests for the
POST
method and/ticket
route are handled.For an unsupported method, you should return a
405
HTTP error code.info> Note: In Go, the standard HTTP status codes are available as named constants within the
net/http
package (e.g.http.StatusOK
for200
). Finally, let's tie everything together - when you receive a POST request, you need to read the JSON data from the request body.The request body will look something like this:
{ "eventName": "some_event", "tickets": 5, }
This data will be available in the request body. You'll then need to find the event entry in the
ticketPurchases
map and increment its value by the number specified in"tickets"
, which in this case would be 5. Nice! You now have a functional route handler. Any POST requests going tolocalhost:8000/ticket
will be handled by your code.If you want to test this out yourself, try running the application, and on the other Terminal, you can execute:
curl -v -X POST -d '{"eventName":"my_event", "tickets":2}' http://localhost:8000/ticket
You should see a
HTTP/1.1 200 OK
message which indicates that it ran successfully. -
Challenge
Step 3: Creating a GET Route to View Ticket Purchases
The
POST /ticket
route that you created earlier allows you to add ticket purchase details for any number of events.Now, you need to create another route that allows you to see the number of tickets purchased for a given event.
The best practice in this case is to use a
GET
route. The event name can be read from the query parameter. For example, if you want to view the ticket purchases for an event named "my_event", you can make the following request:GET http://localhost:8000/tickets?eventName=my_event
And this should give you the response in JSON:
{ "eventName": "my_event", "tickets": 3 }
There are different ways to create a JSON string in Go. You could, of course, construct the string yourself. For example:
myJsonString := "{\"eventName\":\"my_event\"}"
However, this is very brittle, since you'll need to take care of special characters and end quotes.
A better way would be to create a
struct
that represents the JSON structure of the object and use thejson.NewEncoder
function to convert an object of that type into a JSON string.For example:
type MyStruct struct { MyField int `json:"my_field"` // remember to add these `json` tags - this will be the actual name of the field in the JSON string } s := MyStruct{} json.NewEncoder(w).Encode(s) ``` Awesome! You can now read event ticket purchase details. Try using the curl command like before to view the number of tickets purchased for different event names:
curl http://localhost:8000/tickets?eventName=my_event
Does this number change after calling the `POST /ticket` route repeatedly?
-
Challenge
Step 4: Error Handling and Production Readiness
If you've completed the previous steps, you should end up with a working application.
However, there are still some issues that you may face in the real world, where users don't call the API in exactly the way you intended.
For example, when calling the
POST /ticket
API, the user may not provide valid JSON data. Or, when calling theGET /tickets
API, the event name might be missing.To build a production-ready application, you'll need to handle these cases and send a useful error message, so that the end user knows why they're getting an error,
Almost all error-prone functions in Go return an
error
type. In Go, all errors are values and they need to be check explicitly. This means that you should ideally have error checks for every error-returning method in your code.The most common way to handle errors is using a
nil
check:err := someFunction() if err != nil { // handle errors here }
If you observe carefully, some of the responses are in a different format than others.
- For the
POST /ticket
endpoint, an empty response is sent in case it's successful. - For the
GET /tickets
endpoint, JSON data is sent as the response. - For errors, a plain text message is sent along with a bad-request status code.
By default, the response is expected to be in plain text, which is okay for points 1 and 3.
However, for JSON data the best practice is to set the
Content-Type
response header toapplication/json
, so that any user calling our API knows which format to expect.The
http.ResponseWriter
type has aHeader()
method which returns the header object. You can use theAdd
method of this object to add new headers. For example:w.Header().Add("Header Name", "Header Value")
Now that you've handled some edge cases, your handlers are more stable and user-friendly.
Remember, this is an ongoing process and you should always look for edge cases and unexpected behavior when adding new functionality.
There are some more things you could do to make this application better as next steps:
- Adding logs to your code helps you debug your application if things don't work as they should. Try using the
fmt
orlog
package to add informational logs. For example, every time a ticket is purchased, you can add a log:fmt.Printf("Purchased %d tickets for event %s", tickets, eventName)
- Adding unit tests helps with testing your code in a reproducible way. You can use Go's
testing
package to do this. - Moving the code for each handler to its own file helps in organizing the overall code base. Try and see if you can do this with the two handler you defined so far.
- For the
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.