Play Microservices: Api-gateway service

khaledhosseini

Khaled Hosseini

Posted on July 11, 2023

Play Microservices: Api-gateway service

This is the 6th part of a series of articles under the name "Play Microservices". Links to other parts:
Part 1: Play Microservices: Bird's eye view
Part 2: Play Microservices: Authentication
Part 3: Play Microservices: Scheduler service
Part 4: Play Microservices: Email service
Part 5: Play Microservices: Report service
Part 6: You are here
Part 7: Play Microservices: Client service
Part 8: Play Microservices: Integration via docker-compose
Part 9: Play Microservices: Security

The source code for the project can be found here:


Contents:

  • Summary
  • Tools
  • Docker dev environment
  • API mock service: Gripmock
  • Api-gateway service: Golang
  • To do

  • Summary

In the previous stages, we successfully developed a set of services consisting of the auth, scheduler, email, and report services. Our current objective is to establish a gateway service that acts as a single entry point for clients to access these individual services. To ensure smooth development and testing, we have included an additional service in our development environment solely for debugging purposes. As we independently develop the API gateway, the remaining services are temporarily unavailable. To overcome this limitation, we create mock implementations to simulate the behavior of the unavailable services during the development of the API gateway.

Summary

At the end, the project directory structure will appear as follows:

Folder structure


  • Tools

The tools required In the host machine:

The tools and technologies that we will use Inside containers for each service:

  • Gripmock service: Gripmock
  • Api-gateway service:
    • Golang : programming language
    • gRPC-GO: gRPC framework for golang
    • Gin: Is a web framework written in Golang
    • gin-swagger for our rest api documentation.

  • Docker dev environment

Development inside Docker containers can provide several benefits such as consistent environments, isolated dependencies, and improved collaboration. By using Docker, development workflows can be containerized and shared with team members, allowing for consistent deployments across different machines and platforms. Developers can easily switch between different versions of dependencies and libraries without worrying about conflicts.

dev container

When developing inside a Docker container, you only need to install Docker, Visual Studio Code, and the Dev Containers and Dockerextensions on VS Code. Then you can run a container using Docker and map a host folder to a folder inside the container, then attach VSCode to the running container and start coding, and all changes will be reflected in the host folder. If you remove the images and containers, you can easily start again by recreating the container using the Dockerfile and copying the contents from the host folder to the container folder. However, it's important to note that in this case, any tools required inside the container will need to be downloaded again. Under the hood, When attaching VSCode to a running container, Visual Studio code install and run a special server inside the container which handle the sync of changes between the container and the host machine.


  • API mock service: Gripmock

We have 3 services in our microservice application that the api-gateway service will communicate with them. Authentication service, Job scheduler service and report service. These services are all gRPC services. During the development of api-gateway we do not have access to these services, therefore we mock their behavior. For this purpose we use gripmock. Instructions on how these service works can be found here. For gripmock configuration, we need .proto files for our gRPC services. Then we add stubs to the stub service. For this we can either define our stubs inside a json file (static stubbing) or add stubbing on the fly with a simple REST API (HTTP stub server is running on port :4771). Lets begin.

  • Create a folder for the project and choose a name for it (such as 'microservice'). Then create a folder named api-gateway. This folder is the root directory of the current project. You can then open the root folder in VS Code by right-clicking on the folder and selecting 'Open with Code'.
  • Inside the root directory create a folder with the name gripmock, then create the following files inside.
  • Create a Dockerfile and set content to FROM tkpd/gripmock:v1.12.1
  • Copy all .proto files from here to the gripmock directory.
  • Create a file named .env in the root directory and add the following content:


GRIPMOCK_ADMIN_PORT=4771
GRIPMOCK_GRPC_PORT=4770
API_GATEWAY_PORT=5010


Enter fullscreen mode Exit fullscreen mode
  • Inside gripmock folder create a folder named stubs. Inside these folder we are going to create json stubs for our services. you can copy the files from here. For example, for report.json we have the following content. Our grpc service name is ReportService and the method name is ListReports. For input, we define some equals critria, that is when we received a request for service ReportService and method ListReports and the values for filter, page and size are 1, 1, and 10 respectively, then return the following output. For more information see here.


