Go template project with echo and mysql This project is a template for building web applications using Go, echo, and MySQL. It provides a dockerized environment, a config system, a database schema management and code gen from database schema and openapi specification.
- Clone the project and navigate to the root directory.
- Run
make build
to build the containers needed to run the project. - Run
make up
to start the containers. - Visit
http://localhost:8080
to see the application running.
The project runtime is dockerized in two different docker files. Dockerfile.dev for developement with live reload and Dockerfile that is intended to be the production container definition. The docker and compose are stored in the docker
directory.
You can override the default docker registry by adding the following value to docker/.env
DOCKER_REGISTRY=<your-docker-registry>
This might be necessary if you are behind a firewall that can access the default registry.
Project config is stored in the config.yaml
at the root of the project. This file is read by the config.go
and all the values can be overriden using environment variables. Any missing value from the env variables is read from yaml file by default. Example:
Database struct {
Host string `yaml:"host" envconfig:"DB_HOST"`
Port int `yaml:"port" envconfig:"DB_PORT"`
Name string `yaml:"name" envconfig:"DB_NAME"`
User string `yaml:"user" envconfig:"DB_USER"`
Pass string `yaml:"pass" envconfig:"DB_PASS"`
} `yaml:"database"`
You can override the local config with env variables by adding the file docker/.env
.
Database schema is stored in the schema.sql
file in the sql/schema
directory. You can put your partial templates in the same directory. The template is rendered to a single sql file which represents the desired database state. Schema template example with partial templates:
{{define "id"}}`id` bigint unsigned not null auto_increment{{end}}
{{define "createdAt"}}`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP{{end}}
{{define "updatedAt"}}`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP{{end}}
create database {{.schema}};
create table {{.schema}}.user (
{{template "id"}},
`name` varchar(255) not null,
{{template "createdAt"}},
{{template "updatedAt"}},
PRIMARY KEY(`id`)
);
To change the database schema, first make your changes to the template file and then run the make get_alters
to get the db alters that need to be ran. You can run the alters using the make apply_alters
on the local database. On production env this will be handled in a CI/CD stage. We use the atlasgo to diff/apply the schema.
Let's add an age column to our user table:
create table {{.schema}}.user (
{{template "id"}},
`name` varchar(255) not null,
`age` int null,
{{template "createdAt"}},
{{template "updatedAt"}},
PRIMARY KEY(`id`)
);
Now we should run make get_alters
to see the changes database.
Pending Alters:
ALTER TABLE `localdb`.`user` ADD COLUMN `age` int NULL
And make sync_entity
to update the generated go code in entity package:
type LocaldbUser struct {
ID uint64
Name string
Age sql.NullInt32
CreatedAt time.Time
UpdatedAt time.Time
}
api
dir holds the openapi specification and the code gen config for oapi-codegen. To generate the server interface from specification use make api
. This generates/updates internal/controller/api.go
file.
entity
is the package of generated go code from the sql/schema
and sql/query
files. This package is generated by sqlc and is the primary way of interacting with database in the application code. Example sql query:
-- name: GetUser :one
select * from localdb.user where id = ?;
Generated go code:
func (q *Queries) GetUser(ctx context.Context, id uint64) (LocaldbUser, error) {
row := q.db.QueryRowContext(ctx, getUser, id)
var i LocaldbUser
err := row.Scan(
&i.ID,
&i.Name,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
Keep in mind that this package needs to be regenerated after making changes to schema/query using make sync_entity
.
echo
is the router of choice for this project. Routes are generated from the openapi specification in ./api/service.yaml
. To use, simply implement the ServerInterface
or StrictServerInterface
for your specification.
service
package is responsible for handling config init and database connections and monitoring tools.