🍐 Elegant Golang REST API Framework

Goyave Logo

Version Build Status Coverage Status Go Report

License Awesome Discord

An Elegant Golang Web Framework

Goyave is a progressive and accessible web application framework focused on REST APIs, aimed at making backend development easy and enjoyable. It has a philosophy of cleanliness and conciseness to make programs more elegant, easier to maintain and more focused.

Clean Code

Goyave has an expressive, elegant syntax, a robust structure and conventions. Minimalist calls and reduced redundancy are among the Goyave's core principles.

Fast Development

Develop faster and concentrate on the business logic of your application thanks to the many helpers and built-in functions.

Powerful functionalities

Goyave is accessible, yet powerful. The framework includes routing, request parsing, validation, localization, testing, authentication, and more!

Most golang frameworks for web development don't have a strong directory structure nor conventions to make applications have a uniform architecture and limit redundancy. This makes it difficult to work with them on different projects. In companies, having a well-defined and documented architecture helps new developers integrate projects faster, and reduces the time needed for maintaining them. For open source projects, it helps newcomers understanding the project and makes it easier to contribute. With Goyave, expect a full package with minimum setup.

Table of contents

Learning Goyave

The Goyave framework has an extensive documentation covering in-depth subjects and teaching you how to run a project using Goyave from setup to deployment.

Read the documentation

pkg.go.dev

Example project

Getting started

Requirements

  • Go 1.13+
  • Go modules

Install using the template project

You can bootstrap your project using the Goyave template project. This project has a complete directory structure already set up for you.

Linux / MacOS

$ curl https://raw.githubusercontent.com/System-Glitch/goyave/master/install.sh | bash -s github.com/username/projectname

Windows (Powershell)

> & ([scriptblock]::Create((curl "https://raw.githubusercontent.com/System-Glitch/goyave/master/install.ps1").Content)) -moduleName github.com/username/projectname

Run go run . in your project's directory to start the server, then try to request the hello route.

$ curl http://localhost:8080/hello
Hi!

There is also an echo route, with basic validation of the request body.

$ curl -H "Content-Type: application/json" -X POST -d '{"text":"abc 123"}' http://localhost:8080/echo
abc 123

Features tour

This section's goal is to give a brief look at the main features of the framework. It doesn't describe everything the framework has to offer, so don't consider this documentation. If you want a complete reference and documentation, head to pkg.go.dev and the official documentation.

Hello world from scratch

The example below shows a basic Hello world application using Goyave.

import "github.com/System-Glitch/goyave/v3"

func registerRoutes(router *goyave.Router) {
    router.Get("/hello", func(response *goyave.Response, request *goyave.Request) {
        response.String(http.StatusOK, "Hello world!")
    })
}

func main() {
    if err := goyave.Start(registerRoutes); err != nil {
        os.Exit(err.(*goyave.Error).ExitCode)
    }
}

Configuration

To configure your application, use the config.json file at your project's root. If you are using the template project, copy config.example.json to config.json. The following code is an example of configuration for a local development environment:

{
    "app": {
        "name": "goyave_template",
        "environment": "localhost",
        "debug": true,
        "defaultLanguage": "en-US"
    },
    "server": {
        "host": "127.0.0.1",
        "maintenance": false,
        "protocol": "http",
        "domain": "",
        "port": 8080,
        "httpsPort": 8081,
        "timeout": 10,
        "maxUploadSize": 10
    },
    "database": {
        "connection": "mysql",
        "host": "127.0.0.1",
        "port": 3306,
        "name": "goyave",
        "username": "root",
        "password": "root",
        "options": "charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=true&loc=Local",
        "maxOpenConnections": 20,
        "maxIdleConnections": 20,
        "maxLifetime": 300,
        "autoMigrate": false
    }
}

If this config file misses some config entries, the default values will be used.

All entries are validated. That means that the application will not start if you provided an invalid value in your config (for example if the specified port is not a number). That also means that a goroutine trying to change a config entry with the incorrect type will panic.
Entries can be registered with a default value, their type and authorized values from any package.

Getting a value:

config.GetString("app.name") // "goyave"
config.GetBool("app.debug") // true
config.GetInt("server.port") // 80
config.Has("app.name") // true

Setting a value:

config.Set("app.name", "my awesome app")

Learn more about configuration in the documentation.

Routing

Routing is an essential part of any Goyave application. Routes definition is the action of associating a URI, sometimes having parameters, with a handler which will process the request and respond to it. Separating and naming routes clearly is important to make your API or website clear and expressive.

Routes are defined in routes registrer functions. The main route registrer is passed to goyave.Start() and is executed automatically with a newly created root-level router.

func Register(router *goyave.Router) {
    // Register your routes here

    // With closure, not recommended
    router.Get("GET", "/hello", func(response *goyave.Response, r *goyave.Request) {
        response.String(http.StatusOK, "Hi!")
    })

    router.Get("/hello", myHandlerFunction)
    router.Post("/user", user.Register).Validate(user.RegisterRequest)
    router.Route("PUT|PATCH", "/user", user.Update).Validate(user.UpdateRequest)
    router.Route("POST", "/product", product.Store).Validate(product.StoreRequest).Middleware(middleware.Trim)
}

URIs can have parameters, defined using the format {name} or {name:pattern}. If a regular expression pattern is not defined, the matched variable will be anything until the next slash.

Example:

