Skip to main content

Build Your First Service

This document describes the step by step about how to generate a game backend service using mykit.

Setup repository

Step 1: Login gitlab and create new project. ex: demo01

Step 2: Open the terminal and navigate to the go-workspace/src folder, at here create gitlab.nautilusgames.tech folder, then navigate to the folder that has already created, ex:

cd go-workspace/src/
mkdir gitlab.nautilusgames.tech
cd gitlab.nautilusgames.tech

Step 3: Clone and open demo01 project

git clone https://gitlab.nautilusgames.tech/demo01.git
cd demo01
code .

Init project

Run command below to init new project

mykit init --dir ~/go-workspace/src/gitlab.nautilusgames.tech/demo01 --name demo01  

Note:

  • If mykit is not found, please install mykit and try again.

  • Local folder much be same with repo URL.

Project structure

After using mykit init to init new project, this produces the following structure:

demo01
├── api
│ ├── ...
│ ├── demo01_config.proto
│ ├── demo01_code.proto
│ └── demo01.proto
├── build
│ └── ...
├── cmd
│ └── main.go
├── configs
│ └── config.yaml
├── internal
│ └── server
├── package
│ └── ...
├── schema
│ ├── base.go
│ └── sample.go
├── vendor
│ └── ...
├──.dockerignore
├──.gitignore
├── ci.yaml
├── go.mod
├── go.sum
├── Makefile
└── mykit.yaml
  • api: including proto file that use to define gRPC services and the structure for the data you want to serialize.

  • configs: including config.yaml file that use to define the config of your service, that config matching with Config message structure that defines in proto file.

  • internal: using handle core logic and runs gRPC server to handle client calls.

  • schema: using to declare model database schema as a graph structure.

  • mykit.yaml: define mykit config, can change some config to support your demand, below is a example for mykit.yaml file.

project:
name: demo01
monorepo: false
go_package: gitlab.nautilusgames.tech/demo01
npm_package: "@marketplace/demo01"
npm_registry: gitlab.nautilusgames.tech/api/v4/projects/31/packages/npm/

extend: # extend file name

generate:
allow_custom_options: true
profiling:
port: 6060
enable: false
dockerfile: # using generate dockerfile
enable: true
ent: # using generate schema when enable is true
enable: false
command: # using generate cmd that have main.go file
enable: true
server: # using generate server
enable: true
path: internal/server
grpc_server: # using generate grpc server
enable: true
path: internal/server
grpc_gateway:
enable: false
client: # using generate client that can use to test API if new it
enable: true
path: pkg/client
helm:
enable: false
proto: # using declare proto files that you want to generate
go:
- demo01.proto
- demo01_config.proto
- demo01_code.proto
js: # allow build npm package using grpc-web
- demo01.proto
- demo01_code.proto
js_connect: # allow build npm package for connect-grpc
- demo01.proto
- demo01_code.proto
imports: # optional
grpc_log:
enable: false

Working with gRPC

Create new proto file

Step 1: At ./api folder create new proto file, such as demo01_model.proto

Step 2: Define protocol buffer messages

Ex:

message User {
string user_name = 1;
int age = 2;
}

Step 3: Generate proto message

The first step, need add demo01_model.proto into mykit.yaml files.

Ex:

project:
...

extend: # extend file name

generate:
...
proto:
go:
...
- demo01_model.proto
js:
...
- demo01_model.proto
js_connect:
...
- demo01_model.proto
imports: # optional
grpc_log:
enable: false

Note:

  • At go type need add proto file.

  • At js, js_connect type only add if want to share model for client.

The second step, generate proto file.

mykit generate go

Define gRPC service

Step 1: Define gRPC services in ordinary proto files, with RPC method parameters and return types specified as protocol buffer messages.

./api/demo01.proto

