Writing a RESTful API on Golnag


INhello there! We continue the series of articles devoted to the Golang programming language, which our freelance author wrote in anticipation of the start of the course.

Last time we looked at how Golang feels within the framework of the functional paradigm – and I must say, he did not feel too confident (most of the possibilities for using the functional paradigm had to be emulated). Today we will continue to program in Go, only now we will try it in what it should be good at – in creating a REST API service in which you can receive data for a specific request. In the first part, I will try to explain the simpler part, in which we send the data recorded in our Golang file, in which we will transfer a regular object, for example, articles with a title, description and content. In the second part of the article, we will complicate this process: we will start the MySQL database, connect with it, receive the data, transform it into JSON, and successfully render it by the GET request.

So what is a REST API? In short, this is a set of remote standard methods that return various kinds of data (and not necessarily this will be the application at the front, in Android applications this technology is widely used). However, REST, like BEM at the front, is needed in part to ensure that every member of the team, even if he is the first day at work, immediately understands how your requests are arranged, and not invent your own bicycle. In other words, REST is an architecture that acts as a kind of standard that is no coincidence mentioned in so many offers from companies.

I do not want to give further definitions of REST in our article, they are so simple everywhere, so for a quick introduction you can read here. And also I really liked this introduction. Everything is brief here, on the case and with many examples. Well, we are starting to make our application.

So, the main file we can put other imports into is always called main.go. The rest should only be exported and called there. Total, in any directory we create
main.go (I really hope that you have already downloaded and installed golang from here) and there we begin to create our application:

package main

import (
"encoding / json" // For working with json
"fmt" // library for outputting data in the terminal
"log" // for logging
"net / http" // and for handling http requests

"github.com/gorilla/mux" // to make routing easier
)
   

So, a few details about each of the packages. encoding / json – a package from the standard library in which you can encode and decode data in JSON format according to the RFC7159 standard. fmt – A simple library for input / output (I / O). log – a library for logging, it is necessary for simple logging of errors and messages in our web application. net http – built-in package for working with the protocol http. Gorilla is without a doubt one of the most popular web application frameworks on Go. It will allow us to describe our requests and methods much easier. httpby which they will be sent.

If you only recently installed Go, then be more careful with the ctrl + s keyboard shortcuts in VS Code – in this case, the go formatter loves to remove packages and variables if they are not used.

So, in this language there are no classes. But we can create with you struct, in which we describe, as in the Design pattern of the Constructor, exactly how our article object should look:

type Article struct {
Title string `json:" Title "`
ID int `json: id` // field of our struct is no coincidence with a capital letter. Initially, they would not be exported
Desc string `json:" Desc "`
Content string `json:" Content "`
}

type Articles []Article // here we create an array of our articles. 

