Skip to content

Instantly share code, notes, and snippets.

@festum
Last active June 23, 2023 15:36
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save festum/88c17a7ea48557cabc70fe3141759bf4 to your computer and use it in GitHub Desktop.
Save festum/88c17a7ea48557cabc70fe3141759bf4 to your computer and use it in GitHub Desktop.
Golang Guideline

Project Structure

The following content shows a general structure of a web app, it can be changed based on the different conventions of web frameworks.

- templates (views)          # Template files
- public (static)            # Public assets
	- css
	- fonts
	- img
	- js
- routes
- models
- pkg
	- setting                 # Global settings of app
- cmd                         # Files of commands
- conf                        # Default configuration
	- locale                  # I18n locale files
- custom                      # Custom configuraion
- data                        # App generated data
- log                         # App generated log

Command Line App

When writing a CLI app, each command should be in a separate file under /cmd directory:

/cmd
	dump.go
	fix.go
	serve.go
	update.go
	web.go

Coding Guidelines

Basic Rules

  • All applications that have main package should have constant APP_VER to indicate its version with format X.Y.Z.Date [Status]. For example, 0.7.6.1112 Beta.

  • A library/package should have function Version to return its version in string type with format X.Y.Z[.Date].

  • You should consider split lines of code that have more than 80 characters. The rule is use to argument as the unit to split:

     So(z.ExtractTo(
     	path.Join(os.TempDir(), "testdata/test2"),
     	"dir/", "dir/bar", "readonly"), ShouldBeNil)
  • When the function or method's declaration has more than 80 characters, you should consider split it as well. The rule is arguments with same type in the same line. After declaration, you also need to put an empty line to distinguish itself with function or method's body:

     // NewNode initializes and returns a new Node representation.
     func NewNode(
     	importPath, downloadUrl string,
     	tp RevisionType, val string,
     	isGetDeps bool) *Node {
    
     	n := &Node{
     		Pkg: Pkg{
     			ImportPath: importPath,
     			RootPath:   GetRootPath(importPath),
     			Type:       tp,
     			Value:      val,
     		},
     		DownloadURL: downloadUrl,
     		IsGetDeps:   isGetDeps,
     	}
     	n.InstallPath = path.Join(setting.InstallRepoPath, n.RootPath) + n.ValSuffix()
     	return n
     }
  • Group declaration should be organized by types, not all together:

     const (
     	// Default section name.
     	DEFAULT_SECTION = "DEFAULT"
     	// Maximum allowed depth when recursively substituing variable names.
     	_DEPTH_VALUES = 200
     )
    
     type ParseError int
    
     const (
     	ERR_SECTION_NOT_FOUND ParseError = iota + 1
     	ERR_KEY_NOT_FOUND
     	ERR_BLANK_SECTION_NAME
     	ERR_COULD_NOT_PARSE
     )
  • When there are more than one functional modules in single source file, you should use ASCII Generator to make a highlight title:

     // _________                                       __
     // \_   ___ \  ____   _____   _____   ____   _____/  |_
     // /    \  \/ /  _ \ /     \ /     \_/ __ \ /    \   __\
     // \     \___(  <_> )  Y Y  \  Y Y  \  ___/|   |  \  |
     //  \______  /\____/|__|_|  /__|_|  /\___  >___|  /__|
     //         \/             \/      \/     \/     \/
  • Functions or methods are ordered by the dependency relationship, such that the most dependent function or method should be at the top. In the following example, ExecCmdDirBytes is the most fundamental function, it's called by ExecCmdDir, and ExecCmdDir is also called by ExecCmd:

     // ExecCmdDirBytes executes system command in given directory
     // and return stdout, stderr in bytes type, along with possible error.
     func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
     	...
     }
    
     // ExecCmdDir executes system command in given directory
     // and return stdout, stderr in string type, along with possible error.
     func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
     	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
     	return string(bufOut), string(bufErr), err
     }
    
     // ExecCmd executes system command
     // and return stdout, stderr in string type, along with possible error.
     func ExecCmd(cmdName string, args ...string) (string, string, error) {
     	return ExecCmdDir("", cmdName, args...)
     }
  • Methods of struct should be put after struct definition, and order them by the order of fields they mostly operate on:

     type Webhook struct { ... }
     func (w *Webhook) GetEvent() { ... }
     func (w *Webhook) SaveEvent() error { ... }
     func (w *Webhook) HasPushEvent() bool { ... }
  • If a struct has operational functions, should basically follow the CRUD order:

     func CreateWebhook(w *Webhook) error { ... }
     func GetWebhookById(hookId int64) (*Webhook, error) { ... }
     func UpdateWebhook(w *Webhook) error { ... }
     func DeleteWebhook(hookId int64) error { ... }
  • If a struct has functions or methods start with Has, Is, Can or Allow, they should be ordered by the same order of Has, Is, Can or Allow.

  • Declaration of variables should be put before corresponding functions or methods:

     var CmdDump = cli.Command{
     	Name:  "dump",
     	...
     	Action: runDump,
     	Flags:  []cli.Flag{},
     }
    
     func runDump(*cli.Context) { ...
  • Use named field to initialize struct fields whenever possible:

     AddHookTask(&HookTask{
     	Type:        HTT_WEBHOOK,
     	Url:         w.Url,
     	Payload:     p,
     	ContentType: w.ContentType,
     	IsSsl:       w.IsSsl,
     })

Import Packages

All package import paths must be a URL from a code hosting site except packages from the standard library.

Therefore, there are four types of packages a source file could import:

  1. Packages from the standard library
  2. Third-party packages
  3. Packages in the same organization but not in the same repository
  4. Packages in the same repository

Basic rules:

  • Use different groups to separate import paths by an empty line for two or more types of packages.
  • Must not use . to simplify the import path except in test files (*_test.go).
  • Must not use a relative path (./subpackage) as import path, all import paths must be go get-able.

Here is a complete example:

import (
    "fmt"
    "html/template"
    "net/http"
    "os"

    "github.com/codegangsta/cli"
    "gopkg.in/macaron.v1"

    "github.com/gogs/git"
    "github.com/gogs/gfm"

    "github.com/gogs/gogs/routes"
    "github.com/gogs/gogs/routes/repo"
    "github.com/gogs/gogs/routes/user"
)

Go is about naming and organization as much as everything else in the language. Well-organized Go code is easy to discover, use and read. Well-organized code is as critical as well designed APIs. The location, name, and the structure of your packages are the first elements your users see and interact with.

This document’s goal is to guide you with common good practices not to set rules. You will always need to use your own judgement to pick the most elegant solution for your specific case.

Packages

All Go code is organized into packages. A package in Go is simply a directory/folder with one or more .go files inside of it. Go packages provide isolation and organization of code similar to how directories/folders organize files on a computer.

All Go code lives in a package and a package is the entry point to access Go code. Understanding and establishing good practices around packages is important to write effective Go code.

Package Organization

Let’s begin with suggestions how you should organize Go code and explain conventions about locating Go packages.

Use multiple files

A package is a directory with one or more Go files. Feel free to separate your code into as many files as logically make sense for optimal readability.

For example, an HTTP package might have been separated into different files according to the HTTP aspect the file handles. In the following example, an HTTP package is broken down into a few files: header types and code, cookie types and code, the actual HTTP implementation, and documentation of the package.

- doc.go       // package documentation
- headers.go   // HTTP headers types and code
- cookies.go   // HTTP cookies types and code
- http.go      // HTTP client implementation, request and response types, etc.

Keep types close

As a rule of thumb, keep types closer to where they are used. This makes it easy for any maintainer (not just the original author) to find a type. A good place for a Header struct type might be in headers.go.

$ cat headers.go
package http

// Header represents an HTTP header.
type Header struct {...}

Even though, the Go language doesn’t restrict where you define types, it is often a good practice to keep the core types grouped at the top of a file.

Organize by responsibility

A common practise from other languages is to organize types together in a package called models or types. In Go, we organize code by their functional responsibilities.

package models // DON'T DO IT!!!

// User represents a user in the system.
type User struct {...}

Rather than creating a models package and declare all entity types there, a User type should live in a service-layer package.

package mngtservice

// User represents a user in the system.
type User struct {...}

func UsersByQuery(ctx context.Context, q *Query) ([]*User, *Iterator, error)

func UserIDByEmail(ctx context.Context, email string) (int64, error)

Optimize for godoc

It is a great exercise to use godoc in the early phases of your package’s API design to see how your concepts will be rendered on doc. Sometimes, the visualization also has an impact on the design. Godoc is the way your users will consume a package, so it is ok to tweak things to make them more accessible. Run godoc -http=<hostport> to start a godoc server locally.

Provide examples to fill the gaps

In some cases, you may not be able to provide all related types from a single package. It might be noisy to do so, or you might want to publish concrete implementations of a common interface from a separate package, or those types could be owned by a third-party package. Give examples to help the user to discover and understand how they are used together.

$ godoc cloud.google.com/go/datastore
func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error)
...