service Demo01 {
rpc Hello(HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
string user_name = 1;
}

message HelloReply {
string message = 1;
}

Step 2: Generate proto file

Run command below to generate proto file.

mykit generate go

Step 3: Implement server

./internal/server/demo01/grpc_demo01_hello.go

func (s *demo01Server) Hello(ctx context.Context, request *demo01.HelloRequest) (*demo01.HelloReply, error) {
if err := request.Validate(); err != nil {
return nil, err
}

if request.UserName == "" {
return nil, fmt.Errorf("user not found")
}

return &demo01.HelloReply{
Message: fmt.Sprintf("%s%s", "hello ", request.GetUserName()),
}, nil
}

Step 4: Run server

Input command below to run demo01 server. You can use Postman or BloomRPC to call API.

go run cmd/main.go -c ./configs/config.yaml

Working with database

Init schema

We will use ent to generate database. Ent is a simple, yet powerful entity framework for Go, that makes it easy to build and maintain applications with large data-models.

Step 1: To generate schema, go to mykit.yaml file and set enable ent is true.

generate:
...
ent:
enable: true
...

Step 2: Define schema model

./schema/user.go

type User struct {
ent.Schema
}

func (User) Fields() []ent.Field {
return []ent.Field{
field.Int64("id"),
field.String("user_name"),
field.Int64("age"),
}
}

func (User) Mixin() []ent.Mixin {
return []ent.Mixin{
Base{},
}
}

Step 3: Generate database

Go to ./pkg/doc.go file, click run go generate to generate schema. If is not found doc.go file, run below command to generate database.

mykit generate go

Config database

Step 1: Add database config and database config value

At ./api/demo01_config.proto file, add database config. Ex:

import "database/api/database.proto"; // need to import

message Config {
...
greyhole.database.Database database = 1 [(validate.rules).message.required = true];
...
}

At ./configs/config.yaml file, add database value. The environment variable value will be set at aws secret.

database:
auth_method: ${DATABASE_AUTH_METHOD} # DATABASE_AUTH_METHOD_AWS_IAM
host: ${DATABASE_HOST}
port: 3306
name: ${DATABASE_NAME}
username: ${DATABASE_USERNAME}
aws_region: ${DATABASE_AWS_REGION}
debug: false
tracing_enabled: true
max_idle_conns: 5
max_open_conns: 5
conn_max_life_time: 3 # minutes
conn_max_idle_time: 3 # minutes

Step 2: Generate proto

Introduction to the gRPC-Gateway and How to do it

Introduction to the gRPC-Gateway

We all know that gRPC is not a tool for everything. There are cases where we still want to provide a traditional HTTP/JSON API. The reasons can range from maintaining backward-compatibility to supporting programming languages or clients not well supported by gRPC. But writing another service just to expose an HTTP/JSON API is quite a time consuming and tedious task.

The gRPC-Gateway is a plugin of the Google protocol buffers compiler protoc. It reads protobuf service definitions and generates a reverse-proxy server which translates a RESTful HTTP API into gRPC. This server is generated according to the google.api.http annotations in your service definitions.

This helps you provide your APIs in both gRPC and HTTP/JSON format at the same time.

Working with gRPC-gateway

Step 1: Add the gateway server

Continue modify the ./api/demo01.proto file to add the gateway server changes. The new contents look like this:

./api/demo01.proto

import "google/api/annotations.proto"; // need to import

service Demo01 {
rpc Hello(HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/api/v1/demo01/hello"
};
}
}

message HelloRequest {
string user_name = 1;
}

message HelloReply {
string message = 1;
}

Step 2: At mykit.yaml file, set enable generate grpc_gateway and http_server.

generate:
...
http_server:
enable: true
grpc_gateway:
enable: true

Step 3: Add http listener

At ./api/demo01_config.proto file, add http_listener:

import "validate/validate.proto";
import "logger/api/logger.proto";

message Config {
...
greyhole.carbon.Listener http_listener = 1 [(validate.rules).message = { required: true }];
}

At ./configs/config.yaml file, add http_listener value:

...

http_listener:
tcp:
address: 0.0.0.0
port: 8081
secure: false

...

Step 4: Generate proto

Step 5: Test API by postman or bloomRPC, try with gRPC request and HTTP request.