Go beginner tried to create a cloud native web application using Datastore / GAE

Introduction

This is Qiita's first post. It's been half a year since I changed jobs without any experience in the IT industry, so I created a web application to output the results of Go language learning so far. In this article, I will introduce this WEB application. (Because I am a beginner, if you have any mistakes, please let me know in the comments: bow_tone1:)

Simple 4-choice question collection / learning app

1 demo

-** WEB application url ** https://workbook-292312.an.r.appspot.com/

-** WEB application repository ** https://github.com/Gompei/WorkBookApp

2 Function introduction / implementation contents

(1) Account function

-** Create ** Based on the account information obtained from the form, the data is registered with the primary key issued in the Datastore. Also, considering the password reissue function, we have implemented so that the e-mail address is not registered when logging in with an external account. By doing so, the email address in the Datastore is always unique.

handler.go


/*CreateAccount is a function that creates a user structure and stores it in the Datastore (automatically generated for the primary key).*/
func (a *App) CreateAccount(w http.ResponseWriter, r *http.Request) {
	password := r.FormValue(password)
	user := UserAccount{
		Name:            r.FormValue(userName),
		OauthFlg:        false,
		Mail:            r.FormValue(email),
		ProfileImg:      Environment.ImgUrl + "NoImage.jpg ",
		CreateTimeStamp: time.Now(),
		UpdateTimeStamp: time.Now(),
	}

 (abridgement)

	//Password is encrypted (hash value)
	hash, err := HashFiled(password)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, nil)
		a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
		return
	}
	user.HashPassword = hash

	//Issue user ID on the data store side ・ Register data as primary key
	err = a.DB.InsertUserAccount(user)
	if err != nil || SendDate["message"] != "" {
		a.WriteLog(ErrDBProcessing, err)
		a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
		return
	}
}
 (abridgement)

-** Login ** The password obtained from the form and the hash value are compared using the bcrypto package.

hadler.go


/*ValidateLoginData is a function that retrieves an entity from a Datastore using an email address as the primary key and checks if it matches the password.*/
func (a *App) ValidateLoginData(w http.ResponseWriter, r *http.Request) {
	user := UserAccount{
		Mail: r.FormValue(email),
	}
	password := r.FormValue(password)

	(abridgement)

	//The email address is registered in a unique state
	err, user := a.DB.CheckUserLogin(user, password)
	if err != nil {
		a.ReadTemplate(w, PageLogin, showLogin, SendDate)
		return
	}

	//Acquire the session and store the user information acquired from the database in the session memory.
	session, err := GetSession(r)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, nil)
		a.ReadTemplate(w, PageLogin, showLogin, SendDate)
		return
	}

	CreateSession(w, r, session, user)
(abridgement)
}

-** Update ** Since I am constantly replacing the data in the session with the map, I get the id (primary key) from it and update the account. By the way, input check is carried out on both the front side and the server side. (Same as when creating or logging in)

handler.go


/*UpdateAccount is a function that updates the data in the Datastore based on the entered user information.*/
func (a *App) UpdateAccount(w http.ResponseWriter, r *http.Request) {
	user := UserAccount{
		Name:            r.FormValue(userName),
		Mail:            r.FormValue(email),
		UpdateTimeStamp: time.Now(),
	}

	if !NameValidate(user.Name) || !MailValidate(user.Mail) {
		(abridgement)
	}
	if r.FormValue(password) != "" || PasswordValidate(password) {
		(abridgement)
	}

	id := SendDate["user"].(map[string]interface{})["id"].(int64)
	err, tmp := a.DB.UpdateUserAccount(id, user)
	if err != nil {
		a.WriteLog(ErrDBProcessing, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
	}

	session, err := GetSession(r)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	CreateSession(w, r, session, tmp)
(abridgement)
}

handler.go


/*DeleteAccount is a function that deletes the user account registered in the Datastore.*/
func (a *App) DeleteAccount(w http.ResponseWriter, r *http.Request) {
	id := SendDate["user"].(map[string]interface{})["id"].(int64)

	err := a.DB.DeleteUserAccount(id)
	if err != nil {
		a.WriteLog(ErrDBProcessing, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
	}
(abridgement)
}

handler.go


/*ExternalAuthenticationFaceBook is a function that acquires a facebook account, registers it in the database, and logs in.*/
func (a *App) ExternalAuthenticationFaceBook(w http.ResponseWriter, r *http.Request) {
	user, err := a.Facebook.FetchFacebookAccount(r)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, nil)
		a.ReadTemplate(w, PageLogin, showLogin, SendDate)
		return
	}

	user, err = a.CheckDBOauthAccount("Facebook", user)
	if err != nil {
		a.WriteLog(ErrDBProcessing, err)
		SetMessage(errorMessage, nil)
		a.ReadTemplate(w, PageLogin, showLogin, SendDate)
		return
	}
(abridgement)
}

