๐Ÿ Elegant Golang REST API Framework

Overview

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. Goyave is an opinionated framework, helping your applications keeping a uniform architecture and limit redundancy. With Goyave, expect a full package with minimum setup.

  • 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!
  • Reliability: Error reporting is made easy thanks to advanced error handling and panic recovery. The framework is deeply tested.

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://goyave.dev/install.sh | bash -s github.com/username/projectname

Windows (Powershell)

> & ([scriptblock]::Create((curl "https://goyave.dev/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 "goyave.dev/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")

Using environment variables:

{
  "database": {
    "host": "${DB_HOST}"
  }
}

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("/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"
    "goyave.dev/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 "goyave.dev/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 "goyave.dev/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 "goyave.dev/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.

Websocket

Goyave is using gorilla/websocket and adds a layer of abstraction to it to make it easier to use. You don't have to write the connection upgrading logic nor the close handshake. Just like regular HTTP handlers, websocket handlers benefit from reliable error handling and panic recovery.

Here is an example of an "echo" feature:

upgrader := websocket.Upgrader{}
router.Get("/websocket", upgrader.Handler(func(c *websocket.Conn, request *goyave.Request) error {
    for {
        mt, message, err := c.ReadMessage()
        if err != nil {
            return err
        }
        goyave.Logger.Printf("recv: %s", message)
        err = c.WriteMessage(mt, message)
        if err != nil {
            return fmt.Errorf("write: %w", err)
        }
    }
}))

Learn more about websockets 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, sponsors and donators:

  • Ben Hyrman
  • Massimiliano Bertinetti
  • ethicnology

Contributors

A big "Thank you" to the Goyave contributors:

License

The Goyave framework is MIT Licensed. Copyright ยฉ 2019 Jรฉrรฉmy LAMBERT (SystemGlitch)

Issues
  • 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.

    feature request 
    opened by System-Glitch 23
  • 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.

    opened by System-Glitch 16
  • 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 :-)

    feature request 
    opened by agbaraka 12
  • 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

    opened by phamtai97 11
  • 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.

    feature request 
    opened by System-Glitch 8
  • Benchmark router's goyave.

    Benchmark router's goyave.

    Benchmark router's goyave.

    I relied on the idea of โ€‹โ€‹the repo go-http-routing-benchmark to perform the benchmark.

    Related issue(s)

    • #11
    opened by phamtai97 7
  • 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!

    opened by frederikhors 6
  • 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.

    enhancement 
    opened by Morishiri 6
  • 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.

    contributions welcome feature request 
    opened by System-Glitch 5
  • 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!! ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰

    opened by ridwankustanto 4
  • 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.

    enhancement feature request 
    opened by System-Glitch 0
  • 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 as a parameter.
    • Add the ability to use an io.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.

    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.

    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.

    contributions welcome feature request 
    opened by System-Glitch 0
  • Logging not working when no route matched

    Logging not working when no route matched

    Version: v3.6.0 Environment: Ubuntu 20.04, Go 1.15.6
    Reproduces: always

    This is not really a bug. Requests that don't match any route (404 or 405) are not logged by the logging middleware, because middleware are only executed if a route is matching.

    How it occurred

    1. Register the logging middleware
    2. Request a non-existing URL or an existing one with the wrong HTTP method
    3. Notice nothing is logged

    Expected result:
    Logging should probably log all requests, including 404 and 405.

    How to reproduce

    router.Middleware(log.CombinedLogMiddleware())
    
    v4 
    opened by System-Glitch 0
  • 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.

    contributions welcome feature request 
    opened by snadrus 5
  • 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.

    contributions welcome feature request 
    opened by System-Glitch 14
Releases(v4.0.0-rc1)
  • v4.0.0-rc1(Aug 26, 2021)

    • Only the last two major versions of Go will be supported, starting with this release.

    Motivation: Supporting only the last two minor versions of the language follows Google's policy and allows for development of new modern features. It lets the framework be updated without it being considered a breaking change, and encourages to not use outdated versions of the language while newer ones contain security fixes.

    • Validation changes
      • Added validation rules before_now and after_now.
      • Added validation RuleSet and Rules composition for re-usability of redundant validated objects.
      • Changed signature of validation.RuleFunc to func(*validation.Context) bool. validation.Context provides more information and a cleaner and more flexible API.
        • Note: Converting rules now modify context.Value instead of directly modifying the input data.
      • Improved n-dimensional array validation with a new syntax.
        • The chevron-based syntax disappears in favor of a new more idiomatic syntax based on the name of the field, for example array[] will validate all the elements of the array array.
        • This new syntax allows for the validation of objects inside arrays.
      • Improved tree-like validation error messages structure, identifying precisely each element based on the expected structure. This structure also identifies each array element precisely with its index.
      • Removed the confirmed validation rule. Use same:path.to.field instead.
      • If a field is missing and not required, child fields are now skipped. This prevents required rules to not pass if the parent is not required and missing.

    Motivation: Although the validation package has seen some major improvements in v3, it was still far from perfect and was really lacking some important capabilities, such as the validation of objects in arrays. The array validation syntax was confusing, hard to read, and incompatible with the new syntax that was needed for object validation in arrays. We now have a more readable syntax, offering better capabilities. Because of how differently the validator works internally, we also had the opportunity to rework the error messages output, which now offers an impressive degree of precision. Precision in error messages gives a real edge over competing validation libraries.

    • Added helper/walk package to navigate complex data structure of unknown type (map[string]interface{}). This is used by the validator.
    • Migrated from dgrijalva/jwt-go library to the maintained golang-jwt/jwt.
    • Added ability to set default language strings from code using lang.SetDefaultLine(), lang.SetDefaultValidationRule() and lang.SetDefaultFieldName().
    • JWT: now uses standard sub instead of userid by default.
    • Reduced the amount of reflection in auth.FindColums.
    • Added support for promoted pointer of struct in auth.FindColumns and helper.Only.
    • database.Paginator improvements
      • Now exports its DB field so it can be re-assigned with new clauses.
      • Now exports its UpdatePageInfo() method so it can be called manually before the real query.
      • Now uses a Gorm session for page info query to prevent weird queries in some specific scenarios.
      • Fields names are now in camel case when json marshalling a Paginator.
    • Removed the DisallowNonValidatedFields middleware as it would be too expensive and complex to properly implement it with the new validation system.
    • Removed the unused name parameter in request.Cookie(). Thanks to @agbaraka for the contribution!
    • Fixed duplicate error message when a request accessor panicked.
    Source code(tar.gz)
    Source code(zip)
  • v3.11.0(Aug 17, 2021)

    • Exported validation.Rules.Check() and validation.Field.Check().
    • Added validation rule exists.
    • Changed default invalid credentials "en-US" language entry to "Invalid credentials.".
    • Added auth.Unauthorizer.
    Source code(tar.gz)
    Source code(zip)
  • v3.10.1(Jul 3, 2021)

    • Fixed native middleware don't replace http.Request and always used the initial one.
    • Fixed CORS options not working on subrouters. The main router's ones were always used.
    • Fixed route groups at root level don't match routes with / prefix.
    Source code(tar.gz)
    Source code(zip)
  • v3.10.0(Jun 28, 2021)

  • v3.9.1(May 28, 2021)

  • v3.9.0(May 28, 2021)

    • Fixed random inconsistencies in validation results when using rules comparing value to other fields.
    • Added RuleDefinition.ComparesFields to specify a rule can compare the field under validation with another field. Custom rules that do so should update their definition to avoid previously mentioned inconsistencies.
    • Fixed a bug causing the shutdown hook listening to SIGINT and SIGTERM to clear itself at server startup.
    • Fixed a bug causing fields inside objects to not be converted by type rules and leaving junk into the input data.
    • Handle dot-separated path for comparison validation rules.
    Source code(tar.gz)
    Source code(zip)
  • v3.8.0(Apr 29, 2021)

    • Added helper.Map. Thanks to @agbaraka for the contribution!
    • Added router.Group, which is an alias for router.Subrouter(""), aimed at improving route definition readability.
    • Address generation outputs 127.0.0.1 if 0.0.0.0 is set as server host.
    • Added validation Rule.IsType() and Rule.IsTypeDependent().
    • Added route.BuildURI() to be able to generate a full URI without the host and protocol prefix.
    • Added various accessors for routes and routers.
    • Fixed timing attack on config basic authenticator.
    • Exported goyave.NewRouter().
    • Minor memory improvements by re-aligning some structure fields.
    • Added support for custom JWT claims. (jwt.GenerateTokenWithClaims()). Thanks to @Morishiri for the contribution!
    • Added JWTController.TokenFunc to let developers define their own token generation logic. Thanks to @Morishiri for the contribution!
    • Added support for RSA and ECDSA JWT signatures.
    • Added JWTAuthenticator.ClaimName. This field defines what is the name if the claim used as an ID.
    • JWTAuthenticator now adds token claims to request.Extra if the provided token is valid. (key: "jwt_claims")
    • JWTController.Login now returns 401 Unauthorized instead of 422 Unprocessable Entity if provided credentials are invalid.
    Source code(tar.gz)
    Source code(zip)
  • v3.7.1(Mar 2, 2021)

    • Don't parse body if Content-Type is not set. Query params are still parsed. This change should drastically improve performance on requests with no body.
    • Set request.Data to nil (meaning parse failed) if query params couldn't be parsed.
    Source code(tar.gz)
    Source code(zip)
  • v3.7.0(Mar 1, 2021)

    • Added Optional flag to BasicAuthenticator and JWTAuthenticator.
    • Added support for database.options for the SQLite driver.
    • Added response.Hijack(). Therefore, *goyave.Response now implements http.Hijacker. Note that status handlers and middleware (requests finalization step in their lifecycle) will still work for hijacked connections.
    • Added websocket support.
    • Added shutdown hooks.
    • Added goyave.BaseURL().
    • Added config.LoadJSON(). This can be used to load configuration from an embedded configuration file using Go's 1.16 embed directive.
    • Static file serving will no longer print "no such file or directory" to the error logger.
    • Static file serving optimization: check file existence once instead of twice.
    Source code(tar.gz)
    Source code(zip)
  • v3.6.0(Dec 21, 2020)

    • Set content type to application/json; charset=utf-8 instead of application/json when using response.JSON().
    • Added default behavior for HEAD method: all GET routes can now match the HTTP HEAD method. This fixes 405 Method Not Allowed when requesting an URL with the HEAD method when no route explicitly matches the HEAD method. See the HEAD routing advice for more details.
    • Added request.ToStruct(), which puts the request's data into the given structure.
    Source code(tar.gz)
    Source code(zip)
  • v3.4.0(Nov 6, 2020)

    • Type-dependent rules validating integers (via the "integer" type rule) now share their validation message with the "numeric" type.
    • Added paginators.
    • Added helper.EscapeLike().
    • Performance improvement by caching critical config entries (protocol, maxUploadSize and defaultLanguage). This change leads to about 18% more requests per second. However, these entries cannot be dynamically changed anymore: a server restart will be needed.
    Source code(tar.gz)
    Source code(zip)
  • v3.3.1(Nov 2, 2020)

    • Fixed a bug in the validatior: the original value of type-converted fields was always used, leading to wrong validation of subsequent type-dependent rules.
    Source code(tar.gz)
    Source code(zip)
  • v3.3.0(Oct 30, 2020)

    • Added request.Extra. Thank you @gmgalvan for your contribution!
    • TestSuite now runs auto migrations if they're enabled before running the tests.
    • TestSuite don't load config anymore if it's already loaded. This allows you to load a test configuration file using LoadFrom() before calling goyave.RunTest().
    • response.JSON() doesn't remove hidden fields anymore. The use of json:"-" makes more sense and saves some execution time. Removing hidden fields manually is still possible.
    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Oct 21, 2020)

    • Added a way to customize the request's body field used by JWTController for the authentication process. (By default, "username" and "password" are used)
    • Added a new validation rule: unique.
    • Added helper.Only().
    • filesystem.File.Save() now creates directories if needed.
    Source code(tar.gz)
    Source code(zip)
  • v3.1.0(Sep 21, 2020)

  • v3.0.1(Sep 12, 2020)

  • v3.0.0(Sep 10, 2020)

    • Changed conventions:
      • validation.go and placeholders.go moved to a new http/validation package.
      • Validation rule sets are now located in a request.go file in the same package as the controller.
    • Validation system overhaul, allowing rule sets to be parsed only once instead of every time a request is received, giving better overall performance. This new system also allows a more verbose syntax for validation, solving the comma rule parameter value and a much easier use in your handlers.
      • Rule functions don't check required parameters anymore. This is now done when the rules are parsed at startup time. The amount of required parameters is given when registering a new rule.
      • Optimized regex-based validation rules by compiling expressions once.
      • A significant amount of untested cases are now tested.
      • The following rules now pass if the validated data type is not supported: greater_than, greater_than_equal, lower_than, lower_than_equal, size.
      • Type-dependent rules now try to determine what is the expected type by looking up in the rule set for a type rule. If no type rule is present, falls back to the inputted type. This change makes it so the validation message is correct even if the client didn't input the expected type.
      • Fixed a bug triggering a panic if the client inputted a non-array value in an array-validated field.
    • Routing has been improved by changing how validation and route-specific middleware are registered. The signature of the router functions have been simplified by removing the validation and middleware parameters from Route(), Get(), Post(), etc. This is now done through two new chainable methods on the Route: route.Validate() and route.Middleware().
    • Log Formatter now receive the length of the response (in bytes) instead of the full body.
    • Configuration system has been revamped.
      • Added support for tree-like configurations, allowing for better categorization. Nested values can be accessed using dot-separated path.
      • Improved validation: nested entries can now be validated too and all entries can have authorized values. Optional entries can now be validated too.
      • Improved support for slices. The validation system is also able to check slices.
      • Entries that are validated with the int type are now automatically converted from float64 if they don't have decimal places. It is no longer necessary to manually cast float64 that are supposed to be integers.
      • More openness: entries can be registered with a default value, their type and authorized values from any package. This allows config entries required by a specific package to be loaded only if the latter is imported.
      • Core configuration has been sorted in categories. This is a breaking change that will require you to update your configuration files.
      • Entries having a nil value are now considered unset.
      • Added accessors GetInt() and GetFloat().
      • Added slice accessors: GetStringSlice(), GetBoolSlice(), GetIntSlice(), GetFloatSlice()
      • Added LoadFrom(), letting you load a configuration file from a custom path.
      • Added the ability to use environment variables in configuration files.
      • Bug fix: config.IsLoaded() returned true even if config failed to load.
      • maxUploadSize config entry now supports decimal places.
    • Database improvements
      • Goyave has moved to GORM v2. Read the release note to learn more about what changed.
      • Protect the database instance with mutex.
      • database.Close() can now return errors.
      • Added database connection initializers.
      • Added the ability to regsiter new SQL dialects to use with GORM.
      • Use utf8mb4 by default in database options.
      • Added a short alias for database.GetConnection(): database.Conn().
      • Factories now use batch insert.
      • Factories now return interface{} instead of []interface{}. The actual type of the returned value is a slice of the the type of what is returned by your generator, so you can type-assert safely.
    • Status handlers improvements
      • Export panic and error status handlers so they can be expanded easily.
      • Added goyave.ValidationStatusHandler(), a status handler for validation errors. Therefore, the format in which validation errors are sent to the client can be customized by using your own status handler for the HTTP status 400 and 422.
    • goyave.Response improvements
      • response.Render and response.RenderHTML now execute and write the template to a bytes.Buffer instead of directly to the goyave.Response. This allows to catch and handle errors before the response header has been written, in order to return an error 500 if the template doesn't execute properly for example.
      • Added response.GetStacktrace(), response.IsEmpty() and response.IsHeaderWritten().
      • Re-organised the goyave.Response structure fields to save some memory.
      • Removed deprecated method goyave.CreateTestResponse(). Use goyave.TestSuite.CreateTestResponse() instead.
    • Recovery middleware now correctly handles panics with a nil value.
    • Test can now be run without the -p 1 flag thanks to a lock added to the goyave.RunTest method. Therefore, goyave.TestSuite still don't run in parallel but are safe to use with the typical test command.
    • Cache the regex used by helper.ParseMultiValuesHeader() to improve performance. This also improves the performance of the language middleware.
    • Bug fix: data under validation wasn't considered from JSON payload if the content type included the charset.
    • The Gzip middleware will now skip requests that have the Upgrade HTTP header set to any value.
    • response.String() and response.JSON() don't write header before calling Write anymore. This behavior prevented middleware and chained writers to alter the response headers.
    • Added goyave.PreWriter interface for chained writers needing to alter headers or status before they are written.
      • Even if this change is not breaking, it is recommended to update all your chained writers to call PreWrite() on their child writer if they implement the interface.
      • Thanks to this change, a bug with the gzip middleware has been fixed: header Content-Length wasn't removed, resulting in false information sent to the clients, which in turn failed to decompress the response.

    Read the upgrade guide here.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-rc2(Aug 31, 2020)

    • Use utf8mb4 by default in database options.
    • Added goyave.ValidationStatusHandler(), a status handler for validation errors. Therefore, the format in which validation errors are sent to the client can be customized by using your own status handler for the HTTP status 400 and 422.
    • Cache the regex used by helper.ParseMultiValuesHeader() to improve performance. This also improves the performance of the language middleware.
    • Added a short alias for database.GetConnection(): database.Conn().
    • Updated documentation.
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-rc1(Aug 24, 2020)

    • Changed conventions:
      • validation.go and placeholders.go moved to a new http/validation package.
      • Validation rule sets are now located in a request.go file in the same package as the controller.
    • Validation system overhaul, allowing rule sets to be parsed only once instead of every time a request is received, giving better overall performance. This new system also allows a more verbose syntax for validation, solving the comma rule parameter value and a much easier use in your handlers.
    • Routing has been improved by changing how validation and route-specific middleware are registered. The signature of the router functions have been simplified by removing the validation and middleware parameters from Route(), Get(), Post(), etc. This is now done through two new chainable methods on the Route: route.Validate() and route.Middleware().
    • Log Formatter now receive the length of the response (in bytes) instead of the full body.
    • Configuration system has been revamped.
      • Added support for tree-like configurations, allowing for better categorization. Nested values can be accessed using dot-separated path.
      • Improved validation: nested entries can now be validated too and all entries can have authorized values. Optional entries can now be validated too.
      • Entries that are validated with the int type are now automatically converted from float64 if they don't have decimal places. It is no longer necessary to manually cast float64 that are supposed to be integers.
      • More openness: entries can be registered with a default value, their type and authorized values from any package. This allows config entries required by a specific package to be loaded only if the latter is imported.
      • Core configuration has been sorted in categories. This is a breaking change that will require you to update your configuration files.
      • Entries having a nil value are now considered unset.
      • Added accessors GetInt() and GetFloat().
      • Added LoadFrom(), letting you load a configuration file from a custom path.
      • Added the ability to use environment variables in configuration files.
      • Bug fix: config.IsLoaded() returned true even if config failed to load.
    • Rule functions don't check required parameters anymore. This is now done when the rules are parsed at startup time. The amount of required parameters is given when registering a new rule.
    • Optimized regex-based validation rules by compiling expressions once.
    • A significant amount of untested cases are now tested.
    • Protect the database instance with mutex.
    • Recovery middleware now correctly handles panics with a nil value.
    • The following rules now pass if the validated data type is not supported: greater_than, greater_than_equal, lower_than, lower_than_equal, size.
    • Type-dependent rules now try to determine what is the expected type by looking up in the rule set for a type rule. If no type rule is present, falls back to the inputted type. This change makes it so the validation message is correct even if the client didn't input the expected type.
    • Fixed a bug triggering a panic if the client inputted a non-array value in an array-validated field.
    • response.Render and response.RenderHTML now execute and write the template to a bytes.Buffer instead of directly to the goyave.Response. This allows to catch and handle errors before the response header has been written, in order to return an error 500 if the template doesn't execute properly for example.
    • Test can now be run without the -p 1 flag thanks to a lock added to the goyave.RunTest method. Therefore, goyave.TestSuite still don't run in parallel but are safe to use with the typical test command.
    • maxUploadSize config entry now supports decimal places.
    • Added database connection initializers.
    • Re-organised the goyave.Response structure fields to save some memory.
    • Added the ability to regsiter new SQL dialects to use with GORM.
    • database.Close() can now return errors.
    • Added response.GetStacktrace(), response.IsEmpty() and response.IsHeaderWritten().
    • Removed deprecated method goyave.CreateTestResponse(). Use goyave.TestSuite.CreateTestResponse() instead.
    • Export panic and error status handlers so they can be expanded easily.
    Source code(tar.gz)
    Source code(zip)
  • v2.10.2(Jul 2, 2020)

  • v2.10.1(May 9, 2020)

    • Changed the behavior of response.File() and response.Download() to respond with a status 404 if the given file doesn't exist instead of panicking.
    • Improved error handling:
      • log.Panicf is not used anymore to print panics, removing possible duplicate logs.
      • Added error checks during automatic migrations.
      • goyave.Start() now exits the program with the following error codes:
        • 2: Panic (server already running, error when loading language files, etc)
        • 3: Configuration is invalid
        • 4: An error occurred when opening network listener
        • 5: An error occurred in the HTTP server

    This change will require a slightly longer main function but offers better flexibility for error handling and multi-services.

    if err := goyave.Start(route.Register); err != nil {
    	os.Exit(err.(*goyave.Error).ExitCode)
    }
    
    • Fixed a bug in TestSuite: HTTP client was re-created everytime getHTTPClient() was called.
    • Fixed testing documentation examples that didn't close http response body.
    • Documentation meta improvements.
    • Protect JSON requests with maxUploadSize.
    • The server will now automatically return 413 Payload Too Large if the request's size exceeds the maxUploadSize defined in configuration.
    • The request parsing middleware doesn't drain the body anymore, improving native handler compatibility.
    • Set a default status handler for all 400 errors.
    • Fixed a bug preventing query parameters to be parsed when the request had the Content-Type: application/json header.
    • Added a dark theme for the documentation. It can be toggled by clicking the moon icon next to the search bar.
    Source code(tar.gz)
    Source code(zip)
  • v2.10.0(Apr 23, 2020)

    • Added router Get, Post, Put, Patch, Delete and Options methods to register routes directly without having to specify a method string.
    • Added placeholder support in regular language lines.
    Source code(tar.gz)
    Source code(zip)
  • v2.9.0(Apr 8, 2020)

  • v2.8.0(Mar 25, 2020)

    • Added a built-in logging system.
      • Added a middleware for access logs using the Common Log Format or Combined Log Format. This allows custom formatters too.
      • Added three standard loggers: goyave.Logger, goyave.AccessLogger and goyave.ErrLogger
    • Fixed bug: the gzip middleware now closes underlying writer on close.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.1(Mar 12, 2020)

    • Changed MIME type of js and mjs files to text/javascript. This is in accordance with an IETF draft that treats application/javascript as obsolete.
    • Improved error handling: stacktrace wasn't relevant on unexpected panic since it was retrieved from the route's request handler therefore not including the real source of the panic. Stacktrace retrieval has been moved to the recovery middleware to fix this.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.0(Feb 23, 2020)

    • Added Request.Request() accessor to get the raw *http.Request.
    • Fixed a bug allowing non-core middleware applied to the root router to be executed when the "Not Found" or "Method Not Allowed" routes were matched.
    • Fixed a bug making route groups (sub-routers with empty prefix) conflict with their parent router when two routes having the same path but different methods are registered in both routers.
    • Added chained writers.
    • Added gzip compression middleware.
    • Added the ability to register route-specific middleware in Router.Static().
    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(Feb 19, 2020)

    • Custom router implementation. Goyave is not using gorilla/mux anymore. The new router is twice as fast and uses about 3 times less memory.
    • Now redirects to configured protocol if request scheme doesn't match.
    • Added named routes.
    • Added Route.GetFullURI() and Route.BuildURL() for dynamic URL generation.
    • Added helper.IndexOfStr() and helper.ContainsStr() for better performance when using string slices.
    • Moved from GoDoc to pkg.go.dev.
    • Print errors to stderr.
    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Feb 5, 2020)

    • Added an authentication system.
    • Various optimizations.
    • Various documentation improvements.
    • Added dbMaxLifetime configuration entry.
    • Moved from Travis CI to Github Actions.
    • Fixed a bug making duplicate log entries on error.
    • Fixed a bug preventing language lines containing a dot to be retrieved.
    • Fixed TestSuite.GetJSONBody() not working with structs and slices.
    • Added TestSuite.ClearDatabaseTables().
    • Added Config.Has() and Config.Register() to check for the existence of a config entry and to allow custom config entries valdiation.
    • Added Request.BearerToken().
    • Added Response.HandleDatabaseError() for easier database error handling and shorter controller handlers.
    Source code(tar.gz)
    Source code(zip)
  • v2.4.3(Jan 15, 2020)

    • Improved string validation by taking grapheme clusters into consideration when calculating length.
    • lang.LoadDefault now correctly creates a fresh language map and clones the default en-US language. This avoids the default language entries to be overridden permanently.
    Source code(tar.gz)
    Source code(zip)
Owner
Goyave
๐Ÿ Elegant Golang REST API Framework
Goyave
๐Ÿ Elegant Golang REST API Framework

An Elegant Golang Web Framework Goyave is a progressive and accessible web application framework focused on REST APIs, aimed at making backend develop

Goyave 892 Sep 22, 2021
Yet Another REST Framework

YARF: Yet Another REST Framework YARF is a fast micro-framework designed to build REST APIs and web services in a fast and simple way. Designed after

null 62 Jun 30, 2021
SendGrid's Golang HTTP Client for calling APIs

Quickly and easily access any RESTful or RESTful-like API. If you are looking for the SendGrid API client library, please see this repo. Announcements

Twilio SendGrid 147 Sep 22, 2021
:zap: Go web framework benchmark

go-web-framework-benchmark This benchmark suite aims to compare the performance of Go web frameworks. It is inspired by Go HTTP Router Benchmark but t

smallnest 1.6k Sep 24, 2021
A small and evil REST framework for Go

go-rest A small and evil REST framework for Go Reflection, Go structs, and JSON marshalling FTW! go get github.com/ungerik/go-rest import "github.com/

Erik Unger 125 Feb 11, 2021
lightweight, idiomatic and composable router for building Go HTTP services

chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's especially good at helping you write large REST API services

go-chi 10.1k Sep 21, 2021
A REST framework for quickly writing resource based services in Golang.

What is Resoursea? A high productivity web framework for quickly writing resource based services fully implementing the REST architectural style. This

Resoursea 31 Feb 21, 2020
Simple web framework for go, still quite beta at this point

WFDR Framework - Beta Release New 18/Feb/2012: Updated for go 1.0, new directory layout to take advantage of the go build tool. Background There's a m

null 23 Feb 11, 2021
โšก๏ธ Express inspired web framework written in Go

Fiber is an Express inspired web framework built on top of Fasthttp, the fastest HTTP engine for Go. Designed to ease things up for fast development w

Fiber 15.4k Sep 22, 2021
Gerasimos (Makis) Maropoulos 21.2k Sep 18, 2021
A secure, flexible, rapid Go web framework

A secure, flexible, rapid Go web framework Visit aah's official website https://aahframework.org to learn more News v0.12.3 released and tagged on Feb

aah framework 654 Sep 20, 2021
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

Flamingo 232 Sep 20, 2021
Gin is a HTTP web framework written in Go (Golang).

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-Gonic 51.6k Sep 23, 2021
๐Ÿ Elegant Golang Web Framework

Goyave Template A template project to get started with the Goyave framework. Getting Started Requirements Go 1.13+ Go modules Running the project Firs

Goyave 13 Aug 17, 2021
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

golanger 302 Aug 16, 2021
The jin is a simplified version of the gin web framework that can help you quickly understand the core principles of a web framework.

jin About The jin is a simplified version of the gin web framework that can help you quickly understand the core principles of a web framework. If thi

null 7 Sep 19, 2021
Hexya business application development framework

Hexya Hexya is an open source ERP and a business application development framework written in Go. This repository houses the business application deve

Hexya 318 Sep 2, 2021
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

null 366 Sep 10, 2021
go-zero is a web and rpc framework written in Go. It's born to ensure the stability of the busy sites with resilient design. Builtin goctl greatly improves the development productivity.

go-zero English | ็ฎ€ไฝ“ไธญๆ–‡ 0. what is go-zero go-zero is a web and rpc framework that with lots of engineering practices builtin. Itโ€™s born to ensure the

ๅฅฝๆœชๆฅๆŠ€ๆœฏ 10.9k Sep 17, 2021