DEV Community

loading...

Adding user

Matija Krajnik
QA automation engineer experienced in writing GUI automated tests for Web and Mobile apps, Web REST API automated tests, load/performance tests, and developing custom testing tools from scratch.
Originally published at letscode.blog Updated on ・2 min read

We are making simple blog, so first thing we have to do is to implement some way for users to create account and log in. For now, we will just make simple struct User that will be stored in memory. Latter we will connect our application with a database so we can store our data there.

First let's create new directory inside of internal/ which we will name store/. This package will implement logic for storing all our data. Until we connect our app to database, we will use simple slice for storing users. In internal/store/ directory, let's create new file users.go:

package store

type User struct {
  Username string
  Password string
}

var Users []*User
Enter fullscreen mode Exit fullscreen mode

Remove /hello test route from router.go file and add new routes /signup and /signin:

package server

import (
  "github.com/gin-gonic/gin"
)

func setRouter() *gin.Engine {
  // Creates default gin router with Logger and Recovery middleware already attached
  router := gin.Default()

  // Enables automatic redirection if the current route can't be matched but a
  // handler for the path with (without) the trailing slash exists.
  router.RedirectTrailingSlash = true

  // Create API route group
  api := router.Group("/api")
  {
    api.POST("/signup", signUp)
    api.POST("/signin", signIn)
  }

  router.NoRoute(func(ctx *gin.Context) { ctx.JSON(http.StatusNotFound, gin.H{}) })

  return router
}
Enter fullscreen mode Exit fullscreen mode

Handlers signUp and signIn that we are passing to router will be implemented in internal/server/user.go file:

package server

import (
  "net/http"
  "rgb/internal/store"

  "github.com/gin-gonic/gin"
)

func signUp(ctx *gin.Context) {
  user := new(store.User)
  if err := ctx.Bind(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"err": err.Error()})
    return
  }
  store.Users = append(store.Users, user)
  ctx.JSON(http.StatusOK, gin.H{
    "msg": "Signed up successfully.",
    "jwt": "123456789",
  })
}

func signIn(ctx *gin.Context) {
  user := new(store.User)
  if err := ctx.Bind(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"err": err.Error()})
    return
  }
  for _, u := range store.Users {
    if u.Username == user.Username && u.Password == user.Password {
      ctx.JSON(http.StatusOK, gin.H{
        "msg": "Signed in successfully.",
        "jwt": "123456789",
      })
      return
    }
  }
  ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"err": "Sign in failed."})
}
Enter fullscreen mode Exit fullscreen mode

Let's analyze what this code does. We create new variable of type User where we will store data user provided through our frontend. After that we are calling method Bind() which will bind this form data to our User type variable. If this binding fails we immediately set error code and error message, and return from current function. If there were no errors, we will set response code to status OK and return JWT for authentication. For now we are returning only some dummy JWT so our frontend will work correctly, but latter we will change that with real JWT implementation. Now you can try to create new account, logout and login again. As you can see on pictures below, both routes are working.

SignUp route

SignIn route

Discussion (0)