NewClient works with option.ClientOptions but it is neither the datastore package nor the option package that export all the option types.

$ godoc google.golang.org/extraoption
func WithCustomValue(v string) option.ClientOption
...

If your API requires many non-standard packages to be imported, it is often useful to add a Go example to give your users some working code.

Examples are a good way to increase visibility of a less discoverable package. For example, an example for datastore.NewClient might reference the extraoption package.

Don’t export from main

An identifier may be exported to permit access to it from another package.

Main packages are not importable, so exporting identifiers from main packages is unnecessary. Don’t export identifiers from a main package if you are building the package to a binary.

Exceptions to this rule might be the main packages built into a .so, or a .a or Go plugin. In such cases, Go code might be used from other languages via cgo’s export functionality and exporting identifiers are required.

Package Naming

A package name and import path are both significant identifiers of your package and represent everything your package contains. Naming your packages canonically not just improves your code quality but also your users’.

Lowercase only

Package names should be lowercase. Don’t use snake_case or camelCase in package names. The Go blog has a comprehensive guide about naming packages with a good variety of examples.

Short, but representative names

Package names should be short, but should be unique and representative. Users of the package should be able to grasp its purpose from just the package’s name.

Avoid overly broad package names like “common” and “util”.

import "pkgs.org/common" // DON'T!!!