Next we will create a function that will produce, for example, the home page on request. In the future, I confess, it will not be of much use to it – but it will be a wonderful illustration of the standard function that processes request – response inquiries.

		func homePage (w http.ResponseWriter, r * http.Request) {
fmt.Fprint (w, "

Elvis left the building

") // I hope the w and r flags are already familiar to our reader }

Okay, great. But we need a function that will call this function on request and start the server. In addition, we were not prevented by a package with the functions of a dispatcher who would do all the dirty work for us … Well, we already have gorilla / mux:

func handleRequests () {
myRouter: = mux.NewRouter (). StrictSlash (true) // cheers
myRouter.HandleFunc ("/", homePage) // cheers the router will do everything for us and out of the box any methods
Println (http.ListenAndServe (": 8801", myRouter)) // start our server
}

What is left for us to do to make the minimum server core work for us? That's right, in Go without function main nowhere:

func main () {
handleRequests ()
}
 

If you urgently need to lift this heavy duty server and see if everything works, enter the console:

 	 	go run main.go
 

Our server should start, and at http: // localhost: 8810 / you can see:

Okay, but nothing so interesting so far. It's time for a function that will give our articles to json:

func allArticles (w http.ResponseWriter, r * http.Request) {
w.Header (). Set ("Content-Type", "text / html; charset = utf-8") // data can be sent without headers, of course, but nobody will read them then
w.Header (). Set ("Access-Control-Allow-Origin", "*") // allow reading from everywhere
articles: = Articles {
Article {Title: "On Dolphins", ID: 1, Desc: "Cool dolphins", Content: "Dolphins are aquatic mammals of the cetacean order."},
Article {Title: "About Penguins", ID: 2, Desc: "Cool Penguins", Content: "Penguins, or penguins, are a family of flightless sea birds, the only modern in the penguin-like squad."},
} // which article on the hub does without an excursion into wildlife. And if on business, make new Article objects

fmt.Println ("Endpoint Hit: All articles") // confirmation that everything works for us.
json.NewEncoder (w) .Encode (articles) // encode our content in JSON
}

Wow, articles with us! It remains only to give them on request. Add to our handleRequests () new line:

myRouter.HandleFunc ("/ articles", allArticles) .Methods ("GET") // we specialize in reading

Now, if we restart our server, we can receive our data, for example, by visiting the page http: // localhost: 8810 / articles. However, it would be cool, for example, to immediately send data. For example, you can make a request from our web application that creates a page in JS and get the data:

async function f () {
    let response = await fetch ('http: // localhost: 8801 / articles');
    if (response.ok) {
        let json = await response.json ();
        let value = JSON.parse (json);
        console.log (typeof (value));
        console.log (value.length); // debugging joys to make sure in what form it came.
        for (let i = 1; i <value.length; i ++) {
            draw (value[i]); // draw is a function that draws our articles. I don’t think that you can’t write such a thing (and it’s better of course immediately on React)
        }
    } else {
console.log ("An HTTP error occurred:" + response.status);
console.warn ("don't test this on prod")
    }

}
f (); // press F

Of course, a normal person would download Postman and see how his request works. But in a live application, of course, a hundred times more interesting.

Okay However, now our application is strikingly dull, and there is no sense in it – we could write all these articles in fixtures any thread with the same success, and no server would be needed. It's time to connect our beauty with databases. The database will be MySQL. This booklet seemed quite useful to me, although it seems best to me to immediately check the documentation of the package that is responsible for connecting to the database. So, add the package and driver to our imports:

import (
"database / sql" // main plugin for using sql

_ "github.com/go-sql-driver/mysql" // driver for our sql to work
)

By the way, I forgot to mention. Non-standard library packages must first be installed globally using the go get -u "package name" command.
Okay, and of course, our function, which is responsible for the transfer of all articles, will change now:

func allArticles (w http.ResponseWriter, r * http.Request) {
w.Header (). Set ("Content-Type", "text / html; charset = utf-8")
w.Header (). Set ("Access-Control-Allow-Origin", "*")
sql: = "SELECT * from articles" // select all columns from our articles table
articles: = rows
json.NewEncoder (w) .Encode (articles)
}

In the previous code snippet, we used the getJSON magic function to get json. Do you want to know what is inside her? If anything, you yourself asked, I have nothing to do with:



func getJSON (sqlString string) (string, error) {
db, err: = sql.Open ("mysql", "pavel: @tcp (127.0.0.1lla306) / testdb") // without password, username is pavel, database name is testdb
if err! = nil {
log.Println (err.Error ())
}

rows, err: = db.Query (sqlString) // make a request, it comes with an argument
if err! = nil {
return "", err
}
columns, err: = rows.Columns ()
if err! = nil {
return "", err
}
count: = len (columns)
tableData: = make ([]map[string]interface {}, 0) // yes, we read into empty interfaces. And it’s not worth doing this, it’s bad practice. But our method is universal, not universal it is better to peep here
	values: = make ([]interface {}, count)
valuePtrs: = make ([]interface {}, count)
for rows.Next () {
for i: = 0; i <count; i ++ {
valuePtrs[i] = & values[i]
		}
rows.Scan (valuePtrs ...)
entry: = make (map[string]interface {})
for i, col: = range columns {
var v interface {}
val: = values[i]
			b, ok: = val. ([]byte)
if ok {
v = string (b)
} else {
v = val
}
entry[col] = v
}
tableData = append (tableData, entry) // there are a lot of all kinds of interfaces, so I’ll just say that we make a table into which we will translate into JSON
}
jsonData, err: = json.Marshal (tableData)
if err! = nil {
return "", err
}
return string (jsonData), nil
}

All cool, now it remains only to specialize our request in handleRequest ():

myRouter.HandleFunc ("/ articles", allArticles) .Methods ("GET")

Fine! Now just run, for example, your MySQLWorkBranch (yes yes, I use interfaces, not the console) and create a diagram there testdbwhere you can create a table articles with columns id, Title, Desc, Content – and after filling them with some kind of content (well, let’s find it yourself elementary) you can see how your system will work. I deliberately simplified the request a bit to make the material easier. But now you can change the data table to a more real one and see how your REST API works with other requests. How to write and compile using go build and deploy – everyone, for example, praises Google Cloud (this is not advertising) for a very small fee (there is also a free quota), or heroku (there is also a free tariff, but in my opinion it’s not so convenient).
That's all. Hopefully, despite the frightening part of processing a database with many interfaces, the language has risen a bit in your eyes. And despite some lexical poverty, we are waiting for GO 2 and we hope that in the end we will get a compiled, fast and convenient, lexically rich programming language with many fields of application.

So, according to tradition, useful links:

  1. Golang tour. Without a doubt, one of the best ways to learn Go now if you haven't ordered Head First Go in English yet
  2. Go for examples. A very useful reference if you want to try something out of the language features – for example, working with JSON, coroutines, and the like
  3. The part of the official documentation that will guide you through all the necessary steps to a working go application on Heroku
  4. A good guide on how to configure custom errors in Go

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *