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"}

Resources

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