[
  {
    "service": "ReportService",
    "method": "ListReports",
    "input": {
      "equals": {
        "filter": 1,
        "page": 1,
        "size": 10
      }
    },
    "output": {
      "data": "..."
    }
  }
]


Enter fullscreen mode Exit fullscreen mode
  • Inside root directory create a file named docker-compose.yml and add the following content.


version: '3'
services:
  gripmock:
    build:
      context: ./gripmock
      dockerfile: Dockerfile
    container_name: gripmock
    ports:
      - ${GRIPMOCK_GRPC_PORT}:${GRIPMOCK_GRPC_PORT}
      - ${GRIPMOCK_ADMIN_PORT}:${GRIPMOCK_ADMIN_PORT}
    volumes:
      - ./gripmock:/mock
    # we use admin to manage grpc servers. we connect to grpc servers using our clinets(from code).
    command: > 
      --admin-listen=0.0.0.0 
      --admin-port=${GRIPMOCK_ADMIN_PORT} 
      --grpc-listen=0.0.0.0 
      --grpc-port=${GRIPMOCK_GRPC_PORT} 
      --stub=/mock/stub /mock/job.proto /mock/report.proto /mock/user.proto


Enter fullscreen mode Exit fullscreen mode
  • Run docker-compose up -d --build. Now go to http://localhost:4771/. You can see the available mock grpc servers. These services acts as a real grpc servers. We connect them from our code and query them.

Gripmock

  • Run docker-compose down

  • Api-gateway service: Golang

We aim to develop an API gateway service that acts as a REST API server and a gRPC client. This service will receive REST requests from client applications and connect to various gRPC servers to fetch the required results. The API gateway acts as an intermediary that facilitates communication between the client and the gRPC servers. A summary of what we are going to do: First, we will import the .proto files for the gRPC servers and compile them into the Go programming language using protoc. This step ensures that we have the necessary client libraries to interact with the gRPC services.
Next, we'll prepare gRPC clients for each individual gRPC service. These clients will allow our API gateway to establish connections and communicate with the respective gRPC servers. Finally, we will run our REST API service, which will expose multiple endpoints for client applications to interact with. Within each endpoint, we will utilize a specific gRPC client to handle the requests and retrieve data from the corresponding gRPC server.

  • Create a folder named api-gateway-service inside api-gateway folder.
  • Create a Dockerfile inside api-gateway-service and set the contents to


FROM golang:1.19

ENV PROTOC_VERSION=23.3
ENV PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
RUN apt-get update && apt-get install -y unzip
RUN curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/$PROTOC_ZIP \
    && unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
    && unzip -o $PROTOC_ZIP -d /usr/local 'include/*' \ 
    && rm -f $PROTOC_ZIP
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
RUN export PATH="$PATH:$(go env GOPATH)/bin"

WORKDIR /usr/src/app


Enter fullscreen mode Exit fullscreen mode
  • Add the following to the service part of our docker-compose.yml file.


  api-gateway:
    build: 
      context: ./api-gateway-service
      dockerfile: Dockerfile
    container_name: api-gateway
    environment:
      - SERVER_PORT=${API_GATEWAY_PORT}
      - CLIENT_DOMAIN=localhost # we call the server from this domain (for cookie registration ,...)
      - AUTH_SERVICE_URL=gripmock:${GRIPMOCK_GRPC_PORT}
      - SCHEDULER_SERVICE_URL=gripmock:${GRIPMOCK_GRPC_PORT}
      - REPORT_SERVICE_URL=gripmock:${GRIPMOCK_GRPC_PORT}
      - AUTH_PUBLIC_KEY_FILE=/run/secrets/auth-public-key
    ports:
      - ${API_GATEWAY_PORT}:${API_GATEWAY_PORT}
    command: sleep infinity
    volumes:
      - ./api-gateway-service:/usr/src/app


Enter fullscreen mode Exit fullscreen mode
  • We are going to do all the development inside a docker container without installing Golang in our host machine. To do so, we run the containers and then attach VSCode to the api-gateway-service container. As you may noticed, the Dockerfile for api-gateway-service has no entry-point therefore we set the command value of it to sleep infinity to keep the container awake.
  • Now run docker-compose up -d --build
  • While running, attach to the api-gateway service by clicking bottom-left icon and then select attach to running container. Select api-gateway service and wait for a new instance of VSCode to start. Upon starting the attached instance of VSCode, you will be prompted to open a folder within the container. As per our Dockerfile configuration, we have designated the WORKDIR as /usr/src/app. Therefore, we will select this folder inside the container. It is important to note that this designated folder is mounted to the api-gateway-service folder on the host machine using Docker Compose volumes. Consequently, any changes made within the selected folder will be automatically synced to the corresponding folder on the host machine. This synchronization ensures that modifications made during development are reflected in both the container and the host environment.
  • After opening the folder /usr/src/app, open a new terminal and initialize the go project by running go mod init github.com/<your_username>/play-microservices/api-gateway/api-gateway-service. This command will create a go.mod file.
  • Run go get -u google.golang.org/grpc. This is a gRPC framework for running grpc server using Golang.
  • Now create a folder named proto and copy the proto files from here.
  • Create file named build_grpc.sh and set the contents to:


#!/bin/bash
declare -a services=("proto")
for SERVICE in "${services[@]}"; do
   DESTDIR='proto'
   mkdir -p $DESTDIR
   protoc \
       --proto_path=$SERVICE/ \
       --go_out=$DESTDIR \
       --go-grpc_out=$DESTDIR \
       $SERVICE/*.proto
done


Enter fullscreen mode Exit fullscreen mode
  • Run source build_grpc.sh. This command compile our .proto file to Golang. For each .proto file, Two files will be created. X.pb.go and X_grpc.pb.go. The first contains the proto models and the second contains the code for grpc service interface.
  • Note: We adopt a Golang project structure that aligns with the recommended guidelines stated here
  • Create a folder named config and a file named config.go. Set the contents from here. Also create a file named .env in the same folder. we will put our internal environment variables here. Set contents from here.
  • Create a folder named pkg in the root directory (beside mod.go). We will put general packages here. Inside Create a folder named logger then a file named logger.go and set the contents from here.
  • Inside pkg folder create a folder named grpc and then a file named GRPC_Client.go. set the content to:


package grpc

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

type GRPC_Client struct{}

func (jc *GRPC_Client) Connect(url string) (*grpc.ClientConn, error) {
    return grpc.Dial(url, grpc.WithTransportCredentials(insecure.NewCredentials()))
}


Enter fullscreen mode Exit fullscreen mode
  • Create a folder named cookie inside pkg, then a file named cookie.go. Set the contents from here.
  • Create this folder tree: internal/models/report/grpc. inside grpc folder create a file named client_service.go and set the contents to


package grpc

import (
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/config"
    gr "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/grpc"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/logger"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/proto"
    "github.com/gin-gonic/gin"
)

type ReportGRPCClient struct {
    log logger.Logger
    cfg *config.Config
    gr.GRPC_Client
}

func NewReportGRPCClient(log logger.Logger, cfg *config.Config) *ReportGRPCClient {
    return &ReportGRPCClient{log: log, cfg: cfg}
}

func (jc *ReportGRPCClient) GRPC_ListReports(c *gin.Context, listReportsRequest *proto.ListReportsRequest) (*proto.ListReportResponse, error) {
    jc.log.Info("ReportGRPCClient.GRPC_ListReports: Connecting to grpc server...")
    conn, err := jc.Connect(jc.cfg.ReportServiceURL)
    if err != nil {
        return nil, err
    }
    defer conn.Close()

    client := proto.NewReportServiceClient(conn)
    jc.log.Info("ReportGRPCClient.GRPC_ListReports: Conneced to grpc server...")
    jc.log.Infof("ReportGRPCClient.GRPC_ListReports: calling server for ListReports: %v", listReportsRequest)
    return client.ListReports(c, listReportsRequest)
}


Enter fullscreen mode Exit fullscreen mode
  • This file contains the logic for creating a client for report gRPC service and then retrieve the list of reports. We will use this class in our rest handler.
  • Run go install github.com/swaggo/swag/cmd/swag@latest We will use this tool for generation of rest api documentations.
  • Run go get -u github.com/swaggo/gin-swagger and go get -u github.com/swaggo/files
  • Create a folder named handler inside report folder and then a file named handler.go. set the contents to


package handler

import (
    "net/http"
    "strconv"

    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/config"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/internal/models"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/internal/models/report/grpc"
    grpcutils "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/grpc"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/logger"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/proto"
    "github.com/gin-gonic/gin"
)

type ReportHandler struct {
    log logger.Logger
    *grpc.ReportGRPCClient
}

func NewReportHandler(log logger.Logger, cfg *config.Config) *ReportHandler {
    grpcClient := grpc.NewReportGRPCClient(log, cfg) // Initialize the embedded type
    return &ReportHandler{log: log, ReportGRPCClient: grpcClient}
}

// @Summary Get the list of reports
// @Description retrieve the reports
// @Tags report
// @Produce json
// @Param   page      query    int     true        "Page"
// @Param   size      query    int     true        "Size"
// @Success 200 {array} models.ListReportResponse
// @Router /report/list [get]
func (rh *ReportHandler) ListReports(c *gin.Context) {
    rh.log.Infof("Request arrived: list reports: %v", c.Request)

    page, err := strconv.ParseInt(c.Query("page"), 10, 32)
    if err != nil {
        rh.log.Error("ReportHandler.ListReports: invalid input. page is not provided in the query")
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid page parameter"})
        return
    }

    size, err := strconv.ParseInt(c.Query("size"), 10, 32)
    if err != nil {
        rh.log.Error("ReportHandler.ListReports: invalid input. size is not provided in the query")
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid size parameter"})
        return
    }

    res, err := rh.GRPC_ListReports(c.Request.Context(), &proto.ListReportsRequest{Page: page, Size: size})
    if err != nil {
        rh.log.Errorf("ReportHandler.ListReports: error listing reports: %v",err.Error())
        status := grpcutils.GetHttpStatusCodeFromGrpc(err)
        c.AbortWithStatusJSON(status, gin.H{"status": false, "error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, models.ListReportResponseFromProto(res))
}


Enter fullscreen mode Exit fullscreen mode
  • Run swag init -g ./cmd/main.go -o ./docs. This command will parse comments and generate required files(docs folder and docs/doc.go) at /docs folder.
  • Create a folder named api inside internal folder and then a file named router.go. set the contents to


package api

import (
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/config"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/docs"
    rh "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/internal/models/report/handler"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/logger"
    "github.com/gin-gonic/gin"

    "net/http"

    swaggerfiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
)

type Router struct {
    log logger.Logger
    cfg *config.Config
}

func NewRouter(log logger.Logger, cfg *config.Config) *Router {
    return &Router{log: log, cfg: cfg}
}
func (r *Router) Setup(router *gin.Engine) {

    docs.SwaggerInfo.BasePath = "/api/v1"
    router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))

    router.GET("/api/v1/ping", r.Pong)

    reportHandler := rh.NewReportHandler(r.log, r.cfg)
    router.GET("/api/v1/report/list", reportHandler.ListReports)
}

// @BasePath /api/v1
// PingExample godoc
// @Summary ping example
// @Schemes
// @Description do ping
// @Tags ping
// @Accept json
// @Produce json
// @Success 200 {string} Pong
// @Router /ping [get]
func (s *Router) Pong(c *gin.Context) {
    c.JSON(http.StatusOK, "Pong")
}


Enter fullscreen mode Exit fullscreen mode
  • Create a folder named server inside internal folder. Then a file named server.go. set the content to


package server

import (
    "fmt"

    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/config"
    api "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/internal/api"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/logger"

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

type Server struct {
    log logger.Logger
    cfg *config.Config
}

func NewServer(log logger.Logger, cfg *config.Config) *Server {
    return &Server{log: log, cfg: cfg}
}

func (s *Server) Run() {
    r := gin.Default()

    router := api.NewRouter(s.log, s.cfg)
    router.Setup(r)

    r.Run(fmt.Sprintf("0.0.0.0:%s", s.cfg.ServerPort))
}


Enter fullscreen mode Exit fullscreen mode
  • Create a folder named cmd and a file named main.go. Set the content to


package main

import (
    "log"

    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/config"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/internal/server"
    "github.com/<yourusername>/play-microservices/api-gateway/api-gateway-service/pkg/logger"
)

func main() {

    cfg, err := config.InitConfig()
    if err != nil {
        log.Fatal(err)
    }

    appLogger := logger.NewApiLogger(cfg)
    appLogger.InitLogger()
    appLogger.Infof(
        "AppVersion: %s, LogLevel: %s, Environment: %s",
        cfg.AppVersion,
        cfg.Logger_Level,
        cfg.Environment,
    )
    appLogger.Infof("Success parsed config: %#v", cfg.AppVersion)

    appLogger.Info("Starting the server")
    s := server.NewServer(appLogger, cfg)
    s.Run()
}



Enter fullscreen mode Exit fullscreen mode
  • Run go mod tidy
  • Run go run cmd/main.go
  • Open a browser and go to http://localhost:5010/api/v1/report/list?page=1&size=10 If everything goes on plan, you can connect to the server and receive the results. What happens here is that from the browser we send a rest request to our api-gateway by calling http://localhost:5010/api/v1/report/list?page=1&size=10. The gateway receive our parameters, and then create a gRPC request to our mock server. The mock server returns the result and then the api-gateway return that result to us.

Rest report service

  • Stop the server by hitting ctl + c
  • We repeat the exact procedure for user and job services. For user and job you can get the files from here and here respectively. Also do not forget to add the endpoints to router.go file from here.
  • It is essential to consider the clear separation of REST API models and gRPC models. Just as we adhere to the practice of separating the database layer models from the gRPC layer models in our gRPC services, we follow the same approach here. By separating the models specific to the REST layer from those used in the gRPC layer, we gain valuable flexibility in querying and combining data from multiple gRPC services. This distinction allows us to define and manage models unique to each layer in a cohesive manner, facilitating efficient data retrieval and manipulation within the respective contexts.
  • Some notes on swagger and rest api parameter types. We use gin-swagger for creating documentation for our rest api. In rest api we have 5 parameter types:

    • Query parameters: appended to the URL after a question mark (?). example: http://localhost:5010/api/v1/report/list?page=1&size=10
    • Path parameters: are part of the URL path. Example: http://localhost:5010/api/v1/user/{id}
    • Headers: are key-value pairs included in the request or response headers. Example: Authorization, Content-Type.
    • The request body: carries additional data sent with the HTTP request. It is used to send complex or larger data payloads, such as JSON or XML, to the server.
    • Form data: is used to submit data from an HTML form to the server. It consists of key-value pairs representing form fields and their values. Form data is encoded and sent in the body of an HTTP request with the "Content-Type" header set as "application/x-www-form-urlencoded" or "multipart/form-data".
  • For example the swag declarative comments for GetUser endpoint is as follows. For more details see here



// @Summary Get user
// @Description Get user
// @Tags user
// @Produce json
// @Success 200 {object} models.GetUserResponse
// @Router /user/get [get]
func (uh *UserHandler) GetUser(c *gin.Context) {

    res, err := uh.GRPC_GetUser(c, &proto.GetUserRequest{})

    if err != nil {
        c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"status": false, "error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, models.GetUserResponseFromProto(res))
}


Enter fullscreen mode Exit fullscreen mode
  • After adding swag comments Run swag init --parseDependency -g ./cmd/main.go -o ./docs and then run go run cmd/main.go. Go to http://localhost:5010/swagger/index.html and you can see the swagger documentation for your rest api. based on the stubs you have created for your gRPC servers, you can test the api endpoints.

Swagger

  • In the end, we will add an auth interceptor to our api-gateway. Create a folder named interceptors inside internal/api and then a file named auth_interceptor.go.


package interceptors

import (
    "net/http"

    "github.com/KhaledHosseini/play-microservices/api-gateway/api-gateway-service/pkg/cookie"
    "github.com/gin-gonic/gin"
    "google.golang.org/grpc/metadata"
)

// We do authentication and authorization in the end services. We just attach the auth headers to grpc requests.
func AuthenticateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        accessToken, err := cookie.GetAccessToken(c) // Retrieve the access token from the request header
        if err != nil {
            c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"status": false, "error": err.Error()})
            return
        }

        md := metadata.Pairs("authorization", accessToken)
        ctx := metadata.NewOutgoingContext(c.Request.Context(), md)

        // Update the request context with the modified context
        c.Request = c.Request.WithContext(ctx)

        c.Next()
    }
}



Enter fullscreen mode Exit fullscreen mode
  • In microservices architecture, depending on the design one approach is to do authentication inside api-gateway and authorization inside downstream services. If we use another layer of authentication between microservices using protocols like mTLS, this approach can be considered safe. Another approach is to do both authentication and authorization of users inside downstream services. Here we use the second approach and inside interceptor file we just add the authorization header of http call to the metadata header of grpc context. In gRPC, headers are sent as part of the gRPC message metadata using key-value pairs. The headers are serialized using Protocol Buffers (protobuf) and encoded as binary data while REST API headers are sent as part of the HTTP request or response headers. They are typically represented as plain text in a key-value format, following the HTTP header specifications.

  • Change the contents of router.go inside api folder to add the interceptor.



func (r *Router) Setup(router *gin.Engine) {

    docs.SwaggerInfo.BasePath = "/api/v1"
    router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))

    router.GET("/api/v1/ping", r.Pong)

    userHandler := uh.NewUserHandler(r.log, r.cfg)
    jobHandler := jh.NewJobHandler(r.log, r.cfg)
    reportHandler := rh.NewReportHandler(r.log, r.cfg)

    router.POST("/api/v1/user/create", userHandler.CreateUser)
    router.POST("/api/v1/user/login", userHandler.LoginUser)
    router.POST("/api/v1/user/refresh_token", userHandler.RefreshAccessToken)
    router.POST("/api/v1/user/logout", userHandler.LogOutUser)

    // Apply the middleware to the routes inside the router.Group function
    api := router.Group("/api")
    api.Use(interceptors.AuthenticateUser()) // Apply the middleware here
    {
        api.GET("/v1/user/get", userHandler.GetUser)
        api.GET("/v1/user/list", userHandler.ListUsers)

        api.POST("/v1/job/create", jobHandler.CreateJob)
        api.POST("/v1/job/update", jobHandler.UpdateJob)
        api.GET("/v1/job/get", jobHandler.GetJob)
        api.GET("/v1/job/list", jobHandler.ListJobs)
        api.POST("/v1/job/delete", jobHandler.DeleteJob)

        api.GET("/v1/report/list", reportHandler.ListReports)
    }
}


Enter fullscreen mode Exit fullscreen mode
  • For subsequent requests, you need to add authorization headers to the request. In this case to be able to access protected endpoints, you need to login first. On logging in the cookie for the user will be set on the browser. You can inspect the cookies value by right click on the page and select inspect. Then under application tab and storage section you can see the values for authorizationand x-refresh-token values. On subsequent requests, these cookies will be sent along with the request. Api-gateway then add the authorization cookie to the metadata of grpc context to be consumed by downstream services for authentication and authorization purposes.

  • To DO
  • Add tests
  • Add tracing using Jaeger
  • Add monitoring and analysis using grafana
  • Refactoring

I would love to hear your thoughts. Please comment on your opinions. If you found this helpful, let's stay connected on Twitter! xaledhosseini.

💖 💪 🙅 🚩
khaledhosseini
Khaled Hosseini

Posted on July 11, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Play Microservices: Api-gateway service
microservices Play Microservices: Api-gateway service

July 11, 2023