router.Get("/product/{key}", product.Show)
router.Get("/product/{id:[0-9]+}", product.ShowById)
router.Get("/category/{category}/{id:[0-9]+}", category.Show)

Route parameters can be retrieved as a map[string]string in handlers using the request's Params attribute.

func myHandlerFunction(response *goyave.Response, request *goyave.Request) {
    category := request.Params["category"]
    id, _ := strconv.Atoi(request.Params["id"])
    //...
}

Learn more about routing in the documentation.

Controller

Controllers are files containing a collection of Handlers related to a specific feature. Each feature should have its own package. For example, if you have a controller handling user registration, user profiles, etc, you should create a http/controller/user package. Creating a package for each feature has the advantage of cleaning up route definitions a lot and helps keeping a clean structure for your project.

A Handler is a func(*goyave.Response, *goyave.Request). The first parameter lets you write a response, and the second contains all the information extracted from the raw incoming request.

Handlers receive a goyave.Response and a goyave.Request as parameters.
goyave.Request can give you a lot of information about the incoming request, such as its headers, cookies, or body. Learn more here.
goyave.Response implements http.ResponseWriter and is used to write a response. If you didn't write anything before the request lifecycle ends, 204 No Content is automatically written. Learn everything about reponses here.

Let's take a very simple CRUD as an example for a controller definition: http/controller/product/product.go:

func Index(response *goyave.Response, request *goyave.Request) {
    products := []model.Product{}
    result := database.Conn().Find(&products)
    if response.HandleDatabaseError(result) {
        response.JSON(http.StatusOK, products)
    }
}

func Show(response *goyave.Response, request *goyave.Request) {
    product := model.Product{}
    result := database.Conn().First(&product, request.Params["id"])
    if response.HandleDatabaseError(result) {
        response.JSON(http.StatusOK, product)
    }
}

func Store(response *goyave.Response, request *goyave.Request) {
    product := model.Product{
        Name:  request.String("name"),
        Price: request.Numeric("price"),
    }
    if err := database.Conn().Create(&product).Error; err != nil {
        response.Error(err)
    } else {
        response.JSON(http.StatusCreated, map[string]uint{"id": product.ID})
    }
}

func Update(response *goyave.Response, request *goyave.Request) {
    product := model.Product{}
    db := database.Conn()
    result := db.Select("id").First(&product, request.Params["id"])
    if response.HandleDatabaseError(result) {
        if err := db.Model(&product).Update("name", request.String("name")).Error; err != nil {
            response.Error(err)
        }
    }
}

func Destroy(response *goyave.Response, request *goyave.Request) {
    product := model.Product{}
    db := database.Conn()
    result := db.Select("id").First(&product, request.Params["id"])
    if response.HandleDatabaseError(result) {
        if err := db.Delete(&product).Error; err != nil {
            response.Error(err)
        }
    }
}

Learn more about controllers in the documentation.

Middleware

Middleware are handlers executed before the controller handler. They are a convenient way to filter, intercept or alter HTTP requests entering your application. For example, middleware can be used to authenticate users. If the user is not authenticated, a message is sent to the user even before the controller handler is reached. However, if the user is authenticated, the middleware will pass to the next handler. Middleware can also be used to sanitize user inputs, by trimming strings for example, to log all requests into a log file, to automatically add headers to all your responses, etc.

func MyCustomMiddleware(next goyave.Handler) goyave.Handler {
    return func(response *goyave.Response, request *goyave.Request) {
        // Do something
        next(response, request) // Pass to the next handler
    }
}

To assign a middleware to a router, use the router.Middleware() function. Many middleware can be assigned at once. The assignment order is important as middleware will be executed in order.

router.Middleware(middleware.MyCustomMiddleware)

Learn more about middleware in the documentation.

Validation

Goyave provides a powerful, yet easy way to validate all incoming data, no matter its type or its format, thanks to a large number of validation rules.

Incoming requests are validated using rules set, which associate rules with each expected field in the request.

Validation rules can alter the raw data. That means that when you validate a field to be number, if the validation passes, you are ensured that the data you'll be using in your controller handler is a float64. Or if you're validating an IP, you get a net.IP object.

Validation is automatic. You just have to define a rules set and assign it to a route. When the validation doesn't pass, the request is stopped and the validation errors messages are sent as a response.

Rule sets are defined in the same package as the controller, typically in a separate file named request.go. Rule sets are named after the name of the controller handler they will be used with, and end with Request. For example, a rule set for the Store handler will be named StoreRequest. If a rule set can be used for multiple handlers, consider using a name suited for all of them. The rules for a store operation are often the same for update operations, so instead of duplicating the set, create one unique set called UpsertRequest.

Example: (http/controller/product/request.go)

var (
    StoreRequest validation.RuleSet = validation.RuleSet{
        "name":  {"required", "string", "between:3,50"},
        "price": {"required", "numeric", "min:0.01"},
        "image": {"nullable", "file", "image", "max:2048", "count:1"},
    }

    // ...
)

Once your rules sets are defined, you need to assign them to your routes using the Validate() method.

router.Post("/product", product.Store).Validate(product.StoreRequest)

Learn more about validation in the documentation.

Database

Most web applications use a database. In this section, we are going to see how Goyave applications can query a database, using the awesome Gorm ORM.

Database connections are managed by the framework and are long-lived. When the server shuts down, the database connections are closed automatically. So you don't have to worry about creating, closing or refreshing database connections in your application.