Avoid duplicate names in cases where user may need to import the same package.

If you cannot avoid a bad name, it is very likely that there is a problem with your overall structure and code organization.

Clean import paths

Avoid exposing your custom repository structure to your users. Align well with the GOPATH conventions. Avoid having src/, pkg/ sections in your import paths.

github.com/user/repo/src/httputil   // DON'T DO IT, AVOID SRC!!

github.com/user/repo/gosrc/httputil // DON'T DO IT, AVOID GOSRC!!

No plurals

In go, package names are not plural. This is surprising to programmers who came from other languages and are retaining an old habit of pluralizing names. Don’t name a package httputils, but httputil!

package httputils  // DON'T DO IT, USE SINGULAR FORM!!

Renames should follow the same rules

If you are importing more than one packages with the same name, you can locally rename the package names. The renames should follow the same rules mentioned on this article. There is no rule which package you should rename. If you are renaming the standard package library, it is nice to add a go prefix to make the name self document that it is “Go standard library’s” package, e.g. gourl, goioutil.

import (
    gourl "net/url"

    "myother.com/url"
)

Enforce vanity URLs

go get supports getting packages by a URL that is different than the URL of the package’s repo. These URLs are called vanity URLs and require you to serve a page with specific meta tags the Go tools recognize. You can serve a package with a custom domain and path using vanity URLs.

For example,

$ go get cloud.google.com/go/datastore

checks out the source code from https://code.googlesource.com/gocloud behind the scenes and puts it in your workspace under $GOPATH/src/cloud.google.com/go/datastore.

Given code.googlesource.com/gocloud is already serving this package, would it be possible to go get the package from that URL? The answer is no, if you enforce the vanity URL.

To do that, add an import statement to the package. The go tool will reject any import of this package from any other path and will display a friendly error to the user. If you don’t enforce your vanity URLs, there will be two copies of your package that cannot work together due to the different namespace.

package datastore // import "cloud.google.com/go/datastore"

Package Documentation

Always document the package. Package documentation is a top-level comment immediately preceding the package clause. For non-main packages, godoc always starts with “Package {pkgname}” and follows with a description. For main packages, documentation should explain the binary.

// Package ioutil implements some I/O utility functions.
package ioutil

// Command gops lists all the processes running on your system.
package main

// Sample helloworld demonstrates how to use x.
package main

Use doc.go

Sometimes, package docs can get very lengthy, especially when they provide details of usage and guidelines. Move the package godoc to a doc.go file. (See an example of a doc.go.)

Declaration

Functions or Methods

Order of arguments of functions or methods should generally apply the following rules (from left to right):

  1. More important to less important.
  2. Simple types to complicated types.
  3. Same types should be put together whenever possible.

Example

In the following declaration, type User is more complicated than type string, but Repository belongs to User, so User is more left than Repository.