auth.go


/*FetchHttpClient is a function that returns the client information required for external authentication*/
func FetchHttpClient(conf *oauth2.Config, r *http.Request) (*http.Client, error) {
	code := r.URL.Query()["code"]
	if code == nil {
		err := errors.New("External authentication error")
		return nil, err
	}
	ctx := context.Background()
	tok, err := conf.Exchange(ctx, code[0])
	if err != nil {
		return nil, err
	}

	return conf.Client(ctx, tok), nil
}

/*Each func Fetch~Is a function that gets the user account information of each application from the url, refills it into a structure, and returns it.*/
func (f *FacebookClient) FetchFacebookAccount(r *http.Request) (UserAccount, error) {
	client, err := FetchHttpClient(f.Conf, r)
	if err != nil {
		return UserAccount{}, err
	}

(abridgement)

	res, err := session.Get("/me?fields=id,name,email,picture", nil)
	if err != nil {
		return UserAccount{}, err
	}

    //Cast to ini64 type so that it can be registered in the Datastore
	id := res["id"]
	userId, err := strconv.ParseInt(id.(string), 10, 64)
	if err != nil {
		return UserAccount{}, err
	}

(abridgement)

	user := UserAccount{
		FacebookId:      userId,
		OauthFlg:        true,
		Name:            res["name"].(string),
		ProfileImg:      pictureUrl,
		CreateTimeStamp: time.Now(),
		UpdateTimeStamp: time.Now(),
	}

	return user, nil
}

(2) Password reissue function

-** JWT issuance / verification / email transmission ** This article explains JWT in an easy-to-understand manner. ↓ I have summarized what I researched about JWT. Using this technology, JWT sends an email set in the query parameter to the email address registered in Datastoren. The contents of JWT only include the user id, creation time stamp, and expiration date so that the user can see it. If JWT has been modified, an error screen will be displayed. By the way, mail transmission is also implemented in Go.

handler.go


/*SendReissueEmail is a function that sends a password reissue email to an already registered email address.*/
func (a *App) SendReissueEmail(w http.ResponseWriter, r *http.Request) {
	searchMail := r.FormValue(email)

(abridgement)

	//Return account based on email address
	id, err := a.DB.SelectUserAccountMail(searchMail)
	if err != nil {
		(abridgement)
	}

	//Send an email to the email address of the searched user
	err = gmailSend(searchMail, id)
	if err != nil {
		(abridgement)
	}

(abridgement)
}

func (a *App) ShowRecoverPasswordPage(w http.ResponseWriter, r *http.Request) {
	/*The contents of the token
1 User ID
2 Creation time stamp
3 Expiration date
	*/
	tokenString := r.URL.Query().Get("token")
	if tokenString == "" {
		SetMessage(errorTokenMessage, nil)
		a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
		return
	}

	claims := jwt.MapClaims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(Environment.JwtSing), nil
	})

	if err != nil {
		(abridgement)
	}

	//Do not enter user ID in session
	SendDate["id"] = claims["id"]
(abridgement)
}

mail.go


/*body is a function that creates the content of an email*/
func (m mail) body() string {
	return "To: " + m.to + "\r\n" +
		"Subject: " + m.sub + "\r\n\r\n" +
		m.msg + "\r\n"
}

/*gmailSend is a function that sends Gmail mail to the argument mail address*/
func gmailSend(send string, id int64) error {
	token, err := CreateToken(id)
	if err != nil {
		return err
	}

	m := mail{
		from:     Environment.MailAddress,
		username: Environment.MailName,
		password: Environment.MailPassWord,
		to:       send,
		sub:      "Workbook |Password reset",
	}
	m.msg = "To complete the reset of the workbook login password" + "\r\n" +
		"Please access the URL below and set a new password." + "\r\n" +
		"https://workbook-292312.an.r.appspot.com/login/recover-password/page?token=" + token + "\r\n" +
		"* The password reset URL is valid for one hour after it is issued. Please note that you will not be able to reset from the above URL after the expiration date." + "\r\n" +
		"―――――――――――――――――――――――――――――――――――" + "\r\n" +
		"This message is a login password reset procedure from the workbook" + "\r\n" +
		"It has been sent to the customer who made it." + "\r\n" +
		"If you don't know, someone else registered your email address incorrectly." + "\r\n" +
		"There is a possibility." + "\r\n" +
		"In that case, sorry to trouble you." + Environment.MailAddress + "Please contact us." + "\r\n" +
		"―――――――――――――――――――――――――――――――――――"

	smtpSvr := "smtp.gmail.com:587"
	auth := smtp.PlainAuth("", m.username, m.password, "smtp.gmail.com")
	if err := smtp.SendMail(smtpSvr, auth, m.from, []string{m.to}, []byte(m.body())); err != nil {
		return err
	}
	return nil
}

auth.go


/*CreateToken is a function that issues a JWT with a user ID for password reissue.*/
func CreateToken(id int64) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)

	claims := token.Claims.(jwt.MapClaims)
	claims["id"] = strconv.FormatInt(id, 10)
	claims["iat"] = time.Now()
	claims["exp"] = time.Now().Add(time.Hour * 1).Unix()

	tokenString, err := token.SignedString([]byte(Environment.JwtSing))

	return tokenString, err
}

(3) 4-choice question collection function

-** Create ** You can create up to 20 questions. For questions, the data is held in a slice of the structure. Also, when getting image data from form at the same time, it is necessary to use ParseMultipartForm method.

entity.go


//Problem collection (user ID+title+Question array)
type WorkbookContent struct {
	UserId   int64
	BookId   int64
	ShareId  int64
	Author   string
	Category string
	Title    string
	Image    string
	Contents        []Content
	CreateTimeStamp time.Time
	UpdateTimeStamp time.Time
}

//question
type Content struct {
	ProblemNumber    string
	ProblemStatement string
	Choice1          string
	Choice2          string
	Choice3          string
	Choice4          string
	Answer           string
	Explanation      string
}

handler.go


/*CreateWorkBook is a function that registers the problem collection information obtained from the form in the Datastore based on the bookId.*/
func (a *App) CreateWorkBook(w http.ResponseWriter, r *http.Request) {
	err := r.ParseMultipartForm(32 << 20)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	mf := r.MultipartForm.Value
	if len(mf) == 0 {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	var UpFileName string
	file, fileHeader, err := r.FormFile(image)
	if err != nil {
		a.WriteLog(ErrLog, err)
		UpFileName = "NoImage.jpg "
	} else {
		UpFileName = fileHeader.Filename
	}

	workbook := WorkbookContent{
		UserId:          SendDate["user"].(map[string]interface{})["id"].(int64),
		Author:          SendDate["user"].(map[string]interface{})["name"].(string),
		Image:           Environment.ImgUrl + UpFileName,
		CreateTimeStamp: time.Now(),
		UpdateTimeStamp: time.Now(),
	}
	workbook.Title = mf["title"][0]
	workbook.Category = mf["category"][0]

(abridgement)

	workbook.Contents = make([]Content, 0)
	for i := 1; i <= total; i++ {
		s := strconv.Itoa(i)
		content := Content{
			ProblemNumber:    mf["problem"+s][0],
			ProblemStatement: mf["statement"+s][0],
			Choice1:          mf["choices"+s+"-1"][0],
			Choice2:          mf["choices"+s+"-2"][0],
			Choice3:          mf["choices"+s+"-3"][0],
			Choice4:          mf["choices"+s+"-4"][0],
			Answer:           mf["answer"+s][0],
			Explanation:      mf["commentary"+s][0],
		}

		workbook.Contents = append(workbook.Contents, content)
	}

(abridgement)

	err = a.DB.InsertWorkbook(workbook)
	if err != nil {
		a.WriteLog(ErrDBProcessing, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}
(abridgement)
}

handler.go


/*DeleteWorkBook is a function that deletes the data corresponding to the bookId obtained from the form.*/
func (a *App) DeleteWorkBook(w http.ResponseWriter, r *http.Request) {
	id := r.FormValue(bookId)
	if id == "" {
		SetMessage("The deletion could not be executed.", "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	bookId, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	userId := SendDate["user"].(map[string]interface{})["id"].(int64)

	err = a.DB.DeleteWorkBook(bookId, userId)
	if err != nil {
		a.WriteLog(ErrSTProcessing, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}
(abridgement)
}

-** Learning ** Regarding the learning screen, the display / non-display processing is implemented in JavaScript without page transition for each question.

handler.go


/*CheckAnswerWorkBook is a function that answers based on the value obtained from form and creates the result with map.*/
func (a *App) CheckAnswerWorkBook(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	questionTotal := r.FormValue("question-total")
	total, err := strconv.Atoi(questionTotal)
	if err != nil {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	//Extract only the problem number(As long as you know the checked value, you can answer.)
	//Go1.12+Use the mechanism that the map key is automatically sorted from
	answer := make(map[string]string)
	choice := make(map[string]string)
	for k, v := range r.Form {
		if v[0] == "on" {
			checked := strings.Replace(k, "check", "", 1)[0:1]
			answer[checked] = k
			choice[checked] = k
		}
	}

	//When the user forcibly submits without selecting
	if total != len(choice) {
		a.WriteLog(ErrLog, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	for i := 1; i <= total; i++ {
		s := strconv.Itoa(i)
		//If the answer is incorrect, remove the value from the map
		if answer[s][7:] != r.FormValue("Answer"+s) {
			delete(answer, s)
		}
	}

	bookId := r.FormValue(bookId)

	err, book := a.DB.SelectWorkbook(bookId)
	if err != nil {
		a.WriteLog(ErrDBProcessing, err)
		SetMessage(errorMessage, "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}
	kind := r.FormValue("kind")

	SendDate["learningBook"] = book
	SendDate["checkBook"] = answer
	SendDate["choiceBook"] = choice
	SendDate["kind"] = kind
(abridgement)
}

handler.go


/*Upload WorkBook is a share in the Datastore_A collection of questions obtained from the form in the book (kind)(After searching based on bookId)Function to register*/
func (a *App) UploadWorkBook(w http.ResponseWriter, r *http.Request) {
	bookId := r.FormValue(bookId)
	if bookId == "" {
		SetMessage("Sharing could not be performed.", "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}

	err := a.DB.InsertShareWorkbook(bookId)
	if err != nil {
		a.WriteLog(ErrSTProcessing, err)
		SetMessage("Already uploaded", "block")
		a.ReadTemplate(w, PageHome, showHome, SendDate)
		return
	}
(abridgement)
}

database.go


/*InsertShareWorkbook shares the information obtained based on the bookID._function to register in book Kind*/
func (c *Client) InsertShareWorkbook(bookId string) error {
	ctx := context.Background()

	err, book := c.SelectWorkbook(bookId)
	if err != nil {
		return err
	}

	var workbook api.WorkbookContent
	query := datastore.NewQuery("share_workbook").Filter("BookId =", book.BookId)
	it := c.DataStore.Run(ctx, query)
	_, err = it.Next(&workbook)
	if err == nil {
		return errors.New("It has already been uploaded.")
	}

	parentKey := datastore.IDKey("user_account", book.UserId, nil)
	childKey := datastore.IncompleteKey("share_workbook", parentKey)
	childKey.ID = book.BookId

	_, err = c.DataStore.Put(ctx, childKey, &book)
	if err != nil {
		return err
	}
	return nil
}

workbook_share.html


<div class="article-cta workbook_learning_start">
  {{if checkOwner $book.UserId}}
    <a href="#" class="btn btn-danger" data-toggle="modal"data-target="#modal-danger">Delete</a>
  {{end}}
    <a href="#" class="btn btn-primary" data-toggle="modal" data-target="#modal-default">start</a>
  <input type="hidden" class="book-id" name="" value="{{$book.BookId}}">
</div>

data.go


FuncMap = template.FuncMap{
		//Used to display a deleteable button by the author on a shared page
		"checkOwner": func(checkId interface{}) bool {
			userId := SendDate["user"].(map[string]interface{})["id"].(int64)
			checkUserId := checkId.(int64)
			if checkUserId == userId {
				return true
			}
			return false
		},
	}

3 Selection technology

(1)Go Since Java was originally created, I chose Go as the next language to learn because it allows similar development. At the beginning of development, I didn't understand how to use the structure / interface, so I implemented it as follows, but I think I was able to write Go-like code at the end of development.

database.go(initial)


//NewClient is a function that creates a Datastore client
func NewClient(ctx context.Context) (*datastore.Client, bool) {
	var client *datastore.Client
	client, err := datastore.NewClient(ctx, project_id)
	if err != nil {
		return nil, false
	}
	return client, true
}

//CheckUserLogin is a function that compares email addresses and passwords and returns boolean and user account information.
func CheckUserLogin(user UserAccount, password string) (bool, UserAccount) {
	ctx := context.Background()
	client, flg := NewClient(ctx)
	if flg == false {
		return false, user
	}
	defer client.Close()
(abridgement)
}

datbase.go(Current)


/*NewClient is a function that creates a Datastore client*/
func NewClient(ctx context.Context) (*Client, error) {
	client, err := datastore.NewClient(ctx, api.Environment.ProjectId)
	if err != nil {
		return nil, err
	}
	return &Client{
		DataStore: client,
	}, nil
}

/*CheckUserLogin is a function that queries based on an email address and returns a user account when an error and password match.*/
func (c *Client) CheckUserLogin(user api.UserAccount, password string) (error, api.UserAccount) {
	ctx := context.Background()
(abridgement)
}

DB, ST, LD are interfaces and implement the methods required for Datastore and Cloud Storage. Also, the contents of handler.go are mostly App (structure) methods.

handler.go(Current)


/*NewApp is DB,Storage,Logging,A function that combines external account connection information into one*/
func NewApp(d Repository, s Storage, l Logging, google GoogleClient, facebook FacebookClient, github GithubClient) *App {
	return &App{
		DB:       d,
		ST:       s,
		LD:       l,
		Google:   google,
		Facebook: facebook,
		Github:   github,
	}
}

main.go(Current)


/*run is a function that creates each client and starts the server based on the registered handler function.*/
func run() error {
	ctx := context.Background()

	d, err := database.NewClient(ctx)
	if err != nil {
		return err
	}

	s, err := api.NewStorageClient(ctx)
	if err != nil {
		return err
	}

	l, err := api.NewLoggingClient(ctx)
	if err != nil {
		return err
	}

	google := api.NewGoogleClient()
	facebook := api.NewFacebookClient()
	github := api.NewGithubClient()

	app := api.NewApp(d, s, l, google, facebook, github)
	router := api.Route(app)
(abridgement)
}

entity.go(Current)


//DB repository
type Repository interface {
	CheckUserLogin(user UserAccount, password string) (error, UserAccount)
	InsertUserAccount(user UserAccount) error
	(abridgement)
}

//Storage and logging repositories
type Client struct {
	CloudStorage *storage.Client
	Logging      *logging.Client
}

//Logging connection information
type Logging interface {
	WriteStackDriverLog(appErr *ApplicationError)
}

//Storage connection information
type Storage interface {
	UploadImg(file multipart.File, fileHeader *multipart.FileHeader) error
}

//Application information
type App struct {
	DB       Repository
	ST       Storage
	LD       Logging
	Google   GoogleClient
	Facebook FacebookClient
	Github   GithubClient
}

(2)Datastore This is because we followed the storage option flow provided by GCP. I also wanted to try NoSQL. (Currently this image is not on the official website ... right?) fcfa36a8d7e7ea035db3343dc7c65de5.png When actually using it, there are some inconveniences such as partial match search (LIKE) is not provided, but I feel that Datastore is more convenient than SQL for development that uses only simple data structures. .. (Next, I will choose Cloud SQL ...)

If you would like to know more about the database provided by GCP, please click here. ↓ Google Cloud Database

(3)CloudStorage This also follows GCP best practices. You can easily upload files and it is very convenient, so I would like to continue using it in the future.

(4)GoogleAppEngine This is also because we follow GCP best practices. (The reason for choosing GCP is because I like Google ʕ◔ϖ◔ʔ. Readers should think carefully and choose a cloud environment) I'm deploying to GAE SE because Cloud Functions can't deploy the entire application. GAE features the ability to split requests by version and the free use of HTTPS. (There are more features!) 32245.max-800x800.png

Please refer to here for the application execution environment selection criteria ↓ Hosting apps on Google Cloud Which should I use, GCE or GAE? GCP operation diagnosis for those who say GCP --GAE, GCE, GKE decision tree

(5) CloudBuild / CircleCI

Since it was developed on GCP, the main will be Cloud Build. .Circleci / config.yml has been created so that it can be tested and deployed on CircleCI. This time, I chose CloudBuild, which makes it easy to create configuration files, but I would like to utilize CircleCI in the future. I also recommend this article because it explains the benefits of CloudBuild in an easy-to-understand manner. ↓ If you're using GCP, why not Cloud Build?

(6)Docker I'm developing on windows, but I've containerized it so that it can be run on other than windows. It is very convenient to be able to run the application independently of the environment with just Dockerfile and docker-compose.yml. (I want you to incorporate it at the current site ...)

4 Points I did my best

--Utilization of structures and interfaces I mentioned this in the technology selection, so I won't write it in detail, but I think I was able to write Go-like code.

--Building a CI / CD pipeline What is CI / CD? Since it was developed from such a situation, it took time to build it, but I managed to automate it. The test code is still far from complete, but the benefits of automated deployment were huge. Next, I would like to write more test code and develop it so that I can make the most of CI / CD. (I want you to incorporate it at the current site ...)

Impressions

What are Go, GCP, Docker, CI / CD? It was very good that I managed to complete it in the state from the beginning and had the experience of making something. In the future, in order to improve the basic skills of programming, I would like to take on the challenge of learning algorithms and developing levels that can actually be provided as services.

Articles and references that I used as a reference

-[Go / Development environment construction] [Use Goland Winter 2019](https://qiita.com/junpayment/items/f66e85af6a854ca2a296#go%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9% E3% 83% 88% E3% 83% BC% E3% 83% AB% E3% 81% 97% E3% 81% BE% E3% 81% 97% E3% 82% 87% E3% 81% 86)

-[Go / Grammar] [Go] Basic Grammar ① (Basic)

-[Go / Package Management] Go seems to have a standard directory structure. [Go go lang] import your own package

-[Go / Session Management] Sample user login function using gorilla / mux, gorilla / context, gorilla / sessions in Go language

-[Go / External account authentication] Until you get an email address by Oauth authentication of Google, Twitter, Facebook in Go language

-[Go / JWT] JWT authentication implementation hands-on understood in Go language

-[Qiita article writing reference] [Python + Flask] Simple book management application using Web API [Vue.js, Elasticsearch]

Recommended Posts

Go beginner tried to create a cloud native web application using Datastore / GAE
(Python) Try to develop a web application using Django
CTF beginner tried to build a problem server (web) [Problem]
Creating a web application using Flask ②
Creating a web application using Flask ①
Make Flask a Cloud Native application
Creating a web application using Flask ③
Creating a web application using Flask ④
I tried to make a todo application using bottle with python
How to deploy a web application on Alibaba Cloud as a freelancer
I tried to create a linebot (implementation)
I tried to create a linebot (preparation)
If you were to create a TODO application (distributed) using only Python-extension 1
I tried to make a Web API
I tried to create a sample to access Salesforce using Python and Bottle
I tried benchmarking a web application framework
How to build an application from the cloud using the Django web framework
I want to make a web application using React and Python flask
[ES Lab] I tried to develop a WEB application with Python and Flask ②
I want to create a web application that uses League of Legends data ①
Create a web app that converts PDF to text using Flask and PyPDF2
A machine learning beginner tried to create a sheltie judgment AI in one day
If you want to create a Word Cloud.
How to deploy a Streamlit application to GCP (GAE)
Create a web map using Python and GDAL
Steps to develop a web application in Python
Create a web server in Go language (net/http) (2)
I tried to make a ○ ✕ game using TensorFlow
Create a shogi game record management application using Django 5 ~ Pass DB data to Template ~
I tried to create a table only with Django
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 7-
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 1-
How to deploy a Django application on Alibaba Cloud
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 2-
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 0-
Scraping your Qiita articles to create a word cloud
[Go] How to create a custom error for Sentry
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 5-
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 6-
[Go + Gin] I tried to build a Docker environment
How to deploy a Go application to an ECS instance
Create a web server in Go language (net / http) (1)
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 4-
To myself as a Django beginner (1) --Create a project app--
To myself as a Django beginner (4) --Create a memo app--
[Beginner] [Python / Django] A fledgling web engineer tried a Django tutorial-Part 3-
I tried to draw a configuration diagram using Diagrams
I tried to make a motion detection surveillance camera with OpenCV using a WEB camera with Raspberry Pi
Web application using Bottle (1)
Create a web application that recognizes numbers with a neural network
[Go language] Try to create a uselessly multi-threaded line counter
Create a game to control puzzle & dragons drops using pygame
I tried to automatically create a report with Markov chain
I tried to get Web information using "Requests" and "lxml"
I made a tool to create a word cloud from wikipedia
I tried to automate [a certain task] using Raspberry Pi
I tried to create a bot for PES event notification
I tried to make a stopwatch using tkinter in python
A python beginner tried to intern at an IT company
I tried to make a simple text editor using PyQt
Note that I was addicted to accessing the DB with Python's mysql.connector using a web application.