Creating a Simple RESTful API in Go

Creating a Simple RESTful API in Go

In this tutorial demonstrates how you can create a simple RESTful JSON api using Go(Lang)

In this tutorial demonstrates how you can create a simple RESTful JSON api using Go(Lang)

If you are writing any form of web application, then you are most likely interfacing with 1 or more REST APIs in order to populate the dynamic parts of your application and to perform tasks such as updating or deleting data within a database.

In this tutorial, you are going to be building a fully-fledged REST API that exposes GET, POST, DELETE and PUT endpoints that will subsequently allow you to perform the full range of CRUD operations.

In order to keep this simple and focus on the basic concepts, we won’t be interacting with any backend database technologies to store the articles that we’ll be playing with. However, we will be writing this REST API in such a way that it will be easy to update the functions we will be defining so that they make subsequent calls to a database to perform any necessary CRUD operations.

Source Code - The full source code for this article can be found here: TutorialEdge/create-rest-api-in-go-tutorial## Prerequisites

  • You will need Go version 1.11+ installed on your development machine.
Goals

By the end of this tutorial, you will know how to create your own REST-ful APIs in **Go **that can handle all aspects of. You will know how to create **REST **endpoints within your project that can handle POST, GET, PUT and DELETE HTTP requests.

Video Tutorial REST Architectures

REST is everywhere these days, from websites to enterprise applications, the RESTful architecture style is a powerful way of providing communication between separate software components. Building REST APIs allow you to easily decouple both consumers and producers and are typically stateless by design.

Note - If you wish to learn more about the basics of REST APIs then check out What Are RESTful APIs?## JSON

For the purpose of this tutorial I’ll be using JavaScript Object Notation as a means of sending and receiving all information and thankfully Go comes with some excellent support for encoding and decoding these formats using the standard library package, encoding/json.

Note - For more information on the encoding/json package check out the official documentation: encoding/json## Marshalling

In order for us to easily We can easily convert data structures in GO into JSON by using something called marshalling which produces a byte slice containing a very long string with no extraneous white space.

Getting Started with A Basic API

To get started we will have to create a very simple server which can handle HTTP requests. To do this we’ll create a new file called main.go. Within this main.go file we’ll want to define 3 distinct functions. A homePage function that will handle all requests to our root URL, a handleRequests function that will match the URL path hit with a defined function and a main function which will kick off our API.

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func homePage(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homePage")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

func main() {
    handleRequests()
}

If we run this on our machine now, we should see our very simple API start up on port 10000 if it’s not already been taken by another process. If we now navigate to <a href="http://localhost:10000/" target="_blank">http://localhost:10000/</a> in our local browser we should see Welcome to the HomePage! print out on our screen. This means we have successfully created the base from which we’ll build our REST API.

Note - If you want a more in-depth tutorial on how to create a go based web server then check out this tutorial here: Creating a Simple Web Server with Go(Lang)## Our Articles Structure

We’ll be creating a REST API that allows us to CREATE, READ, UPDATE and DELETE the articles on our website. When we talk about CRUD APIs we are referring to an API that can handle all of these tasks: Creating, Reading, Updating and Deleting.

Before we can get started, we’ll have to define our Article structure. Go has this concept of structs that are perfect for just this scenario. Let’s create an Article struct that features a Title, a Description (desc) and Content like so:

type Article struct {
    Title string `json:"Title"`
    Desc string `json:"desc"`
    Content string `json:"content"`
}

// let's declare a global Articles array
// that we can then populate in our main function
// to simulate a database
var Articles []Article

Our Struct contains the 3 properties we need to represent all of the articles on our site. In order for this to work, we’ll also have to import the "encoding/json" package into our list of imports.

Let’s now update our main function so that our Articles variable is populated with some dummy data that we can retrieve and modify later on.

func main() {
    articles := Articles{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

Perfect, let’s now move on to creating our /articles endpoint which will return all of the articles that we’ve just defined here.

Retrieving All Articles

In this part of the tutorial we are going to create a new REST endpoint which, when hit with a HTTP GET request, will return all of the articles for our site.

We’ll first start off by creating a new function called returnAllArticles, which will do the simple task of returning our newly populated Articles variable, encoded in JSON format:

main.go

func returnAllArticles(w http.ResponseWriter, r *http.Request){
    fmt.Println("Endpoint Hit: returnAllArticles")
    json.NewEncoder(w).Encode(Articles)
}

The call to json.NewEncoder(w).Encode(article) does the job of encoding our articles array into a JSON string and then writing as part of our response.

Before this will work, we’ll also need to add a new route to our handleRequests function that will map any calls to <a href="http://localhost:10000/articles" target="_blank">http://localhost:10000/articles</a> to our newly defined function.

func handleRequests() {
    http.HandleFunc("/", homePage)
    // add our articles route and map it to our 
    // returnAllArticles function like so
    http.HandleFunc("/articles", returnAllArticles)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

Now that we’ve done this, run the code by typing go run main.go and then open up <a href="http://localhost:10000/articles" target="_blank">http://localhost:10000/articles</a> in your browser and you should see a JSON representation of your list of articles like so:

http://localhost:10000/articles response

[
  {
    Title: "Hello",
    desc: "Article Description",
    content: "Article Content"
  },
  {
    Title: "Hello 2",
    desc: "Article Description",
    content: "Article Content"
  }
];

We’ve successfully defined our first API endpoint.

In the next part of this series, you are going to update your REST API to use a gorilla/mux router instead of the traditional net/http router.

Swapping the routers will enable you to more easily perform tasks such as parsing any path or query parameters that may reside within an incoming HTTP request which we will need later on.

Getting Started with Routers

Now the standard library is adequate at providing everything you need to get your own simple REST API up and running but now that we’ve got the basic concepts down I feel it’s time to introduce third-party router packages. The most notable and highly used is the gorilla/mux router which, as it stands currently has 2,281 stars on Github.

Building our Router

We can update our existing main.go file and swap in a gorilla/mux based HTTP router in place of the standard library one which was present before.

Modify your handleRequests function so that it creates a new router.

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
    "encoding/json"
    "github.com/gorilla/mux"
)

… // Existing code from above
func handleRequests() {
    // creates a new instance of a mux router
    myRouter := mux.NewRouter().StrictSlash(true)
    // replace http.HandleFunc with myRouter.HandleFunc
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    // finally, instead of passing in nil, we want
    // to pass in our newly created router as the second
    // argument
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

func main() {
    fmt.Println("Rest API v2.0 - Mux Routers")
    Articles = []Article{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

When you now run this, you will see no real change to the way our system works. It will still start up on the same port and return the same results depending on what endpoints you hit.

The only real difference is that we now have a gorilla/mux router which will allow us to easily do things such as retrieve path and query parameters later on in this tutorial.

$ go run main.go

Rest API v2.0 - Mux Routers

Path Variables

So far so good, we’ve created a very simple REST API that returns a homepage and all our Articles. But what happens if we want to just view one article?

Well, thanks to the gorilla mux router we can add variables to our paths and then pick and choose what articles we want to return based on these variables. Create a new route within your handleRequests() function just below our /articles route:

myRouter.HandleFunc("/article/{id}", returnSingleArticle)

Notice that we’ve added {id} to our path. This will represent our id variable that we’ll be able to use when we wish to return only the article that features that exact key. For now, our Article struct doesn’t feature an Id property. Let’s add that now:

type Article struct {
    Id      string `json:"Id"`
    Title   string `json:"Title"`
    Desc    string `json:"desc"`
    Content string `json:"content"`
}

We can then update our main function to populate our Id values in our Articles array:

func main() {
    Articles = []Article{
        Article{Id: "1", Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Id: "2", Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

Now that we’ve done that, in our returnSingleArticle function we can obtain this {id} value from our URL and we can return the article that matches this criteria. As we haven’t stored our data anywhere we’ll just be returning the Id that was passed to the browser.

func returnSingleArticle(w http.ResponseWriter, r *http.Request){
    vars := mux.Vars(r)
    key := vars["id"]

    fmt.Fprintf(w, "Key: " + key)
}

If we navigate to <a href="http://localhost:1000/article/1" target="_blank">http://localhost:1000/article/1</a>after we’ve now run this, you should see Key: 1 being printed out within the browser.

Let’s use this key value to return the specific article that matches that key.

func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    key := vars["id"]

    // Loop over all of our Articles
    // if the article.Id equals the key we pass in
    // return the article encoded as JSON
    for _, article := range Articles {
        if article.Id == key {
            json.NewEncoder(w).Encode(article)
        }
    }
}

Run that by calling go run main.go and then open up <a href="http://localhost:10000/article/1" target="_blank">http://localhost:10000/article/1</a> in your browser:

http://localhost:10000/article/1 response

{
Id: "1",
Title: "Hello",
desc: "Article Description",
content: "Article Content"
}

You will now see the article matching the key 1 returned as JSON.

Creating and Updating Articles

In this part of the tutorial, you are going to build the Create, Update and DELETE part of a CRUD REST API. We have already covered the R with the ability to read both single articles and all articles.

Creating new Articles

Once again, you will need to create a new function which will do the job of creating this new article.

Let’s start off by creating a createNewArticle() function within our main.go file.

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    // get the body of our POST request
    // return the string response containing the request body    
    reqBody, _ := ioutil.ReadAll(r.Body)
    fmt.Fprintf(w, "%+v", string(reqBody))
}

With this function defined, you can now add the route to the list of routes defined within the handleRequests function. This time however, we’ll be adding .Methods("POST") to the end of our route to specify that we only want to call this function when the incoming request is a HTTP POST request:

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/articles", returnAllArticles)
    // NOTE: Ordering is important here! This has to be defined before
    // the other `/article` endpoint. 
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    myRouter.HandleFunc("/article/{id}", returnSingleArticle)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

Try running this again and then try submitting a HTTP POST request which contains the following POST body:

{
    "Id": "3", 
    "Title": "Newly Created Post", 
    "desc": "The description for my new post", 
    "content": "my articles content" 
}

Our endpoint will trigger and subsequently echo back whatever value was in the request body.

Now that you have validated your new endpoint is working correctly, let’s update our createNewArticle function so that it unmarshals the JSON in the request body into a new Article struct which can subsequently be appended to our Articles array:

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    // get the body of our POST request
    // unmarshal this into a new Article struct
    // append this to our Articles array.    
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article 
    json.Unmarshal(reqBody, &article)
    // update our global Articles array to include
    // our new Article
    Articles = append(Articles, article)

    json.NewEncoder(w).Encode(article)
}

Awesome! If you run this now and send the same POST request to your application, you will see that it echoes back the same JSON format as before, but it also appends the new Article to your Articles array.

Validate this now by hitting the <a href="http://localhost:10000/articles" target="_blank">http://localhost:10000/articles</a>:

http://localhost:10000/articles response

[
    {
        "Id": "1",
        "Title": "Hello",
        "desc": "Article Description",
        "content": "Article Content"
    },
    {
        "Id": "2",
        "Title": "Hello 2",
        "desc": "Article Description",
        "content": "Article Content"
    },
    {
        "Id": "3",
        "Title": "Newly Created Post",
        "desc": "The description for my new post",
        "content": "my articles content"
    }
]

You have now successfully added a Create function to your new REST API!

In the next section of this tutorial, you are going to look at how you can add a new API Endpoint which will allow you to delete Articles.

Deleting Articles

There may be times where you need to delete the data being exposed by your REST API. In order to do this, you need to expose a DELETE endpoint within your API that will take in an identifier and delete whatever is associated with that identifier.

In this section of this tutorial, you are going to be creating another endpoint which receives HTTP DELETE requests and deletes articles if they match the given Id path parameter.

Add a new function to your main.go file which we will call deleteArticle:

func deleteArticle(w http.ResponseWriter, r *http.Request) {
    // once again, we will need to parse the path parameters
    vars := mux.Vars(r)
    // we will need to extract the `id` of the article we
    // wish to delete
    id := vars["id"]

    // we then need to loop through all our articles
    for index, article := range Articles {
        // if our id path parameter matches one of our
        // articles
        if article.Id == id {
            // updates our Articles array to remove the 
            // article
            Articles = append(Articles[:index], Articles[index+1:]...)
        }
    }

}

Once again, you will need to add a route to the handleRequests function which maps to this new deleteArticle function:

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/articles", returnAllArticles)
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    // add our new DELETE endpoint here
    myRouter.HandleFunc("/article/{id}", deleteArticle).Methods("DELETE")
    myRouter.HandleFunc("/article/{id}", returnSingleArticle)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

Try sending a new HTTP DELETE request to <a href="http://localhost:10000/article/2" target="_blank">http://localhost:10000/article/2</a>. This will delete the second article within your Articles array and when you subsequently hit <a href="http://localhost:10000/articles" target="_blank">http://localhost:10000/articles</a> with a HTTP GET request, you should see it now only contains a single Article.

Note - To keep this simple, we are updating a global variable. However, we aren’t doing any checks to ensure that our code is free of race conditions. In order to make this code thread-safe, I recommend checking out my other tutorial on Go Mutexes## Updating Articles Endpoint

The final endpoint you will need to implement is the Update endpoint. This endpoint will be a HTTP PUT based endpoint and will need to take in an Id path parameter, the same way we have done for our HTTP DELETE endpoint, as well as a JSON request body.

This JSON in the body of the incoming HTTP PUT request will contain the newer version of the article that we want to update.

Challenge

Try create an updateArticle function and corresponding route in the handleRequests function. This will match to PUT requests. Once you have this, implement the updateArticle function so that it parses the HTTP request body, using the same code that you used in your createNewArticle function.

Finally, you will have to loop over the articles in your Articles array and match and subsequently update the article.

Conclusion

This example represents a very simple RESTful API written using Go. In a real project, we’d typically tie this up with a database so that we were returning real values.

What is REST API? | Restful Web Service

What is REST API? | Restful Web Service

In this post "Restful Web Service", you'll learn: What is Web services, what is API, What is REST API, How REST works and Implementation of REST API

What is REST API? | Restful Web Service

A REST API defines a set of functions to process requests and responses via HTTP protocol.

REST is used in mobile application as well as in web applications.


Web Development with Rust - 03/x: Create a REST API

Web Development with Rust - 03/x: Create a REST API

Since Rust is a static typed language with a strong compiler you won't face many of the common pitfalls about running a web service in production. Although there are still run time errors which you have to cover.

Content
  1. HTTP Requests
  2. POST/PUT/PATCH/DELETE are special
  3. The Job of a Framework
  4. Creating an API spec
  5. Crafting the API
  6. Input Validation
  7. Summary

APIs are the bread and butter of how a modern and fast-paced web environment. Frontend application, other web services and IoT devices need to be able to talk to your service. API endpoints are like doors to which you decide what comes in and in which format.

Since Rust is a static typed language with a strong compiler you won't face many of the common pitfalls about running a web service in production. Although there are still run time errors which you have to cover.

HTTP Requests

When we talk about creating an API we basically mean a web application which listens on certain paths and responds accordingly. But first things first. For two devices to be able to communicate with each other there has to be an established TCP connection.

TCP is a protocol which the two parties can use to establish a connection. After establishing this connection, you can receive and send messages to the other party. HTTP is another protocol, which is built on top of TCP, and it's defining the contents of the requests and responses.

So on the Rust side of things, TCP is implemented in the Rust core library, HTTP is not. Whatever framework you chose in the previous article they all implement HTTP and therefore are able to receive and send HTTP formatted messages.

An example GET requests for example looks like this:

GET / HTTP/1.1
Host: api.awesomerustwebapp.com
Accept-Language: en

It includes:

  • GET: the HTTP method
  • /: The path
  • HTTP/1.1: The version of the HTTP protocol
  • HOST: The host/domain of the server we want to request data from
  • Accept-Language: Which language we prefer and understand

The most common used HTTP methods are:

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
POST/PUT/PATCH/DELETE are special

We are using GET every time we browse the web. If we want to alter data however (like using POST to send data over to another server), we need to be more cautions and precise.

First, not everyone is allowed to just send a bunch of data to another server. Our API can for example say: "I just accept data from the server with the host name allowed.awesomerustapp.com.

Therefore, when you send a POST to another server, what actually happens is the CORS workflow:

We first ask the server what is allowed, where do you accept requests from and what are your accepted headers. If we fulfill all of these requirements, then we can send a POST.

Disclaimer: Not all frameworks (like rocket and tide) are implementing CORS in their core. However, in a professional environment, you handle CORS on the DevOps side of things and put it for example in your NGINX config.
The Job of a Framework

We use the hard work of other people to create web applications. Everything has to be implemented at some point, just not from you for most of the time. A framework covers the following concerns:

  • Start a web server and open a PORT
  • Listen to requests on this PORT
  • If a request comes in, look at the Path in the HTTP header
  • Route the request to the handler according to the Path
  • Help you extract the information out of the request
  • Pack the generated data and HTTP StatusCode (created from you) and form a response
  • Send the response back to the sender

The Rust web framework tide includes http-service, which provides the basic abstractions you need when working with HTTP calls. The crate http-service is built on top of hyper, which transforms TCP-Streams to valid HTTP requests and responses.

Your job is to create routes like /users/:id and add a route_handler which is a function to handle the requests on this particular path. The framework makes sure that it directs the incoming HTTP requests to this particular handler.

Creating an API spec

You have to define your resources first to get an idea what your application needs to handle and uncover relationships between them. So if you want to build a idea-up-voting site, you would have:

  • Users
  • Ideas
  • Votes

A simple spec for this scenario would look like this:

  • Users
  • POST /users
  • GET /users
  • PUT /users/:user_id
  • PATCH /users/:user_id
  • DELETE /users/:user_id
  • GET /users/:user_id

Ideas and Votes behave accordingly. A spec is helpful for two reasons:

  • It gives you guidelines not to forget a path
  • It helps to communicate to your API users what to expect

You can tools like swagger to write a full spec which also describes the structure of the data and the messages/responses for each path and route.

A more professional spec would include the return values for each route and the request and response bodies. However, the spec can be finalized once you know how your API should look like and behave. To get started, a simple list is enough.

Crafting the API

Depending on the framework you are using, your implementation will look different. You have to have the following features on your radar to look out for:

  • Creating routes for each method (like app.at("/users").post(post_users_handler))
  • Extracting information from the request (like headers, uri-params and JSON from the request body)
  • Creating responses with proper HTTP codes (200201400404 etc.)

I am using the latest version of tide for this web series. You can add it in your Cargo.toml file and use it for your web app:

[dependencies]
tide = "0.1.0"

Our first User implementation will look like this:

async fn handle_get_users(cx: Context<Database>) -> EndpointResult {
    Ok(response::json(cx.app_data().get_all()))
}

async fn handle_get_user(cx: Context<Database>) -> EndpointResult {
let id = cx.param("id").client_err()?;
if let Some(user) = cx.app_data().get(id) {
Ok(response::json(user))
} else {
Err(StatusCode::NOT_FOUND)?
}
}

async fn handle_update_user(mut cx: Context<Database>) -> EndpointResult<()> {
let user = await!(cx.body_json()).client_err()?;
let id = cx.param("id").client_err()?;

if cx.app_data().set(id, user) {
    Ok(())
} else {
    Err(StatusCode::NOT_FOUND)?
}

}

async fn handle_create_user(mut cx: Context<Database>) -> EndpointResult<String> {
let user = await!(cx.body_json()).client_err()?;
Ok(cx.app_data().insert(user).to_string())
}

async fn handle_delete_user(cx: Context<Database>) -> EndpointResult<String> {
let id = cx.param("id").client_err()?;
Ok(cx.app_data().delete(id).to_string())
}

fn main() {
// We create a new application with a basic, local database
// You can use your own implementation, or none: App::new(())
let mut app = App::new(Database::default());
app.at("/users")
.post(handle_create_user)
.get(handle_get_users);
app.at("/users/:id")
.get(handle_get_user)
.patch(handle_update_user)
.delete(handle_delete_user);

app.serve("127.0.0.1:8000").unwrap();

}

You can find the full implementation of the code in the GitHub repository to this series.

We see that we first have to create a new App

let mut app = App::new(())

add routes

app.at("/users")

and for each route add the HTTP requests we want to handle

app.at("/users").get(handle_get_users)

Each framework has a different method of extracting parameters and JSON bodies. Actix is using Extractors, rocket is using Query Guards.

With tide, you can access request parameters and bodies and database connections through Context. So when we want to update a User with a specific id, we send a PATCH to /users/:id. From there, we call the handle_update_user method.

Inside this method, we can access the id from the URI like this:

let id = cx.param("id").client_err()?;

Each framework is also handling its own way of sending responses back to the sender. Tide is using EndpointResult, rocket is using Response and actix HttpResponse.

Everything else is completely up to you. The framework might help you with session management and authentication, but you can also implement this yourself.

My suggestion is: Build the first skeleton of your app with the framework of your choice, figure out how to extract information out of requests and how to form responses. Once this is working, you can use your Rust skills to build small or big applications as you wish.

Input Validation

Your best friend in the Rust world will be serde. It will help you parse JSON and other formats, but will also allow you to serialize your data.

When we talk about input validation, we want to make sure the data we are getting has the right format. Lets say we are extracting the JSON body out of a request:

let user: User = serde_json::from_str(&request_body);

We are using serde_json here to transform a JSON-String into a Struct of our choice. So if we created this struct:

struct User {
name: String,
height: u32,
}

we want to make sure the sender is including name and height. If we just do serde_json::from_str, and the sender forgot to pass on the height, the app will panic and shut down, since we expect the response to be a user: let user: User.

We can improve the error handling like this:

let user: User = match serde_json::from_str(&request_body) {
Ok(user) => user,
Err(error) => handle_error_case(error),
};

We catch the error and call our handle_error_case method to handle it gracefully.

Summary
  1. Pick a framework of your choice
  2. rocket is nightly
  3. actix is stable
  4. tide is fostered close to the Rust Core and also works on Rust nightly
  5. Know that there is no common CORS handling (yet). Recommendation is to handle this on the DevOps side (NGINX for example)
  6. After picking a framework, spec out your resources (/users: GET, POST etc.)
  7. Figure out how the framework of your choice is handling extracting parameters and JSON from the request and how to form a response
  8. Validate your input via match and serde_json

Thanks For Visiting, Keep Visiting. If you liked this post, share it with all of your programming buddies!

Why you should learn the Rust programming language

☞ The Rust Programming Language

☞ Rust Vs. Haskell: Which Language is Best for API Design?

☞ An introduction to Web Development with Rust for Node.js Developers

☞ 7 reasons why you should learn Rust programming language in 2019

Why you should move from Node.js to Rust in 2019

☞ Rust: Building Reusable Code with Rust from Scratch

☞  Programming in Rust: the good, the bad, the ugly.

☞  An introduction to Web Development with Rust for Node.js Developers

☞ Intro to Web Development with Rust for NodeJS Developers

☞ Introducing the Rust Crash Course

3 Frameworks for Building APIs Using Rust


This post was originally published here

What is REST? What are RESTful Web Services?

What is REST? What are RESTful Web Services?

This tutorial provides an introduction to RESTful web services and goes over what REST is as well as HTTP.

REST stands for REpresentational State Transfer. It is a popular architectural approach to create your API's in today's world.

You Will Learn
  • What is REST?
  • What are the fundamentals of REST APIs?
  • How do you make use of HTTP when building REST API?
  • What is a Resource?
  • How do you identify REST API Resources?
  • What are some of the best practices in designing REST API?
What Is REST?

The acronym REST stands for REpresentational State Transfer. It was term originally coined by Roy Fielding, who was also the inventor of the HTTP protocol. The striking feature of REST services is that they want to make the best use of HTTP. Let's now have a quick overview of HTTP.

A Relook at HTTP

Let's open up the browser and visit a web page first:

And then click on one of the result pages:

Next, we can click on the link on the page we end up in:

And land upon another page:

This is how we typically browse the web.

When we browse the internet, there are a lot of things that happen behind the scenes. The following is a simplified view of what happens between the browser, and the servers running on the visited websites:

The HTTP Protocol

When you enter a URL such as https://www.google.com in the browser, a request is sent to the server on the website identified by the URL. That server then responds with a response. The important thing is the formats of these requests and responses. These formats are defined by a protocol called HTTPHyper Text Transfer Protocol.

When you type in a URL at the browser, it sends out a GET request to the identified server. The server then replies with an HTTP response that contains data in HTMLHyper Text Markup Language. The browser then takes this HTML and displays it on your screen.

Let's say you are filling in a form present on a web page with a list of details. In such a scenario when you click the Submit button, an HTTP POST request gets sent out to the server.

HTTP and RESTful Web Services

HTTP provides the base layer for building web services. Therefore, it is important to understand HTTP. Here are a few key abstractions.

Resource

A resource is a key abstraction that HTTP centers round. A resource is anything you want to expose to the outside world through your application. For instance, if we write a todo management application, instances of resources are:

  • A specific user
  • A specific todo
  • A list of todos

Resource URIs

When you develop RESTful services, you need to focus your thinking on the resources in the application. The way we identify a resource to expose, is to assign a URIUniform Resource Identifier — to it. For example:

  • The URI for the user Ranga is /user/ranga
  • The URI for all the todos belonging to Ranga is /user/Ranga/todos
  • The URI for the first todo that Ranga has is /user/Ranga/todos/1

Resource Representation

REST does not worry about how you represent your resource. It could be XML, HTML, JSON, or something entirely different! The only important thing is you clearly define your resource and perform whatever actions that are supported on it by making use of features already provided by HTTP. Examples are:

  • Create a user: POST /users
  • Delete a user: DELETE /users/1
  • Get all users: GET /users
  • Get a single user: GET /users/1
REST and Resources

A significant point to note is that with REST, you need to think about your application in terms of resources:

  • Identify what resources you want to expose to the outside world
  • Make use of the verbs already specified by HTTP to perform operations on these resources

Here is how a REST service is generally implemented:

  • Data Exchange Format: No restriction is imposed over here. JSON is a highly popular format, although other such as XML can be used as well
  • Transport: Always HTTP. REST is completely built on top of HTTP.
  • Service Definition: There is no standard to specify this, and REST is flexible. This could be a drawback in some scenarios, as it might be necessary for the consuming application to understand the request and response formats. There are widely used ones however, such as WADL (Web Application Definition Language) and Swagger.

REST focuses on resources and how effectively you perform operations on them using HTTP.

The Components of HTTP

HTTP defines the following for a request:

For the response, HTTP defines the:

HTTP Request Methods

The method used in a HTTP request indicates what action you want to perform with that request. Important examples are:

  • GET: Retrieve details of a resource
  • POST : Create a new resource
  • PUT: Update an existing resource
  • DELETE: Delete a resource

HTTP Response Status Code

A status code is always present in a HTTP response. Common examples are:

  • 200: Success
  • 404: Page not found
Summary

In this article, we had a high-level look at REST. We stressed the fact that HTTP is the building block of REST services. HTTP is a protocol that is used to define the structure of browser requests and responses. We saw that HTTP deals mainly with resources that are exposed on web servers. Resources are identified using URIs, and operations on these resources are performed using verbs defined by HTTP.

Finally, we looked at how REST services make the best use of features offered by HTTP to expose resources to the outside world. REST does not put any restrictions on the resource representation formats or on the service definition.