Very few code is required to get started with databases. There are some configuration options that you need to change though:

  • database.connection
  • database.host
  • database.port
  • database.name
  • database.username
  • database.password
  • database.options
  • database.maxOpenConnection
  • database.maxIdleConnection
  • database.maxLifetime
user := model.User{}
db := database.Conn()
db.First(&user)

fmt.Println(user)

Models are usually just normal Golang structs, basic Go types, or pointers of them. sql.Scanner and driver.Valuer interfaces are also supported.

func init() {
    database.RegisterModel(&User{})
}

type User struct {
    gorm.Model
    Name         string
    Age          sql.NullInt64
    Birthday     *time.Time
    Email        string  `gorm:"type:varchar(100);uniqueIndex"`
    Role         string  `gorm:"size:255"` // set field size to 255
    MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
    Num          int     `gorm:"autoIncrement"` // set num to auto incrementable
    Address      string  `gorm:"index:addr"` // create index with name `addr` for address
    IgnoreMe     int     `gorm:"-"` // ignore this field
}

Learn more about using databases in the documentation.

Localization

The Goyave framework provides a convenient way to support multiple languages within your application. Out of the box, Goyave only provides the en-US language.

Language files are stored in the resources/lang directory.

.
└── resources
    └── lang
        └── en-US (language name)
            ├── fields.json (optional)
            ├── locale.json (optional)
            └── rules.json (optional)

The fields.json file contains the field names translations and their rule-specific messages. Translating field names helps making more expressive messages instead of showing the technical field name to the user. Rule-specific messages let you override a validation rule message for a specific field.

Example:

{
    "email": {
        "name": "email address",
        "rules": {
            "required": "You must provide an :field."
        }
    }
}

The locale.json file contains all language lines that are not related to validation. This is the place where you should write the language lines for your user interface or for the messages returned by your controllers.

Example:

{
    "product.created": "The product have been created with success.",
    "product.deleted": "The product have been deleted with success."
}

The rules.json file contains the validation rules messages. These messages can have placeholders, which will be automatically replaced by the validator with dynamic values. If you write custom validation rules, their messages shall be written in this file.

Example:

{
    "integer": "The :field must be an integer.",
    "starts_with": "The :field must start with one of the following values: :values.",
    "same": "The :field and the :other must match."
}

When an incoming request enters your application, the core language middleware checks if the Accept-Language header is set, and set the goyave.Request's Lang attribute accordingly. Localization is handled automatically by the validator.

func ControllerHandler(response *goyave.Response, request *goyave.Request) {
    response.String(http.StatusOK, lang.Get(request.Lang, "my-custom-message"))
}

Learn more about localization in the documentation.

Testing

Goyave provides an API to ease the unit and functional testing of your application. This API is an extension of testify. goyave.TestSuite inherits from testify's suite.Suite, and sets up the environment for you. That means:

  • GOYAVE_ENV environment variable is set to test and restored to its original value when the suite is done.
  • All tests are run using your project's root as working directory. This directory is determined by the presence of a go.mod file.
  • Config and language files are loaded before the tests start. As the environment is set to test, you need a config.test.json in the root directory of your project.

This setup is done by the function goyave.RunTest, so you shouldn't run your test suites using testify's suite.Run() function.

The following example is a functional test and would be located in the test package.

import (
    "github.com/username/projectname/http/route"
    "github.com/System-Glitch/goyave/v3"
)

type CustomTestSuite struct {
    goyave.TestSuite
}

func (suite *CustomTestSuite) TestHello() {
    suite.RunServer(route.Register, func() {
        resp, err := suite.Get("/hello", nil)
        suite.Nil(err)
        suite.NotNil(resp)
        if resp != nil {
            defer resp.Body.Close()
            suite.Equal(200, resp.StatusCode)
            suite.Equal("Hi!", string(suite.GetBody(resp)))
        }
    })
}

func TestCustomSuite(t *testing.T) {
    goyave.RunTest(t, new(CustomTestSuite))
}

When writing functional tests, you can retrieve the response body easily using suite.GetBody(response).

resp, err := suite.Get("/get", nil)
suite.Nil(err)
if err == nil {
    defer resp.Body.Close()
    suite.Equal("response content", string(suite.GetBody(resp)))
}

URL-encoded requests:

headers := map[string]string{"Content-Type": "application/x-www-form-urlencoded; param=value"}
resp, err := suite.Post("/product", headers, strings.NewReader("field=value"))
suite.Nil(err)
if err == nil {
    defer resp.Body.Close()
    suite.Equal("response content", string(suite.GetBody(resp)))
}

JSON requests:

headers := map[string]string{"Content-Type": "application/json"}
body, _ := json.Marshal(map[string]interface{}{"name": "Pizza", "price": 12.5})
resp, err := suite.Post("/product", headers, bytes.NewReader(body))
suite.Nil(err)
if err == nil {
    defer resp.Body.Close()
    suite.Equal("response content", string(suite.GetBody(resp)))
}

Testing JSON response:

suite.RunServer(route.Register, func() {
    resp, err := suite.Get("/product", nil)
    suite.Nil(err)
    if err == nil {
        defer resp.Body.Close()
        json := map[string]interface{}{}
        err := suite.GetJSONBody(resp, &json)
        suite.Nil(err)
        if err == nil { // You should always check parsing error before continuing.
            suite.Equal("value", json["field"])
            suite.Equal(float64(42), json["number"])
        }
    }
})

The testing API has many more features such as record generators, factories, database helpers, a middleware tester, support for multipart and file uploads...

Learn more about testing in the documentation.

Status handlers

Status handlers are regular handlers executed during the finalization step of the request's lifecycle if the response body is empty but a status code has been set. Status handler are mainly used to implement a custom behavior for user or server errors (400 and 500 status codes).

The following file http/controller/status/status.go is an example of custom 404 error handling:

package status

import "github.com/System-Glitch/goyave/v3"

func NotFound(response *goyave.Response, request *goyave.Request) {
    if err := response.RenderHTML(response.GetStatus(), "errors/404.html", nil); err != nil {
        response.Error(err)
    }
}

Status handlers are registered in the router.

// Use "status.NotFound" for empty responses having status 404 or 405.
router.StatusHandler(status.NotFound, 404)

Learn more about status handlers in the documentation.

CORS

Goyave provides a built-in CORS module. CORS options are set on routers. If the passed options are not nil, the CORS core middleware is automatically added.

router.CORS(cors.Default())

CORS options should be defined before middleware and route definition. All of this router's sub-routers inherit CORS options by default. If you want to remove the options from a sub-router, or use different ones, simply create another cors.Options object and assign it.

cors.Default() can be used as a starting point for custom configuration.

options := cors.Default()
options.AllowedOrigins = []string{"https://google.com", "https://images.google.com"}
router.CORS(options)

Learn more about CORS in the documentation.

Authentication

Goyave provides a convenient and expandable way of handling authentication in your application. Authentication can be enabled when registering your routes:

import "github.com/System-Glitch/goyave/v3/auth"

//...

authenticator := auth.Middleware(&model.User{}, &auth.BasicAuthenticator{})
router.Middleware(authenticator)

Authentication is handled by a simple middleware calling an Authenticator. This middleware also needs a model, which will be used to fetch user information on a successful login.

Authenticators use their model's struct fields tags to know which field to use for username and password. To make your model compatible with authentication, you must add the auth:"username" and auth:"password" tags:

type User struct {
    gorm.Model
    Email    string `gorm:"type:char(100);uniqueIndex" auth:"username"`
    Name     string `gorm:"type:char(100)"`
    Password string `gorm:"type:char(60)" auth:"password"`
}

When a user is successfully authenticated on a protected route, its information is available in the controller handler, through the request User field.

func Hello(response *goyave.Response, request *goyave.Request) {
    user := request.User.(*model.User)
    response.String(http.StatusOK, "Hello " + user.Name)
}

Learn more about authentication in the documentation.

Rate limiting

Rate limiting is a crucial part of public API development. If you want to protect your data from being crawled, protect yourself from DDOS attacks, or provide different tiers of access to your API, you can do it using Goyave's built-in rate limiting middleware.

This middleware uses either a client's IP or an authenticated client's ID (or any other way of identifying a client you may need) and maps a quota, a quota duration and a request count to it. If a client exceeds the request quota in the given quota duration, this middleware will block and return HTTP 429 Too Many Requests.

The middleware will always add the following headers to the response:

  • RateLimit-Limit: containing the requests quota in the time window
  • RateLimit-Remaining: containing the remaining requests quota in the current window
  • RateLimit-Reset: containing the time remaining in the current window, specified in seconds
import "github.com/System-Glitch/goyave/v3/middleware/ratelimiter"

ratelimiterMiddleware := ratelimiter.New(func(request *goyave.Request) ratelimiter.Config {
    return ratelimiter.Config {
        RequestQuota:  100,
        QuotaDuration: time.Minute,
        // 100 requests per minute allowed
        // Client IP will be used as identifier
    }
})

router.Middleware(ratelimiterMiddleware)

Learn more about rate limiting in the documentation.

Contributing

Thank you for considering contributing to the Goyave framework! You can find the contribution guide in the documentation.

I have many ideas for the future of Goyave. I would be infinitely grateful to whoever want to support me and let me continue working on Goyave and making it better and better.

You can support me on Github Sponsor or Patreon.

Sponsor me!

I'm very grateful to my patrons and donators:

  • Ben Hyrman
  • Massimiliano Bertinetti

Contributors

A big "Thank you" to the Goyave contributors:

License

The Goyave framework is MIT Licensed. Copyright © 2019 Jérémy LAMBERT (SystemGlitch)

Owner
SystemGlitch
Craftsman, Software architect, Full-stack, Blockchain-related tech | Active open-source contributor
SystemGlitch
Comments
  • CLI Utility

    CLI Utility

    Proposal

    Create a CLI Utility called goyave-cli or gyv to make Goyave development easier. The utility would be an interactive, a bit like gh

    • Creation tools:
      • Create a project
        • The utility would ask a series of questions
        • Name of the project, name of the go module
        • Goyave version (latest by default)
        • Database used (changes the config options)
      • Create controllers
        • Resource controllers (full CRUD template, then fill the blanks)
        • Resource controller creation can take a model as reference to automatically create a request and the handlers
      • Create middleware
      • Create request (validation, may be created alongside resource controllers)
      • Create model (+ its resource controller)
    • Database commands:
      • Run seeders
      • Run migrations (may open to a more advanced migration system)
      • Clear / recreate database
    • Miscellaneous:
      • Detailed routes list
      • Run tests (maybe not needed, as it would duplicate go test)
      • Generate OpenAPI specification #42
      • Open documentation page (goyave.dev)

    Possible drawbacks

    None.

    Additional information

    This issue is a feature proposal and is meant to be discussed. If you have any other idea for this CLI Utility, feel free to suggest it! It is also a good candidate if you want to contribute to the project. This should probably be developed as a side-project.

  • v3 Discussion

    v3 Discussion

    v2.10.1 is expected to be the last release before v3, unless critical issues have to be addressed while developing this new major version.

    v3 will contain a number of breaking changes. To avoid moving to a new major version too often, required breaking changes are pushed back as much as possible and grouped into a single release.

    For the moment, v3 will contain the following changes:

    • Revamped configuration.
      • Maybe using another configuration format such as toml instead of json
      • Improving the way configuration is organized, by letting the possibility of grouping items together (for example grouping all database config into a single category) instead of accumulating a lot of entries at the root-level.
      • Ease plugin development by adding a possibility to add entries to the default config in an init() function
      • Make configuration checked at compile-time by using structures instead of maps.
    • Validation
      • Improve performance by parsing rule sets at startup time instead of during each request.
      • Add the ability to define validation in a more complex way, which will be more verbose, but more flexible and checked at compile-time. This would solve the comma escaping problem in rules parameters.
    • Route parameter converters ( #93 ) may require a breaking change.
    • ~Remove native handlers. I understand that it would make it harder to plug well known go http modules to Goyave and isolate it a bit from the rest of the environment, but this change would be beneficial in many ways:~
      • ~Performance improvements: no need to duplicate the request body~
      • ~Consistency: plugged modules may not work well with Goyave and there is no way to guarantee it. They probably don't follow the same design choices and principles, which can make programs inconsistent and sometimes unreliable.~
    • Convention changes
      • Remove the request package and move rule sets to a requests.go file inside the controller packages. This change would make route definition cleaner and easier, and the directory structure lighter. The requests package naming was inconvenient too.
    • More flexibility for databases. For example, the ability to add another dialect will be added.

    Before the development of v3 starts, please let me know if you think of any breaking or major change that you think would be good for the framework, and feel free to discuss the points above.

  • add rate limit middleware

    add rate limit middleware

    Description

    This pull requests adds a middleware for rate limiting functionality as proposed in Rate limiting #105.

    The current implementation can:

    • Work with authenticated users and add variable limits depending on a user plan
    • Be configured to count rates in relation to a route. This can be done by creating multiple middlewares with different configurations.

    Example

    
    // configure the middleware
    ratelimiterMiddleware := New(func(request *goyave.Request) LimiterConfig {
        return LimiterConfig {
    	RequestQuota:  2,
    	QuotaDuration: 10 * time.Minute,
        }
    })
    
    // apply to a route
    router.Get("/", hello.SayHi).Middleware(ratelimiterMiddleware)
    

    Possible drawbacks

    Please note that this is initial implementation, and I am still working to make it compliant to IETF spec. The headers are not yet implemented.

    Related issue(s)

    List all related issues here:

    Final Thoughts

    As it is my first time contributing a feature, I would love to get your valuable feedback and opinion. Also please feel free to provide better naming suggestions :-)

  • Benchmark framwork

    Benchmark framwork

    Hi all. Before using the framework I usually care about its performance. Have you tried this framework benchmark? You can refer here about benchmark: https://github.com/smallnest/go-web-framework-benchmark

  • OpenAPI generator

    OpenAPI generator

    Proposal

    Develop a module able to generate OpenAPI specification for Goyave applications by reading the main route registrer and validation rule sets.

    This feature could be included in the CLI Utility ( #39 )

    Possible drawbacks

    None.

    Additional information

    This issue is a feature proposal and is meant to be discussed. It is also a good candidate if you want to contribute to the project. This should probably be developed as a side-project.

  • Support JWT custom claims

    Support JWT custom claims

    Proposal

    I propose to support custom claims in the generated JWT token.

    The user fields to be included as claims could be specified by adding auth:"jwtInclude" (to follow current auth prefix).

    This would allow people putting custom information in the token, e.g. when thinking about username, some additional name fields or permissions (I saw you're planning support in the future).

    Possible drawbacks

    By default - none, if someone uses too many fields then the token will be heavy, but that's up to user of the framework.

    Additional information

    I think I can handle this contribution if you agree.

  • A real demo for benchmarks projects and to improve learning

    A real demo for benchmarks projects and to improve learning

    Proposal

    Your work on this project is amazing. I think it's the most accurate docs ever seen for a Go framework.

    But I think it would also be nice to create a "real demo" repo both to learn even better the "best practices" for proper use and to use as a benchmark in the following projects:

    • https://github.com/smallnest/go-web-framework-benchmark and
    • https://www.techempower.com/benchmarks

    And it's better for the author himself to use his framework for those benchmarks rather than someone who doesn't understand 100% how to best use it.

    What do you think about it?

    Thanks again for the wonderful work!

  • Rate limiting

    Rate limiting

    Proposal

    To make rate limiting easier, a middleware could be developed. This middleware would use a new config entry making it possible to adjust the rate easily. The real goal is to make a basic rate limiter middleware, which would cover most cases and be easy to use, mostly to protect from attacks.

    The middleware will have to correctly set the following headers, following this IETF spec:

    • RateLimit-Limit: the rate limit ceiling
    • RateLimit-Remaining: the number of requests left
    • RateLimit-Reset: the number of seconds until the quota resets

    Note: the IETF spec doesn't use the X- header prefix, unlike popular APIs such as Github's. It would be worth making some research to know if it would be better to add the X- prefix or keep it as defined in the IETF spec.

    In case of cancelled request because of limiting, the middleware would be stopping (don't call next()) and would respond with the HTTP status 429 Too Many Requests, in accordance with RFC 6585. The default status handler can be kept and developers will be able to define the behavior of their choice when a request is rate limited.

    Limiting will be done using an in-memory storage directly implemented in the middleware. A simple map and a struct containing the user's information (IP, time frame, remaining requests) are probably more than enough. The storage needs to be thread-safe, as requests can be served in parallel.

    The official go package golang.org/x/time/rate could be used, but it would need to be extended a little bit to support multiple clients. We want to limit IPs that keep requesting the server, but not the other legitimate users.

    I would like to start with a simple rate limiter, as mentioned above, but a more advanced rate limiter could be developed next to it later on. There is no design or clear requirements specification for it yet. For example, this advanced rate limiter could:

    • work with authenticated users and add variable limits depending on a user plan
    • be configured to count rates in relation to a route
    • use an external storage driver instead of the in-memory one explained earlier (support the database for example)
    • check the X-Forwarded-For and X-Real-IP headers if the application is running behind a reverse proxy
    • and more?

    Either way, this middleware has to be compliant with previously mentioned IETF spec.

    Possible drawbacks

    Rate limiting can have a slight impact of performance, and especially on memory usage if using in-memory storage. However, it is a mandatory feature for public APIs and well worth the upfront cost.

    Additional information

    This issue is a feature proposal and is meant to be discussed. It is also a good candidate if you want to contribute to the project.

  • Adding extra data to request

    Adding extra data to request

    Description

    Make a clear and detailed description of your changes, with the added benefits.

    Adding an extra data to Request that would allow middleware to process data that is not part of the request's body Example a new middleware that could wrap extra information to teh Request and later be processed.

    Possible drawbacks

    None

    Related issue(s)

    List all related issues here:

    • closes #117

    Additional information

  • Social Media and Outreach Activities

    Social Media and Outreach Activities

    Description

    Goyave is a very powerful framework but it is not reaching many gophers because you have not been leveraging social media and other platforms to reach developers.

    Possible drawbacks

    Reach more Gophers and more contributors

    Additional information

    I am a beginner in Golang and would love to contribute to this framework by promoting it to gophers.

  • Can't read json raw request

    Can't read json raw request

    Description

    Version: v2.2.0 Environment: Mac Catalina, go1.13.4 Reproduces: always

    Hi,

    I tried to handle raw json request with Request.Data["key"], but got nil, first I sent this as my request on postman body raw json:

    {
        "name": "John Doe",
        "tags": ["tag1", "tag2"]
    }
    

    Then I log print out the result, got this:

    map[]
    

    And I also tried the Request.Integer(), Request.Bool() and some of them got error like this:

    #Case Request.Integer()
    Field \"test\" is not an integer
    
    #Case Request.Bool()
    Field \"test\" is not a bool
    

    And this is log on terminal:

    2020/01/02 12:16:49 Server is running on port: 8090
    2020/01/02 12:16:54 Field "test" is not an integer
    2020/01/02 12:16:54 Field "test" is not an integer
    goroutine 35 [running]: runtime/debug.Stack(0x10e47ec, 0xc0000b2000, 0x2)
        /usr/local/Cellar/go/1.13.4/libexec/src/runtime/debug/stack.go:24 +0x9d
    runtime/debug.PrintStack()
        /usr/local/Cellar/go/1.13.4/libexec/src/runtime/debug/stack.go:16 +0x22
    github.com/System-Glitch/goyave/v2.(*Response).Error(0xc00000e0a0, 0x148e4a0, 0xc000020190, 0xc0000c3748, 0x100dbc6)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/response.go:118 +0x96
    github.com/System-Glitch/goyave/v2.recoveryMiddleware.func1.1(0xc00000e0a0)
        
     /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/middleware.go:25 +0x4b
    panic(0x148e4a0, 0xc000020190)
        /usr/local/Cellar/go/1.13.4/libexec/src/runtime/panic.go:679 +0x1b2
    log.Panicf(0x1549f5c, 0x1c, 0xc0000c3810, 0x1, 0x1)
        /usr/local/Cellar/go/1.13.4/libexec/src/log/log.go:345 +0xc0
    github.com/System-Glitch/goyave/v2.(*Request).Integer(...)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/request.go:127
    karmapala-backend/http/controller/user.Register(0xc00000e0a0, 0xc0002960a0)
        /Users/ridwankustanto/go/src/gitlab.com/ridwankustanto/karmapala-backend/http/controller/user/user.go:24 +0x137
    github.com/System-Glitch/goyave/v2.validateRequestMiddleware.func1(0xc00000e0a0, 0xc0002960a0)
        
    
    /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/middleware.go:106 +0xa5
    karmapala-backend/http/middleware.EnableCORS.func1(0xc00000e0a0, 0xc0002960a0)
        /Users/ridwankustanto/go/src/gitlab.com/ridwankustanto/karmapala-backend/http/middleware/cors.go:26 +0x580
    github.com/System-Glitch/goyave/v2.languageMiddleware.func1(0xc00000e0a0, 0xc0002960a0)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/middleware.go:139 +0xa9
     github.com/System-Glitch/goyave/v2.parseRequestMiddleware.func1(0xc00000e0a0, 0xc0002960a0)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/middleware.go:56 +0x118
    github.com/System-Glitch/goyave/v2.recoveryMiddleware.func1(0xc00000e0a0, 0xc0002960a0)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/middleware.go:29 +0x77
    github.com/System-Glitch/goyave/v2.(*Router).requestHandler(0xc0002545a0, 0x15f2220, 0xc0002c6000, 0xc0002a6200, 0x1561998, 0x0)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/router.go:120 +0x147
    github.com/System-Glitch/goyave/v2.(*Router).Route.func1(0x15f2220, 0xc0002c6000, 0xc0002a6200)
        /Users/ridwankustanto/go/pkg/mod/github.com/!system-!glitch/goyave/[email protected]/router.go:55 +0x5a
    net/http.HandlerFunc.ServeHTTP(0xc0002547e0, 0x15f2220, 0xc0002c6000, 0xc0002a6200)
        /usr/local/Cellar/go/1.13.4/libexec/src/net/http/server.go:2007 +0x44
    github.com/gorilla/mux.(*Router).ServeHTTP(0xc0000d8600, 0x15f2220, 0xc0002c6000, 0xc0002c0000)
        /Users/ridwankustanto/go/pkg/mod/github.com/gorilla/[email protected]/mux.go:212 +0xe2
    net/http.serverHandler.ServeHTTP(0xc000248380, 0x15f2220, 0xc0002c6000, 0xc0002c0000)
        /usr/local/Cellar/go/1.13.4/libexec/src/net/http/server.go:2802 +0xa4
    net/http.(*conn).serve(0xc0002b0000, 0x15f36e0, 0xc0002bc000)
        /usr/local/Cellar/go/1.13.4/libexec/src/net/http/server.go:1890 +0x875
    created by net/http.(*Server).Serve
        /usr/local/Cellar/go/1.13.4/libexec/src/net/http/server.go:2927 +0x38e
    

    Expected result:
    Got value on a specific key.

    Anyway, happy new year everyone!! 🎉🎉🎉

  • Instances

    Instances

    Proposal

    Instead of using globals for the server, the config, the database connection, etc, we could use structures. That way, every component that is currently more or less a "singleton" could have multiple properly separated instances.

    Using config as an example, the config.Load() function would return a *config.Config, a structure that would implement Get(), Set(), etc.

    This would require changes to fundamentals of the framework, such as the Handlers. Instead of receiving *goyave.Request and *goyave.Response, they would receive *goyave.Context, containing the request, the response, but also the config, a DB connection, etc.

    Benefits

    • Easier and parallel testing.
    • More flexible API thanks to using a context for handlers.
    • Multiple servers could run within the same app (not recommended though. Goyave was originally made thinking you would/should only run one REST service from your app).
    • Less mutex locks because some global values such as the database connection would be passed by the context.
    • Cleaner overall than using globals everywhere.

    Drawbacks

    • This would be a huge breaking change and would require a lot of work to upgrade from v3.
    • Some more code would be required in all components. From config.Get() to ctx.Config.Get() for example.
    • Some features would be less accessible from outside handlers, middleware, etc.
    • Will probably require a bit more code in the main function (need to specify config for example).

    Questions

    • Should the language package be rewritten using the same logic? This wouldn't have many benefits compared to the rest.
    • Are all these changes worth it? The main concrete advantage would be parallel testing, but at the cost of some ease of writing the application code.
    • Feel free to discuss and express your opinion.
  • Validation

    Validation "Required if"

    Proposal

    Add a "required_if" validation rule that would take another field as first parameter and check if its value is exactly the value of the second parameter (converted to proper type). If that is true, then the field under validation is required.

    "credit_card_number": {"required_if:payment_type,cc"}
    

    Possible drawbacks

    None.

  • Embedded static resources

    Embedded static resources

    Proposal

    Go's 1.16 new embed feature is great to bundle static resources into the binary, making it easier to distribute it. Currently, the frameworks relies on a non-interfaced filesystem, which makes it a bit harder to use embedded resources. Only the configuration supports it. I would like to provide a solution for developers wanting to embed their files.

    • Create a new handler similar to router.Static(), but taking io/fs.FS as a parameter.
    • Add the ability to use an io/fs.FS to load language files. (This may require a bit of refactoring into the framework so it's possible to load a language file by file)
    • Provide an abstracted version of the helper/filesystem package so it works with io/fs too.
    • Allow Request to use generic file system.

    On top of adding the ability to embed files, this would also let developers use virtual filesystems and fetch files from other sources more easily. However, the io/fs packge is still a bit young in my opinion, and lacks interfaces for writable FS. I wouldn't want to create a temporary non-standard interface that will probably not be compatible with a potential future standard one. So for now, this issue will stay on standby until more development on the io/fs package.

    ~~Because the embed feature is only available in go 1.16+ and that the io.FS interface is required for what I would like to achieve here, we can't build this into the framework without breaking compatibility with prior versions. I don't want to stop supporting older versions (yet), so we could create an external library. Later on, we could consider merging this library into the framework.~~ With v4, I dropped go versions < 1.16.

    Possible drawbacks

    None.

    Additional information

    This issue is a feature proposal and is meant to be discussed. The design is not set yet neither. It is also a good candidate if you want to contribute to the project.

  • JSON Schema Validator

    JSON Schema Validator

    Other than your input validator, consider the JSON Schema validator (jsonschema.org). Although similar, it has the advantage of write-once, run anywhere. The same validation schema could run on the browser before bothering the server. There would be no challenge keeping the two in synchronization: just use the same validator file.

  • Permission system (guards)

    Permission system (guards)

    Proposal

    Guards are an extension of the authentication system using user-defined fields from the authenticator's user model to allow or deny specific actions to an authenticated user.

    For example, a Goyave application will define a guard for forums moderation. A moderator is allowed to modify the posts of other users, but regular users aren't. A guard update-others will be implemented, using the IsModerator field from the user model and the route parameter to check if the user is a moderator or if its own post.

    Guards could be used in two ways:

    • As function calls, so they can be used anywhere such as in the middle of a controller handler
    • In middleware to protect routes directly without cluttering controller handlers.

    This system could support OAuth in some way too.

    The implementation is not defined yet, so feel free to discuss and suggest one.

    Possible drawbacks

    None.

    Additional information

    This issue is a feature proposal and is meant to be discussed. It is also a good candidate if you want to contribute to the project.

An idiomatic Go REST API starter kit (boilerplate) following the SOLID principles and Clean Architecture

Go RESTful API Starter Kit (Boilerplate) This starter kit is designed to get you up and running with a project structure optimized for developing REST

Jan 3, 2023
Flamingo Framework and Core Library. Flamingo is a go based framework for pluggable web projects. It is used to build scalable and maintainable (web)applications.
Flamingo Framework and Core Library. Flamingo is a go based framework for pluggable web projects. It is used to build scalable and maintainable (web)applications.

Flamingo Framework Flamingo is a web framework based on Go. It is designed to build pluggable and maintainable web projects. It is production ready, f

Jan 5, 2023
Golanger Web Framework is a lightweight framework for writing web applications in Go.

/* Copyright 2013 Golanger.com. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except

Nov 14, 2022
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Gin Web Framework Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks

Jan 2, 2023
package for building REST-style Web Services using Go

go-restful package for building REST-style Web Services using Google Go Code examples using v3 REST asks developers to use HTTP methods explicitly and

Jan 1, 2023
Simple HTTP and REST client library for Go

Resty Simple HTTP and REST client library for Go (inspired by Ruby rest-client) Features section describes in detail about Resty capabilities Resty Co

Jan 9, 2023
Eudore is the core of a golang lightweight web framework.

Eudore eudore是一个golang轻量级web框架核心,可以轻松扩展成一个技术栈专用框架,具有完整框架设计体系。 反馈和交流请加群组:QQ群373278915。 Features 易扩展:主要设计目标、核心全部解耦,接口即为逻辑。 简单:对象语义明确,框架代码量少复杂度低,无依赖库。 易用

Nov 7, 2022
a golang web mvc framework, like asp.net mvc.

goku goku is a Web Mvc Framework for golang, mostly like ASP.NET MVC. doc & api Installation To install goku, simply run go get github.com/QLeelulu/go

Dec 7, 2022
Fast and Reliable Golang Web Framework
Fast and Reliable Golang Web Framework

Gramework The Good Framework Gramework long-term testing stand metrics screenshot made with Gramework Stats Dashboard and metrics middleware What is i

Dec 18, 2022
The web framework for Golang
The web framework for Golang

uAdmin the Golang Web Framework Easy to use, blazing fast and secure. Originally open source by IntegrityNet Solutions and Services For Documentation:

Dec 24, 2022
A lightweight MVC framework for Go(Golang)

utron utron is a lightweight MVC framework in Go (Golang) for building fast, scalable and robust database-driven web applications. Features Postgres,

Dec 31, 2022
Golang CTF framework and exploit development module

Golang CTF framework and exploit development module

Dec 18, 2022
An ideally refined web framework for Go.

Air An ideally refined web framework for Go. High-performance? Fastest? Almost all web frameworks are using these words to tell people that they are t

Dec 15, 2022
Web framework for creating apps using Go in Google AppEngine

Welcome to app.go v3.0 app.go is a simple web framework for use in Google AppEngine. Just copy the app folder to your working folder and import it fro

Mar 21, 2021
Goal is a toolkit for high productivity web development in Go language in the spirit of Revel Framework that is built around the concept of code generation.

Goal Goal is a set of tools for high productivity web development in Go language. Goal, being mostly inspired by Revel Framework and its discussions,

Sep 27, 2021
A simple blog framework built with GO. Uses HTML files and a JSON dict to give you more control over your content.

Go-Blog A simple template based blog framework. Instructions Built for GO version: 1 See the Documentation or Getting Started pages in the wiki. Notes

Sep 10, 2022
Goldorak GO is a mini framework for the Go programming language. (unfinished dead code)

Goldorak Go =========== > Goldorak GO ! Rétrolaser en action > Goldorak GO !! Va accomplir ta mission > Dans l'infini > Des galaxies > Poursuis ta lu

Apr 29, 2021
A high level web-framework for Go

go-start is a high level web-framework for Go, like Django for Python or Rails for Ruby. Installation: go get github.com/ungerik/go-start Documentatio

Dec 24, 2022
A lightweight RESTful web framework for Go
A lightweight RESTful web framework for Go

Goweb A lightweight RESTful web framework for Go. For examples and usage, please read the Goweb API Documentation Read our Articles Who uses Goweb? "U

Dec 12, 2022