What is REST?

REST (REpresentational State Transfer) is an architectural style for designing and developing APIs that could be used for client and server interactions.

REST defines 6 architectural constraints for APIs:

  • Uniform interface: API requests for a resource should look the same irrespective of its origin client. The Uniform Resource Identifier (URI) allocated to resources should be unique.
  • Client-server: Changes on the client side should not affect the server and vice-versa.
  • Stateless: The API request should contain all the resources required to process it. The server should not store any details related to client requests.
  • Cacheable: API Responses could be cached on the client or server side.
  • Layered system: Neither the client nor the server should have information regarding the number of intermediate layers during communication.
  • Code on demand (optional): When required, code snippets sent through API responses should be executable on demand.

APIs that comply with REST architecture constraints are called RESTful APIs.

REST Requests

A REST request comprises of:

  • HTTP Verb: Specifies the type of operation to be performed on resource/s like creation, fetch, updation, and deletion.
  • Header: Contains the information about the request like its datatype (text/html, text/plain, application/json, etc.), credentials, proxy server address, etc.
  • Path: Path to the resource (URI)
  • Body: Input data for the operation.

HTTP Verbs

GET, POST, PUT, and DELETE are common HTTP verbs available in RESTful APIs.

Note: To follow this tutorial you need to head over to crudcrud.com to get a new API endpoint.

The net/http Go package provides all the necessary utilities for implementing REST clients and servers.

POST

A POST request is performed to create resources.

If we have to perform a POST request on the API endpoint provided by crudcrud.com we have to pass the resource (to be created) as a JSON payload in the request’s body.

package main

import (
   "fmt"
   "net/http"
   "encoding/json"
   "bytes"
   "io/ioutil"
)

func main() {
	// Replace with the API ID provided by crudcrud.com
	apiID := "XXXXXX"

	// HTTP endpoint with path of resource (vehicles)
	createResourceURL := fmt.Sprintf("https://crudcrud.com/api/%s/vehicles",
									apiID)

	// JSON payload containing resource information
	requestBody, _ := json.Marshal(map[string]string{
		"color": "White",
		"license": "ABC123",
		"numWheels": "4",
		"type": "Car",
	})

	// Converting request body into bytes buffer
	requestBodyBytes := bytes.NewBuffer(requestBody)

	// Creating a POST request on createResourceURL
	request, err := http.NewRequest("POST", createResourceURL, requestBodyBytes)
	if err != nil {
		panic(err)
	}

	// Adding application/json as payload type 
	request.Header.Add("Content-Type", "application/json")

	client := &http.Client{}

	// Performing POST request
	response, err := client.Do(request)
	if err != nil {
		panic(err)
	}

	defer response.Body.Close()

	// Reading the response to the request
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error received in response:",err)
	}

	responseBodyString := string(responseBody)
	fmt.Println(responseBodyString)
}

// Output
// {"color":"White","license":"ABC123","numWheels":"4","type":"Car",
// "_id":"6501480bb987ad03e8769e4}

To check the resource on the server side you can click on the “Check Endpoint Information” button on the crudcrud.com page. A new REST resource named vehicles would have been created.

GET

To fetch all vehicles resources created by the POST request we will perform a GET request on the same API endpoint.

package main

import (
   "fmt"
   "net/http"
   "io/ioutil"
   "encoding/json"
)

type Vehicle struct{
	Id          string `json:"_id"`
	Color       string
	License     string
	NumWheels   string
	VehicleType string
}

func main() {
	// Replace this with the API ID provided by crudcrud.com
	apiID := "XXXXXX"

	// HTTP endpoint with the path of the resource (vehicles)
	fetchResourceURL := fmt.Sprintf("https://crudcrud.com/api/%s/vehicles", 
									apiID)

	// Performing GET request
	response, err := http.Get(fetchResourceURL)
	if err != nil {
		panic(err)
	}

	defer response.Body.Close()

	// Reading the response to the request
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error received in response:",err)
	}

	// Marshalling the response of API into a list of type Vehicle
	var vehicles []Vehicle
	json.Unmarshal(responseBody, &vehicles)
	
	for _, veh := range vehicles{
		fmt.Println("Vehicle Resource ID:", veh.Id)
		fmt.Println("Color:", veh.Color)
		fmt.Println("License:", veh.License)
		fmt.Println("NumWheels:", veh.NumWheels)
		fmt.Println("Type:", veh.VehicleType)
		fmt.Println()
	}

}

// Output
// Vehicle Resource ID: 65014c0ab987ad03e8769e5c
// Color: Black
// License: XYZ435
// NumWheels: 4
// Type: Car
//
// Vehicle Resource ID: 65014c1cb987ad03e8769e5d
// Color: White
// License: ABC123
// NumWheels: 4
// Type: Car
//
// Vehicle Resource ID: 65014c59b987ad03e8769e5e
// Color: Red
// License: IJK546
// NumWheels: 2
// Type: Motorcycle

We can also fetch specific resources by specifying their _id in URI.

package main

import (
   "fmt"
   "net/http"
   "io/ioutil"
   "encoding/json"
)

type Vehicle struct{
	Id          string `json:"_id"`
	Color       string
	License     string
	NumWheels   string
	VehicleType string
}

func main() {
	// Replace this API ID with the one provided by crudcrud.com
	apiID := "XXXXXX"

	// Replace this with the ID of the resource you want to fetch
	vehicleResourceID := "65014c59b987ad03e8769e5e"

	// HTTP endpoint with the path of the resource (vehicles)
	fetchResourceURL := fmt.Sprintf("https://crudcrud.com/api/%s/vehicles/%s",	
							apiID, vehicleResourceID)

	// Performing GET request
	response, err := http.Get(fetchResourceURL)
	if err != nil {
		panic(err)
	}

	defer response.Body.Close()

	// Reading the response to the request
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error received in response:",err)
	}

	// Unmarshalling the response of API into a list of type Vehicle
	var veh Vehicle
	json.Unmarshal(responseBody, &veh)

	fmt.Println("Vehicle Resource ID:", veh.Id)
	fmt.Println("Color:", veh.Color)
	fmt.Println("License:", veh.License)
	fmt.Println("NumWheels:", veh.NumWheels)
	fmt.Println("Type:", veh.VehicleType)
}

// Output
// Vehicle Resource ID: 65014c59b987ad03e8769e5e
// Color: Red
// License: IJK546
// NumWheels: 2
// Type: Motorcycle

PUT

To update a resource we perform a PUT request.

package main

import (
   "fmt"
   "net/http"
   "encoding/json"
   "bytes"
   "io/ioutil"
)

func main() {
	// Replace this API ID with the one provided by crudcrud.com
	apiID := "XXXXXX"

	vehicleResourceID := "65014c0ab987ad03e8769e5c"

	// HTTP endpoint with path (vehicle) of the resource
	updateResourceURL := fmt.Sprintf("https://crudcrud.com/api/%s/vehicles/%s",	
							apiID, vehicleResourceID)

	// JSON payload as bytes
	requestBody, _ := json.Marshal(map[string]string{
		"color": "Black",
		"license": "XYZ435",
		"numWheels": "2",
		"vehicleType": "Motorcycle",
	})

	// Converting request body into bytes buffer
	requestBodyBytes := bytes.NewBuffer(requestBody)

	// Creating a PUT request on updateResourceURL
	request, err := http.NewRequest("PUT", updateResourceURL, requestBodyBytes)
	if err != nil {
		panic(err)
	}

	// Adding application/json as payload type 
	request.Header.Add("Content-Type", "application/json")

	client := &http.Client{}

	// Performing PUT request
	response, err := client.Do(request)
	if err != nil {
		panic(err)
	}

	defer response.Body.Close()

	// Verifying changes by fetching the details of the resource
	// Performing GET request
	responseGet, err := http.Get(updateResourceURL)
	if err != nil {
		panic(err)
	}

	defer responseGet.Body.Close()

	// Reading the response to the request
	responseBody, err := ioutil.ReadAll(responseGet.Body)
	if err != nil {
		fmt.Println("Error received in response:",err)
	}

	fmt.Println("Updated Resource:",string(responseBody))
}

// Output
// Updated Resource: {"_id":"65014c0ab987ad03e8769e5c","color":"Black",
// "license":"XYZ435","numWheels":"2","vehicleType":"Motorcycle"}

DELETE

The DELETE verb is used for resource deletion requests.

package main

import (
   "fmt"
   "net/http"
   "io/ioutil"
)

func main() {
	// Replace this API ID with the one provided by crudcrud.com
	apiID := "XXXXXX"

	vehicleResourceID := "65014c0ab987ad03e8769e5c"

	// HTTP endpoint with path (vehicle) of the resource
	deleteResourceURL := fmt.Sprintf("https://crudcrud.com/api/%s/vehicles/%s", 
							apiID, vehicleResourceID)

	// Creating a DELETE request on deleteResourceURL
	request, err := http.NewRequest("DELETE", deleteResourceURL, nil)
	if err != nil {
		panic(err)
	}

	// Adding application/json as payload type 
	request.Header.Add("Content-Type", "application/json")

	client := &http.Client{}

	// Performing DELETE request
	response, err := client.Do(request)
	if err != nil {
		panic(err)
	}

	defer response.Body.Close()

	// Performing GET request
	responseGet, err := http.Get(deleteResourceURL)
	if err != nil {
		panic(err)
	}

	defer responseGet.Body.Close()

	// Reading the response to the request
	responseBody, err := ioutil.ReadAll(responseGet.Body)
	if err != nil {
		fmt.Println("Error received in response:",err)
	}

	fmt.Println("Deleted Resource:",string(responseBody))
}

// Output
// Deleted Resource: {"type":"https://tools.ietf.org/html/rfc7231#section-6.5.4",
// "title":"Not Found","status":404,"traceId":"0HMTFDGO4V3QF:00000001"}

Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my RSS Feed.

Resources

REST API Tutorial
What is REST?
REST Architectural Constraints
net/http Go Package
HTTP Verbs in net/http package