func IsRepositoryExist(user *User, repoName string) (bool, error) { ...

Naming Rules

File name

  • The main entry point file of the application should be named as main.go or the same as the application. For example, the entry point file of Gogs is named gogs.go.

Functions and Methods

  • If the main purpose of functions or methods is returning a bool type value, the name of function or method should start with Has, Is, Can or Allow, etc.

     func HasPrefix(name string, prefixes []string) bool { ... }
     func IsEntry(name string, entries []string) bool { ... }
     func CanManage(name string) bool { ... }
     func AllowGitHook() bool { ... }

Constants

  • Constant should use all capital letters and use underscore _ to separate words.

     const APP_VER = "0.7.0.1110 Beta"
  • If you need enumerated type, you should define the corresponding type first:

     type Scheme string
    
     const (
     	HTTP  Scheme = "http"
     	HTTPS Scheme = "https"
     )
  • If functionality of the module is relatively complicated and easy to mix up with a constant name, you can add a prefix to every constant:

     type PullRequestStatus int
    
     const (
     	PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
     	PULL_REQUEST_STATUS_CHECKING
     	PULL_REQUEST_STATUS_MERGEABLE
     )

Variables

  • A variable name should follow general English expression or shorthand.

  • In relatively simple (fewer objects and more specific) context, the variable name can use a simplified form as follows:

    • user to u
    • userID to uid
  • If variable type is bool, its name should start with Has, Is, Can or Allow, etc.

     var isExist bool
     var hasConflict bool
     var canManage bool
     var allowGitHook bool
  • The last rule also applies for defining structs:

     // Webhook represents a webhook object.
     type Webhook struct {
     	ID           int64 `xorm:"pk autoincr"`
     	RepoID       int64
     	OrgID        int64
     	URL          string `xorm:"url TEXT"`
     	ContentType  HookContentType
     	Secret       string `xorm:"TEXT"`
     	Events       string `xorm:"TEXT"`
     	*HookEvent   `xorm:"-"`
     	IsSSL        bool `xorm:"is_ssl"`
     	IsActive     bool
     	HookTaskType HookTaskType
     	Meta         string     `xorm:"TEXT"` // store hook-specific attributes
     	LastStatus   HookStatus // Last delivery status
     	Created      time.Time  `xorm:"CREATED"`
     	Updated      time.Time  `xorm:"UPDATED"`
     }

Variable Naming Convention

A variable name is generally using camelCase style, but when you have unique nouns, should apply the following rules:

  • If the variable is private, and the unique noun is the first word, then use lower cases, e.g. apiClient.
  • Acronyms should be all capitals, as in ServeHTTP
  • Otherwise, use the original cases for the unique noun, e.g. APIClient, repoID, UserID.

Here is a list of words which are commonly identified as unique nouns:

// A GonicMapper that contains a list of common initialisms taken from golang/lint
var LintGonicMapper = GonicMapper{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
	"XSRF":  true,
	"XSS":   true,
}
  • Single letter represents index: i, j, k
  • Short but descriptive names: cust not customer
  • repeat letters to represent collection, slice, or array and use single letter in loop:
var tt []*Thingfor i, t := range tt {  
  ...  
}
  • avoid repeating package name:
log.Info()    // good  
log.LogInfo() // bad
  • Don’t name like getters or setters :
custSvc.cust()    // good  
custSvc.getCust() // bad
  • Add er to Interface
type Stringer interfaces {  
  String() string  
}

Commentary

  • All exported objects must be well-commented; internal objects can be commented as needed.
  • If the object is countable and does not know the number of it, use singular form and present tense; otherwise, use plural form.
  • Comment of package, function, method and type must be a complete sentence.
  • First letters of sentences should be upper case, but the lower case for phrases.
  • The maximum length of a comment line should be 80 characters.

Package

  • Only one file is needed for package-level comment.

  • For main packages, a line of introduction is enough, and should start with project name:

     // Gogs (Go Git Service) is a painless self-hosted Git Service.
     package main
  • For sub-packages of a complicated project, no package-level comment is required unless it's a functional module.

  • For simple non-main packages, a line of introduction is enough as well.

  • For more complicated functional non-main packages, commonly should have some basic usage examples and must start with Package <name>:

     /*
     Package regexp implements a simple library for regular expressions.
    
     The syntax of the regular expressions accepted is:
    
         regexp:
             concatenation { '|' concatenation }
         concatenation:
             { closure }
         closure:
             term [ '*' | '+' | '?' ]
         term:
             '^'
             '$'
             '.'
             character
             '[' [ '^' ] character-ranges ']'
             '(' regexp ')'
     */
     package regexp
  • For very complicated functional packages, you should create a doc.go file for it.

Type, Interface and Other Types

  • Type is often described in singular form:

     // Request represents a request to run a command.
     type Request struct { ...
  • An interface should be described as follows:

     // FileInfo is the interface that describes a file and is returned by Stat and Lstat.
     type FileInfo interface { ...

Functions and Methods

  • Comments of functions and methods must start with its name:

     // Post returns *BeegoHttpRequest with POST method.
  • If one sentence is not enough, go to the next line:

     // Copy copies file from source to target path.
     // It returns false and error when an error occurs in underlying function calls.
  • If the main purpose of a function or method is returning a bool value, its comment should start with <name> returns true if:

     // HasPrefix returns true if name has any string in given slice as prefix.
     func HasPrefix(name string, prefixes []string) bool { ...

Other Notes

  • When something is waiting to be done, use comment starts with TODO: to remind maintainers.

  • When a known problem/issue/bug needs to be fixed/improved, use comment starts with FIXME: to remind maintainers.

  • When something is too magic and needs to be explained, use comment starts with NOTE::

     // NOTE: os.Chmod and os.Chtimes don't recognize the symbolic link,
     // which will lead "no such file or directory" error.
     return os.Symlink(target, dest)

Test Cases

  • Unit tests must use GoConvey and code coverage must above 80%.

Examples

  • The file of examples of helper modules should be named example_test.go.
  • Test cases of functions must start with Test_, e.g. Test_Logger.
  • Test cases of methods must use the format Text_<Struct>_<Method>, e.g. Test_Macaron_Run.

Copyright

As a open source project, there must be a LICENSE file to claim the rights.

Here are two examples of using Apache License, Version 2.0 and MIT licenses.

Apache License, Version 2.0

This license requires to put following content at the beginning of every file:

// Copyright [yyyy] [name of copyright owner]
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

Replace [yyyy] with the year of creation of the file. Then use personal name for personal project, or organization name for team project to replace [name of copyright owner].

MIT License

This license requires to put following content at the beginning of every file:

// Copyright [yyyy] [name of copyright owner]. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

Replace [yyyy] with the year of creation of the file.

Other Notes

  • Other types of license can follow the template of above two examples.

  • If a file has been modified by different individuals/organizations, and two licenses are compatiable, then change the first line to multiple lines and make them in the order of time:

     // Copyright 2011 Gary Burd
     // Copyright 2013 Unknwon
    
  • Spefify which license is used for the project in the README file:

     ## License
     
     This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text.
    

Module

Test

Version

Patterns

GOPATH
  |-src
    |-projectA
      |-cmd (of project A)
        |-cmdA
          |-main.go
        |-cmdB
          |-main.go
      |-vendor
      |-pkg lib
      |-pkg libB
    |-projectB
    ...

Build

Cross-platform build requires Go version >= 1.5. See arch for more os tags.

GOOS=windows GOARCH=386 go build -o gistnc-386.exe ./src/github.com/festum/gistnc/gistnc.go
GOOS=windows GOARCH=amd64 go build -o gistnc-amd64.exe ./src/github.com/festum/gistnc/gistnc.go
GOOS=linux GOARCH=i386 go build -o gistnc-linux-i386 ./src/github.com/festum/gistnc/g
istnc.go
GOOS=linux GOARCH=amd64 go build -o gistnc-linux-amd64 ./src/github.com/festum/gistnc/g
istnc.go
GOOS=darwin GOARCH=i386 go build -o gistnc-osx-i386 ./src/github.com/festum/gistnc/g
istnc.go
GOOS=darwin GOARCH=amd64 go build -o gistnc-osx-amd64 ./src/github.com/festum/gistnc/g
istnc.go

Tricks

Ternary operator

c := map[bool]int{true: 1, false: 0} [5 > 4]
package main

import (
      "fmt"
)

func main() {
      // initialize a slice of int
      slice := []int{2, 3, 5, 7, 11, 13}

      // save the items of slice in map
      m := make(map[int]bool)
      for i := 0; i < len(slice); i++ {
              m[slice[i]] = true
      }

      // check if 5 is in slice by checking map
      if _, ok := m[5]; ok {
              fmt.Println("5 is in slice")
      } else {
              fmt.Println("5 is not in slice")
      }

      // check if 6 is in slice by checking map
      if _, ok := m[6]; ok {
              fmt.Println("6 is in slice")
      } else {
              fmt.Println("6 is not in slice")
      }
}

Check if a map contains a key

if val, ok := dict["foo"]; ok {
    //do something here
}
package main

import "fmt"
type Person struct {
     name string
     age int
 }

type People map[string]*Person

func main() {
  p := make(People)
  p["HM"] = &Person{"Hank McNamara", 39}
  p["HM"].age += 1
  fmt.Printf("age: %d\n", p["HM"].age)
}

Print struct/map: fmt.Printf("%+v\n", s)

JSON Encoding

String values encode as JSON strings coerced to valid UTF-8, replacing invalid bytes with the Unicode replacement rune. The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" to keep some browsers from misinterpreting JSON output as HTML. Ampersand "&" is also escaped to "\u0026" for the same reason.


Starter

Recipes

Boilplates


System

HTTP Server

  • (140k)mholt/caddy - Fast, cross-platform HTTP/2 web server with automatic HTTPS
  • (8.8k)kataras/iris - Be smart and embrace the future, today. The fastest web framework for Go in the Universe.
  • (3k)go-chi/chi - lightweight, idiomatic and composable router for building Go HTTP services

Serverless

  • (0.1k)yunspace/serverless-golang - Python-based AWS Lambda Go Shim for superior performance compared to Node.js shims
  • (3k)wallix/awless - A powerful, innovative and small surface command line interface (CLI) to manage Amazon Web Services.

CMS

  • (3k)ponzu-cms/ponzu - Headless CMS with automatic JSON API. Featuring auto-HTTPS from Let's Encrypt, HTTP/2 Server Push, and flexible server framework written in Go.

File Server

  • (2k)filebrowser - Web File Manager which can be used as middleware or standalone app.

Component

There is no reason to use net/HTTP's default ServeMux, which is very limited and does not have especially good performance. There are enough alternatives coming in every flavor, choose the one you like best. The broad range of functions of some of the frameworks comes at a high price in terms of performance. For example, Martini has great flexibility, but very bad performance. No router can beat the performance of the HttpRouter package, which currently dominates nearly all benchmarks.

ACL

  • (1.8k)casbin/casbin - An authorization library that supports access control models like ACL, RBAC, ABAC in Golang

Auth

  • (0.1k)danilopolani/gocialite - Social oAuth login in Go has never been so easy
  • (0.1k)qor/auth - a modular authentication system for the web has database password, GitHub, google, facebook, twitter authentication support, and it is fairly easy to add other support based on Auth's Provider interface
  • (0.2k)xyproto/permissions2 - Middleware for keeping track of users, login states and permissions

Database

  • (4k)jmoiron/sqlx - general purpose extensions to golang's database/sql

WebSocket

CLI

  • (7.2k)urfave/cli - A simple, fast, and fun package for building command line apps in Go
  • (6.9k)spf13/cobra - A Commander for modern Go CLI interactions
  • (0.9k)docopt.go - Pythonmic cli parser

Crawler

  • (1k)asciimoo/colly - Colly provides a clean interface to write any kind of crawler/scraper/spider.

Sync


References


Online Tools

@festum
Copy link
Author

festum commented Sep 10, 2019

Path control

The GOPATH environment variable specifies the location of your workspace. It is likely the only environment variable you'll need to set when developing Go code. To get started, create a workspace directory and set GOPATH accordingly. see: https://golang.org/doc/code.html#GOPATH

Import paths:

An import path is a string that uniquely identifies a package. A package's import path corresponds to its location inside a workspace or in a remote repository (explained below).

The packages from the standard library are given short import paths such as "fmt" and "net/http". For your own packages, you must choose a base path that is unlikely to collide with future additions to the standard library or other external libraries.

If you keep your code in a source repository somewhere, then you should use the root of that source repository as your base path. For instance, if you have a GitHub account at github.com/user, that should be your base path.

Note that you don't need to publish your code to a remote repository before you can build it. It's just a good habit to organize your code as if you will publish it someday. In practice, you can choose any arbitrary path name, as long as it is unique to the standard library and greater Go ecosystem.

  • Set go_package as option go_package = "github.com/username/repo/proto/v1";. It's the only way to resolve cross-dimension proto import.
  • Proto files suppose under the go project. Add replace github.com/username/repo/proto/v1 => ../proto in go.mod if using local proto files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment