lightweight, idiomatic and composable router for building Go HTTP services

Overview

chi

GoDoc Widget Travis Widget

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 that are kept maintainable as your project grows and changes. chi is built on the new context package introduced in Go 1.7 to handle signaling, cancelation and request-scoped values across a handler chain.

The focus of the project has been to seek out an elegant and comfortable design for writing REST API servers, written during the development of the Pressly API service that powers our public API service, which in turn powers all of our client-side applications.

The key considerations of chi's design are: project structure, maintainability, standard http handlers (stdlib-only), developer productivity, and deconstructing a large system into many small parts. The core router github.com/go-chi/chi is quite small (less than 1000 LOC), but we've also included some useful/optional subpackages: middleware, render and docgen. We hope you enjoy it too!

Install

go get -u github.com/go-chi/chi/v5

Features

  • Lightweight - cloc'd in ~1000 LOC for the chi router
  • Fast - yes, see benchmarks
  • 100% compatible with net/http - use any http or middleware pkg in the ecosystem that is also compatible with net/http
  • Designed for modular/composable APIs - middlewares, inline middlewares, route groups and sub-router mounting
  • Context control - built on new context package, providing value chaining, cancellations and timeouts
  • Robust - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see discussion)
  • Doc generation - docgen auto-generates routing documentation from your source to JSON or Markdown
  • Go.mod support - v1.x of chi (starting from v1.5.0), now has go.mod support (see CHANGELOG)
  • No external dependencies - plain ol' Go stdlib + net/http

Examples

See _examples/ for a variety of examples.

As easy as:

package main

import (
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

func main() {
	r := chi.NewRouter()
	r.Use(middleware.Logger)
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("welcome"))
	})
	http.ListenAndServe(":3000", r)
}

REST Preview:

Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs in JSON (routes.json) and in Markdown (routes.md).

I highly recommend reading the source of the examples listed above, they will show you all the features of chi and serve as a good form of documentation.

import (
  //...
  "context"
  "github.com/go-chi/chi/v5"
  "github.com/go-chi/chi/v5/middleware"
)

func main() {
  r := chi.NewRouter()

  // A good base middleware stack
  r.Use(middleware.RequestID)
  r.Use(middleware.RealIP)
  r.Use(middleware.Logger)
  r.Use(middleware.Recoverer)

  // Set a timeout value on the request context (ctx), that will signal
  // through ctx.Done() that the request has timed out and further
  // processing should be stopped.
  r.Use(middleware.Timeout(60 * time.Second))

  r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hi"))
  })

  // RESTy routes for "articles" resource
  r.Route("/articles", func(r chi.Router) {
    r.With(paginate).Get("/", listArticles)                           // GET /articles
    r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017

    r.Post("/", createArticle)                                        // POST /articles
    r.Get("/search", searchArticles)                                  // GET /articles/search

    // Regexp url parameters:
    r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug)                // GET /articles/home-is-toronto

    // Subrouters:
    r.Route("/{articleID}", func(r chi.Router) {
      r.Use(ArticleCtx)
      r.Get("/", getArticle)                                          // GET /articles/123
      r.Put("/", updateArticle)                                       // PUT /articles/123
      r.Delete("/", deleteArticle)                                    // DELETE /articles/123
    })
  })

  // Mount the admin sub-router
  r.Mount("/admin", adminRouter())

  http.ListenAndServe(":3333", r)
}

func ArticleCtx(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    articleID := chi.URLParam(r, "articleID")
    article, err := dbGetArticle(articleID)
    if err != nil {
      http.Error(w, http.StatusText(404), 404)
      return
    }
    ctx := context.WithValue(r.Context(), "article", article)
    next.ServeHTTP(w, r.WithContext(ctx))
  })
}

func getArticle(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()
  article, ok := ctx.Value("article").(*Article)
  if !ok {
    http.Error(w, http.StatusText(422), 422)
    return
  }
  w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

// A completely separate router for administrator routes
func adminRouter() http.Handler {
  r := chi.NewRouter()
  r.Use(AdminOnly)
  r.Get("/", adminIndex)
  r.Get("/accounts", adminListAccounts)
  return r
}

func AdminOnly(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    perm, ok := ctx.Value("acl.permission").(YourPermissionType)
    if !ok || !perm.IsAdmin() {
      http.Error(w, http.StatusText(403), 403)
      return
    }
    next.ServeHTTP(w, r)
  })
}

Router interface

chi's router is based on a kind of Patricia Radix trie. The router is fully compatible with net/http.

Built on top of the tree is the Router interface:

// Router consisting of the core routing methods used by chi's Mux,
// using only the standard net/http.
type Router interface {
	http.Handler
	Routes

	// Use appends one or more middlewares onto the Router stack.
	Use(middlewares ...func(http.Handler) http.Handler)

	// With adds inline middlewares for an endpoint handler.
	With(middlewares ...func(http.Handler) http.Handler) Router

	// Group adds a new inline-Router along the current routing
	// path, with a fresh middleware stack for the inline-Router.
	Group(fn func(r Router)) Router

	// Route mounts a sub-Router along a `pattern`` string.
	Route(pattern string, fn func(r Router)) Router

	// Mount attaches another http.Handler along ./pattern/*
	Mount(pattern string, h http.Handler)

	// Handle and HandleFunc adds routes for `pattern` that matches
	// all HTTP methods.
	Handle(pattern string, h http.Handler)
	HandleFunc(pattern string, h http.HandlerFunc)

	// Method and MethodFunc adds routes for `pattern` that matches
	// the `method` HTTP method.
	Method(method, pattern string, h http.Handler)
	MethodFunc(method, pattern string, h http.HandlerFunc)

	// HTTP-method routing along `pattern`
	Connect(pattern string, h http.HandlerFunc)
	Delete(pattern string, h http.HandlerFunc)
	Get(pattern string, h http.HandlerFunc)
	Head(pattern string, h http.HandlerFunc)
	Options(pattern string, h http.HandlerFunc)
	Patch(pattern string, h http.HandlerFunc)
	Post(pattern string, h http.HandlerFunc)
	Put(pattern string, h http.HandlerFunc)
	Trace(pattern string, h http.HandlerFunc)

	// NotFound defines a handler to respond whenever a route could
	// not be found.
	NotFound(h http.HandlerFunc)

	// MethodNotAllowed defines a handler to respond whenever a method is
	// not allowed.
	MethodNotAllowed(h http.HandlerFunc)
}

// Routes interface adds two methods for router traversal, which is also
// used by the github.com/go-chi/docgen package to generate documentation for Routers.
type Routes interface {
	// Routes returns the routing tree in an easily traversable structure.
	Routes() []Route

	// Middlewares returns the list of middlewares in use by the router.
	Middlewares() Middlewares

	// Match searches the routing tree for a handler that matches
	// the method/path - similar to routing a http request, but without
	// executing the handler thereafter.
	Match(rctx *Context, method, path string) bool
}

Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/{userID}) and wildcards (ie. /admin/*). URL parameters can be fetched at runtime by calling chi.URLParam(r, "userID") for named parameters and chi.URLParam(r, "*") for a wildcard parameter.

Middleware handlers

chi's middlewares are just stdlib net/http middleware handlers. There is nothing special about them, which means the router and all the tooling is designed to be compatible and friendly with any middleware in the community. This offers much better extensibility and reuse of packages and is at the heart of chi's purpose.

Here is an example of a standard net/http middleware where we assign a context key "user" the value of "123". This middleware sets a hypothetical user identifier on the request context and calls the next handler in the chain.

// HTTP middleware setting a value on the request context
func MyMiddleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // create new context from `r` request context, and assign key `"user"`
    // to value of `"123"`
    ctx := context.WithValue(r.Context(), "user", "123")

    // call the next handler in the chain, passing the response writer and
    // the updated request object with the new context value.
    //
    // note: context.Context values are nested, so any previously set
    // values will be accessible as well, and the new `"user"` key
    // will be accessible from this point forward.
    next.ServeHTTP(w, r.WithContext(ctx))
  })
}

Request handlers

chi uses standard net/http request handlers. This little snippet is an example of a http.Handler func that reads a user identifier from the request context - hypothetically, identifying the user sending an authenticated request, validated+set by a previous middleware handler.

// HTTP handler accessing data from the request context.
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
  // here we read from the request context and fetch out `"user"` key set in
  // the MyMiddleware example above.
  user := r.Context().Value("user").(string)

  // respond to the client
  w.Write([]byte(fmt.Sprintf("hi %s", user)))
}

URL parameters

chi's router parses and stores URL parameters right onto the request context. Here is an example of how to access URL params in your net/http handlers. And of course, middlewares are able to access the same information.

// HTTP handler accessing the url routing parameters.
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
  // fetch the url parameter `"userID"` from the request of a matching
  // routing pattern. An example routing pattern could be: /users/{userID}
  userID := chi.URLParam(r, "userID")

  // fetch `"key"` from the request context
  ctx := r.Context()
  key := ctx.Value("key").(string)

  // respond to the client
  w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
}

Middlewares

chi comes equipped with an optional middleware package, providing a suite of standard net/http middlewares. Please note, any middleware in the ecosystem that is also compatible with net/http can be used with chi's mux.

Core middlewares


chi/middleware Handler description
AllowContentEncoding Enforces a whitelist of request Content-Encoding headers
AllowContentType Explicit whitelist of accepted request Content-Types
BasicAuth Basic HTTP authentication
Compress Gzip compression for clients that accept compressed responses
ContentCharset Ensure charset for Content-Type request headers
CleanPath Clean double slashes from request path
GetHead Automatically route undefined HEAD requests to GET handlers
Heartbeat Monitoring endpoint to check the servers pulse
Logger Logs the start and end of each request with the elapsed processing time
NoCache Sets response headers to prevent clients from caching
Profiler Easily attach net/http/pprof to your routers
RealIP Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP
Recoverer Gracefully absorb panics and prints the stack trace
RequestID Injects a request ID into the context of each request
RedirectSlashes Redirect slashes on routing paths
RouteHeaders Route handling for request headers
SetHeader Short-hand middleware to set a response header key/value
StripSlashes Strip slashes on routing paths
Throttle Puts a ceiling on the number of concurrent requests
Timeout Signals to the request context when the timeout deadline is reached
URLFormat Parse extension from url and put it on request context
WithValue Short-hand middleware to set a key/value on the request context

Extra middlewares & packages

Please see https://github.com/go-chi for additional packages.


package description
cors Cross-origin resource sharing (CORS)
docgen Print chi.Router routes at runtime
jwtauth JWT authentication
hostrouter Domain/host based request routing
httplog Small but powerful structured HTTP request logging
httprate HTTP request rate limiter
httptracer HTTP request performance tracing library
httpvcr Write deterministic tests for external sources
stampede HTTP request coalescer

context?

context is a tiny pkg that provides simple interface to signal context across call stacks and goroutines. It was originally written by Sameer Ajmani and is available in stdlib since go1.7.

Learn more at https://blog.golang.org/context

and..

Benchmarks

The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark

Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x

BenchmarkChi_Param          	3075895	        384 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_Param5         	2116603	        566 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_Param20        	 964117	       1227 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_ParamWrite     	2863413	        420 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GithubStatic   	3045488	        395 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GithubParam    	2204115	        540 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GithubAll      	  10000	     113811 ns/op	    81203 B/op    406 allocs/op
BenchmarkChi_GPlusStatic    	3337485	        359 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GPlusParam     	2825853	        423 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GPlus2Params   	2471697	        483 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_GPlusAll       	 194220	       5950 ns/op	     5200 B/op     26 allocs/op
BenchmarkChi_ParseStatic    	3365324	        356 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_ParseParam     	2976614	        404 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_Parse2Params   	2638084	        439 ns/op	      400 B/op      2 allocs/op
BenchmarkChi_ParseAll       	 109567	      11295 ns/op	    10400 B/op     52 allocs/op
BenchmarkChi_StaticAll      	  16846	      71308 ns/op	    62802 B/op    314 allocs/op

Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc

NOTE: the allocs in the benchmark above are from the calls to http.Request's WithContext(context.Context) method that clones the http.Request, sets the Context() on the duplicated (alloc'd) request and returns it the new request object. This is just how setting context on a request in Go works.

Go module support & note on chi's versioning

  • Go.mod support means we reset our versioning starting from v1.5 (see CHANGELOG)
  • All older tags are preserved, are backwards-compatible and will "just work" as they
  • Brand new systems can run go get -u github.com/go-chi/chi as normal, or go get -u github.com/go-chi/[email protected] to install chi, which will install v1.x+ built with go.mod support, starting from v1.5.0.
  • For existing projects who want to upgrade to the latest go.mod version, run: go get -u github.com/go-chi/[email protected], which will get you on the go.mod version line (as Go's mod cache may still remember v4.x).
  • Any breaking changes will bump a "minor" release and backwards-compatible improvements/fixes will bump a "tiny" release.

Credits

We'll be more than happy to see your contributions!

Beyond REST

chi is just a http router that lets you decompose request handling into many smaller layers. Many companies use chi to write REST services for their public APIs. But, REST is just a convention for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server system or network of microservices.

Looking beyond REST, I also recommend some newer works in the field:

  • webrpc - Web-focused RPC client+server framework with code-gen
  • gRPC - Google's RPC framework via protobufs
  • graphql - Declarative query language
  • NATS - lightweight pub-sub

License

Copyright (c) 2015-present Peter Kieltyka

Licensed under MIT License

Comments
  • chi@v1.5.x mod issues

    [email protected] mod issues

    I have tried to switch to v1.5.0 from v4.1.2+incompatible but because the version number decreased it caused a downgrade of all other dependencies using chi. For example, I have started with this:

    git.example.com/commons/pkg/rest v1.3.2
    github.com/go-chi/chi v4.1.2+incompatible
    github.com/go-chi/render v1.0.1
    

    After running go get github.com/go-chi/[email protected] I got:

    git.example.com/commons/pkg/rest v1.1.0
    github.com/go-chi/chi v1.5.1
    github.com/go-chi/render v1.0.1
    

    As you can see my pkg/rest downgraded to some old version and such side effects can be very unexpected and unpleasant.

    Is there a reason why you decided to go with v1.5.x and not with a more traditional (and less dangerous) approach with v5 (also changing the package name to "/v5")? I'm not sure how to deal with this. In order to avoid those unexpected downgrades I have to have all of my dependencies migrated to v1.5, but unfortunately some of them not under my control.

    opened by umputun 76
  • HEAD for GET routes

    HEAD for GET routes

    Standard go http router allows to do HEAD requests to GET endpoints and return the same response but without body. Chi response 405 for GET routes with HEAD method.

    opened by alehano 58
  • Add go.mod file

    Add go.mod file

    Perhaps you've been following the discussion around the future of go dependency management? https://research.swtch.com/vgo

    Anyways, a few unrelated people, including me have tried to play with this experiment in our own projects and we are running into problems import chi due to it not having a go.mod file properly advertising the major version as v3.

    Any chance chi could partake in this experiment?

    I'm willing to give creating a proper chi go.mod file a go in a PR if so.

    opened by nicpottier 38
  • Render: managing request and response payload as types

    Render: managing request and response payload as types

    The render subpkg is pretty cool, and its great for managing the different kinds of content types a response may need, including streams. However, one of the important parts for a maintainable API is to manage the request and response payloads (the inputs and the outputs of an endpoint).

    For request payloads, one simple idea is to have a Bind() middleware used in chi's r.With() inline middleware routing, that will take a request body and unmarshal it to a kind of struct. Perhaps.

    type ArticleRequest {
      ID int64 `json:"id"`
      Name string `json:"name"`
    }
    // ...
    r.Get("/", index)
    r.With(render.Request(&ArticleRequest{})).Post("/articles", newArticle)
    

    .. something like that.. the render.Request() would make a new &ArticleRequest{} object and put it on the context under render.RequestKey or something.. its a decent plan, except it would require some kind of reflection to make the new &ArticleRequest{} object.

    As for response payloads, that should be much simpler, just..

    type ArticleResponse {
      *data.Article // embed the core Article type
      // add more methods on it..
    }
    //...
    render.Respond(w, r, &ArticleResponse{a})
    

    if someone has other ideas too that would be awesome!

    opened by pkieltyka 23
  • Question: HTTP Status Code

    Question: HTTP Status Code

    How do you set the HTTP Status code to something other than 200? I have a simple test case below and I'm unable to change the HTTP Status code. It is always a 200 OK. I can render my own text(JSON) but this still does NOT set the HTTP Status code to 401!

    package main
    func routes() *chi.Mux {
    	router := chi.NewRouter()
    	router.Use(
    		render.SetContentType(render.ContentTypeJSON),
    		middleware.Logger,
    		middleware.DefaultCompress,
    		middleware.RedirectSlashes,
    		middleware.Recoverer,
    		middleware.RealIP,
    	)
    	router.Get("/test", test)
    	return router
    }
    
    func test(w http.ResponseWriter, r *http.Request) {
    	w.WriteHeader(401)
    	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
    	render.Status(r, 401)
    	render.Render(w, r, ErrInvalidRequest())
    	return
    }
    type ErrResponse struct {
    	Err            error `json:"-"` // low-level runtime error
    	HTTPStatusCode int   `json:"-"` // http response status code
    
    	StatusText string `json:"status"`          // user-level status message
    	AppCode    int64  `json:"code,omitempty"`  // application-specific error code
    	ErrorText  string `json:"error,omitempty"` // application-level error message, for debugging
    }
    
    func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error {
    	render.Status(r, e.HTTPStatusCode)
    	return nil
    }
    
    func ErrInvalidRequest() render.Renderer {
    	return &ErrResponse{
    		// Err:            err,
    		HTTPStatusCode: 401,
    		StatusText:     "Invalid request.",
    		ErrorText:      "Invalid request.",
    	}
    }
    func main() {
    	router := routes()
    
    	walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
    		log.Printf("%s %s\n", method, route)
    		return nil
    	}
    	if err := chi.Walk(router, walkFunc); err != nil {
    		log.Panicf("Loggin err: %s\n", err.Error())
    	}
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.SrvPort), router))
    }
    

    Results - The text is changed but NOT the HTTP status code

    curl --request GET  --url http://localhost:8080/test -i
    HTTP/1.1 200 OK
    Content-Type: text/plain; charset=utf-8
    X-Content-Type-Options: nosniff
    Date: Tue, 19 Feb 2019 16:56:46 GMT
    Content-Length: 57
    
    Unauthorized
    {"status":401,"message":"Invalid request."}
    
    opened by milesje 18
  • Route params mixup

    Route params mixup

    First of all, love the project & the philosophy behind it - it's the perfect approach for Go 1.7+ IMO :thumbsup:

    But I'm having an issue with some supporting some legacy route formats. Requests map to the correct handlers but the parameters don't match up correctly. Here's a simplified example replacing the routes in TestTree to show the issue I'm having:

    func TestTree(t *testing.T) {
        hDate := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
        hCat := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    
        tr := &node{}
    
        tr.InsertRoute(mGET, "/items/:year/:month", hDate)
        tr.InsertRoute(mGET, "/items/:category", hCat)
    
        tests := []struct {
            r string            // input request path
            h http.Handler      // output matched handler
            p map[string]string // output params
        }{
            {r: "/items/2016/08", h: hDate, p: map[string]string{"year": "2016", "month": "08"}},
            {r: "/items/things", h: hCat, p: map[string]string{"category": "things"}},
        }
    

    That results in:

    --- FAIL: TestTree (0.00s)
        tree_test.go:59: input [1]: find '/items/things' expecting params:map[category:things] , got:map[year:things]
    

    Note that the /items/:category handler is correctly used but the parameter is called :year instead from the previous route.

    It looks like it's due to how the tree is built with the name of the parameter ignored so it then gets the name from the previous node:

    2016/08/23 10:40:56 [node 0 parent:0] typ:0 prefix: label: numEdges:1 isLeaf:false
    2016/08/23 10:40:56 [node 1 parent:0] typ:0 prefix:/items/ label:/ numEdges:1 isLeaf:false
    2016/08/23 10:40:56 [node 2 parent:1] typ:2 prefix::year label:: numEdges:1 isLeaf:true handler:map[512:<nil> 4:0x90450]
    2016/08/23 10:40:56 [node 3 parent:2] typ:0 prefix:/ label:/ numEdges:1 isLeaf:false
    2016/08/23 10:40:56 [node 4 parent:3] typ:2 prefix::month label:: numEdges:0 isLeaf:true handler:map[512:<nil> 4:0x90440]
    

    Is this simply not supported (I know many Go routers are strict about the route variations, others such as Echo cope better with suffix variations) or is it a bug?

    Many thanks.

    opened by CaptainCodeman 18
  • chi v3 roadmap

    chi v3 roadmap

    • [x] Fix bugs #78 #100 - relating to param keys overlapping between different endpoints
    • [x] Change param syntax from /articles/:id to /articles/{id} which improves expressiveness and delimiting (#167)
    • [x] Add support for regexp via /articles/{name:[a-z]}
    • [x] Add support for http.Handler for method routing, as in #176
    • [ ] Add a few param helpers with as chi.URLParamInt64(r, "bookID")
    • [x] Solve: Subrouters stacked, both using /{id} param key - panic? - #61
    opened by pkieltyka 15
  • Feature Request: Add URL param/query helpers

    Feature Request: Add URL param/query helpers

    In Chi, there is a helper to get the URL param. It has this signature:

    func Param(r *http.Request, key string) string
    

    I found I would often have to import strconv and convert a number param to an int. So I forked and created a method with a signature like so:

    func ParamInt(r *http.Request, key string) (int, error)
    

    Then I came across an issue with query parameters. I want to get a query parameter by name without having to parse it manually on each endpoint, and they also might be numbers that should be converted to ints.

    So I created these two signatures as well:

    func QueryString(r *http.Request, key string) string
    func QueryStringInt(r *http.Request, key string) (int, error)
    

    Here is the logic for everything:

    context.go:

    // Param returns the url parameter from a http.Request object.
    func Param(r *http.Request, key string) string {
    	if rctx := RouteContext(r.Context()); rctx != nil {
    		return rctx.Param(key)
    	}
    	return ""
    }
    
    // ParamInt will get a param from the request URL and attempt to parse it as an int.
    func ParamInt(r *http.Request, key string) (int, error) {
    	val, err := strconv.Atoi(Param(r, key))
    	if err != nil {
    		return 0, err
    	}
    	return val, nil
    }
    
    // Query will get a query parameter by key.
    func QueryString(r *http.Request, key string) string {
    	if rctx := RouteContext(r.Context()); rctx != nil {
    		return rctx.QueryString(key)
    	}
    	return ""
    }
    
    // QueryInt will get a query parameter by key and convert it to an int or return an error.
    func QueryStringInt(r *http.Request, key string) (int, error) {
    	val, err := strconv.Atoi(QueryString(r, key))
    	if err != nil {
    		return 0, err
    	}
    	return val, nil
    }
    

    Would you be willing to implement this so I don't need to use a forked version? :P

    opened by lansana 14
  • http: multiple response.WriteHeader calls due CloseNotify

    http: multiple response.WriteHeader calls due CloseNotify

    Adding CloseNotify middleware causing sometimes "multiple response.WriteHeader calls" warning in normal cases, i.e. request was not canceled by caller, but finished normally.

    Probably sometimes https://github.com/pressly/chi/blob/master/middleware/closenotify.go#L32 reached after normal completion? Not sure what the sequence, but I suspect client completed (and disconnected), connection closed and in select it gets closeNotifyCh

    opened by umputun 14
  • Wildcard

    Wildcard "*" behaves unexpectedly when used in conjunction with a nested router which has url parameters.

    Given:

    	r := chi.NewRouter()
    
    	r.Get("/bare/{param}", testStar)
    	r.Get("/bare/{param}/*", testStar)
    
    	r.Route("/case0", func(r chi.Router) {
    		r.Get("/{param}", testStar) // (1)
    		r.Get("/{param}/*", testStar) // (2)
    	})
    

    In case (1) wildcard "*" evaluates to browse/<path_segment> given url <path>/browse/<path_segment> In case (2) wildcard "*" evaluates to <sub_path_1>/<sub_path_2> given url <path>/browse/<path_segment>/<sub_path_1>/<sub_path_2>

    UPDATE: (Minimum reproducible example)

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    
    	"github.com/go-chi/chi"
    )
    
    func main() {
    	r := chi.NewRouter()
    
    	r.Get("/bare/{param}", testStar)
    	r.Get("/bare/{param}/*", testStar)
    
    	{ // case 0: Mounting a router using callback.
    		r.Route("/case0", func(r chi.Router) {
    			r.Get("/{param}", testStar)
    			r.Get("/{param}/*", testStar)
    		})
    	}
    	{ // case 1: Explicitly mounting a router.
    		sr := chi.NewRouter()
    		sr.Get("/{param}", testStar)
    		sr.Get("/{param}/*", testStar)
    		r.Mount("/case1", sr)
    	}
    
    	go http.ListenAndServe(":8080", r)
    
    	testCases := []struct {
    		url    string
    		params map[string]string
    	}{
    		{url: "value", params: map[string]string{
    			"param": "value",
    			"*":     "",
    		}},
    		{url: "value/sub/path", params: map[string]string{
    			"param": "value",
    			"*":     "sub/path",
    		}},
    	}
    
    	prefixes := []string{
    		"bare",
    		"case0",
    		"case1",
    	}
    
    	for _, prefix := range prefixes {
    		for _, tc := range testCases {
    			url := fmt.Sprintf("http://localhost:8080/%v/%v", prefix, tc.url)
    			printHttp(url, tc.params)
    		}
    	}
    }
    
    func testStar(rw http.ResponseWriter, r *http.Request) {
    	ctx := chi.RouteContext(r.Context())
    	fmt.Fprintf(rw, "pattern: %v\n", ctx.RoutePattern())
    	fmt.Fprintf(rw, "  param: (%v) *: (%v)", ctx.URLParam("param"), ctx.URLParam("*"))
    }
    
    func printHttp(url string, params map[string]string) {
    	r, err := http.Get(url)
    	if err != nil {
    		fmt.Println("url:", url, "\n\terr:", err)
    	}
    	data, _ := ioutil.ReadAll(r.Body)
    
    	fmt.Println("url:", url)
    
    	fmt.Println("--   ACTUAL:")
    	fmt.Println(string(data))
    
    	fmt.Println("-- EXPECTED:")
    	fmt.Printf("  param: (%v) *: (%v)", params["param"], params["*"])
    
    	fmt.Printf("\n\n")
    }
    

    Output:

    url: http://localhost:8080/bare/value
    --   ACTUAL:
    pattern: /bare/{param}
      param: (value) *: ()
    -- EXPECTED:
      param: (value) *: ()
    
    url: http://localhost:8080/bare/value/sub/path
    --   ACTUAL:
    pattern: /bare/{param}/*
      param: (value) *: (sub/path)
    -- EXPECTED:
      param: (value) *: (sub/path)
    
    url: http://localhost:8080/case0/value
    --   ACTUAL:
    pattern: /case0/{param}
      param: (value) *: (value)
    -- EXPECTED:
      param: (value) *: ()
    
    url: http://localhost:8080/case0/value/sub/path
    --   ACTUAL:
    pattern: /case0/{param}/*
      param: (value) *: (sub/path)
    -- EXPECTED:
      param: (value) *: (sub/path)
    
    url: http://localhost:8080/case1/value
    --   ACTUAL:
    pattern: /case1/{param}
      param: (value) *: (value)
    -- EXPECTED:
      param: (value) *: ()
    
    url: http://localhost:8080/case1/value/sub/path
    --   ACTUAL:
    pattern: /case1/{param}/*
      param: (value) *: (sub/path)
    -- EXPECTED:
      param: (value) *: (sub/path)
    
    opened by NonLogicalDev 13
  • chi v4

    chi v4

    chi is a pretty minimal library and has been hardened over the years, so v4 won't introduce any big changes other then a few minor things like..

    • [x] chi's Go support will be inline with Go's own support - #366 - therefore, we remove support for Go 1.7 and 1.8, and stop tracking 1.9
    • [x] return 404 for mux with empty routes #362
    • [x] additional check to ensure wildcard is at the end of a url pattern #333
    • [x] middleware: deprecate support for CloseNotifier - #347
    • [ ] Optimal Go module handling #302 - some community submissions: #364 #370 #379

    :)

    opened by pkieltyka 13
  • update _examples/logging to use new

    update _examples/logging to use new "slog"

    Go 1.20 will ship with new "slog" package. We can use it now via "golang.org/x/exp/slog"

    Let's upgrade https://github.com/go-chi/chi/blob/master/_examples/logging/main.go to use it instead

    opened by pkieltyka 0
  • Regex param doesn't match dots

    Regex param doesn't match dots

    Example program:

    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/http/httptest"
    
    	"github.com/go-chi/chi/v5"
    )
    
    func main() {
    	mux := chi.NewMux()
    	mux.Get("/{param:.+}.json", func(w http.ResponseWriter, r *http.Request) {
    		param := chi.URLParam(r, "param")
    		_, _ = fmt.Fprintf(w, "param=%s", param)
    	})
    
    	rec := httptest.NewRecorder()
    	req := httptest.NewRequest(http.MethodGet, "/param.json", nil)
    	mux.ServeHTTP(rec, req)
    
    	fmt.Printf("code: %d, body: %s\n", rec.Code, rec.Body)
    
    	rec = httptest.NewRecorder()
    	req = httptest.NewRequest(http.MethodGet, "/param.with.dots.json", nil)
    	mux.ServeHTTP(rec, req)
    
    	fmt.Printf("code: %d, body: %s\n", rec.Code, rec.Body)
    }
    

    Expected:

    code: 200, body: param=param
    code: 404, body: param=param.with.dots
    

    Got:

    code: 200, body: param=param
    code: 404, body: 404 page not found
    
    opened by Nyoroon 0
  • chi.Walk missing middlewares

    chi.Walk missing middlewares

    Hi,

    I was trying to use chi.Walk to get a report of all routes and the middlewares used by every route. I noticed that some of the middlewares were missing from some of the nested routes when using groups.

    Example routes:

    	r := chi.NewRouter()
    
    	r.Use(middleware.RequestID)
    
    	r.Get("/A", func(w http.ResponseWriter, r *http.Request) {})
    
    	r.Group(func(r chi.Router) {
    		r.Use(middleware.Timeout(2500 * time.Millisecond))
    
    		r.Get("/B", func(w http.ResponseWriter, r *http.Request) {})
    
    		r.Route("/C", func(r chi.Router) {
    
    			// Walk on the below route does not show the Timeout middleware
    			r.Get("/D", func(w http.ResponseWriter, r *http.Request) {})
    		})
    	})
    

    In the example above, I expected the C/D route to have both RequestId and Timeout middlewares. but chi.Walk only shows RequestID.

    It's possible I'm missing something here and this is behaving as expected.

    Go playground link: https://go.dev/play/p/HKh7cA35Bgx

    opened by ebabani 4
  • ContentCharset middleware should be skipped if ContentLength == 0

    ContentCharset middleware should be skipped if ContentLength == 0

    I could be wrong about this but I feel like the ContentCharset middleware should skip processing if the ContentLength == 0, just like the AllowContentType and AllowContentEncoding middleware. I also feel like it for consistency it should be AllowContentCharset? Some of the functions names in the content_charset.go file are confusing as well as they are named contentEncoding, etc.

    I can create a PR if you want.

    opened by lukasmalkmus 0
  • Empty character in regex not getting matched

    Empty character in regex not getting matched

    Hey there,

    In https://github.com/go-chi/chi/pull/224, a check was added to short-circuit and return no matching route if the param value is empty.

    I am trying the following regex in the router configuration to optionally match a route with an extension:

    /v1/resource/done{:(^$|.json)} to match either /v1/resource/done.json or /v1/resource/done

    Since the short-circuit kicks in because of the empty param value, the URL /v1/resource/done never matches that regex. Is there any other way to accomplish the same?

    Thanks!

    opened by tcprbs 1
Releases(v5.0.7)
  • v5.0.7(Nov 18, 2021)

  • v5.0.6(Nov 15, 2021)

  • v5.0.5(Oct 27, 2021)

    • Fix middleware.Recoverer under Go 1.17+ (again)
    • Update middleware.Heartbeat to respond to both HEAD and GET requests
    • History of changes, see: https://github.com/go-chi/chi/compare/v5.0.4...v5.0.5
    Source code(tar.gz)
    Source code(zip)
  • v5.0.4(Aug 29, 2021)

    • Fix middleware.Recoverer under Go 1.17+ (https://github.com/go-chi/chi/pull/633)
    • New in middleware: PageRoute and PathRewrite
    • New docs site! https://go-chi.io -- thank you @Shubhaankar-Sharma for all the amazing work :1st_place_medal: :100:
    • History of changes, see: https://github.com/go-chi/chi/compare/v5.0.3...v5.0.4
    Source code(tar.gz)
    Source code(zip)
  • v5.0.3(Apr 29, 2021)

  • v5.0.2(Mar 25, 2021)

  • v5.0.1(Mar 10, 2021)

  • v5.0.0(Feb 28, 2021)

    • chi v5, github.com/go-chi/chi/v5 introduces the adoption of Go's SIV to adhere to the current state-of-the-tools in Go.
    • chi v1.5.x did not work out as planned, as the Go tooling is too powerful and chi's adoption is too wide. The most responsible thing to do for everyone's benefit is to just release v5 with SIV, so I present to you all, chi v5 at github.com/go-chi/chi/v5. I hope someday the developer experience and ergonomics I've been seeking will still come to fruition in some form, see https://github.com/golang/go/issues/44550
    • History of changes: see https://github.com/go-chi/chi/compare/v1.5.4...v5.0.0
    Source code(tar.gz)
    Source code(zip)
  • v1.5.4(Feb 27, 2021)

    • Undo prior retraction in v1.5.3 as we prepare for v5.0.0 release
    • History of changes: see https://github.com/go-chi/chi/compare/v1.5.3...v1.5.4
    Source code(tar.gz)
    Source code(zip)
  • v1.5.3(Feb 21, 2021)

    • Update go.mod to go 1.16 with new retract directive marking all versions without prior go.mod support
    • History of changes: see https://github.com/go-chi/chi/compare/v1.5.2...v1.5.3
    Source code(tar.gz)
    Source code(zip)
  • v1.5.2(Feb 10, 2021)

    • Reverting allocation optimization as a precaution as go test -race fails.
    • Minor improvements, see history below
    • History of changes: see https://github.com/go-chi/chi/compare/v1.5.1...v1.5.2
    Source code(tar.gz)
    Source code(zip)
  • v1.5.1(Dec 6, 2020)

    • Performance improvement: removing 1 allocation by foregoing context.WithValue, thank you @bouk for your contribution (https://github.com/go-chi/chi/pull/555). Note: new benchmarks posted in README.
    • middleware.CleanPath: new middleware that clean's request path of double slashes
    • deprecate & remove chi.ServerBaseContext in favour of stdlib http.Server#BaseContext
    • plus other tiny improvements, see full commit history below
    • History of changes: see https://github.com/go-chi/chi/compare/v4.1.2...v1.5.1
    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Dec 6, 2020)

    • go.mod release and testing with Go's toolchain to ensure backwards-compatibility. See https://github.com/go-chi/chi/blob/master/CHANGELOG.md#v150-2020-11-12---now-with-gomod-support for full details. Thank you.
    • For existing projects who want to upgrade to the latest go.mod version, run: go get -u github.com/go-chi/[email protected], which will get you on the go.mod version line (as Go's mod cache may still remember v4.x).
    • Brand new systems can run go get -u github.com/go-chi/chi or go get -u github.com/go-chi/[email protected] to install chi, which will install v1.x+ built with go.mod support.
    Source code(tar.gz)
    Source code(zip)
  • v4.1.2(Jun 2, 2020)

    • fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution
    • fix to replace nested wildcards correctly in RoutePattern, thank you @@unmultimedio for your contribution
    • History of changes: see https://github.com/go-chi/chi/compare/v4.1.1...v4.1.2
    Source code(tar.gz)
    Source code(zip)
  • v4.1.1(Apr 16, 2020)

    • fix for issue https://github.com/go-chi/chi/issues/411 which allows for overlapping regexp route to the correct handler through a recursive tree search, thanks to @Jahaja for the PR/fix!
    • new middleware.RouteHeaders as a simple router for request headers with wildcard support
    • updated the FileServer example to a much better implementation, see https://github.com/go-chi/chi/blob/master/_examples/fileserver/main.go
    • History of changes: see https://github.com/go-chi/chi/compare/v4.1.0...v4.1.1
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Apr 1, 2020)

    • middleware.LogEntry: Write method on interface now passes the response header and an extra interface type useful for custom logger implementations.
    • middleware.WrapResponseWriter: minor fix
    • middleware.Recoverer: a bit prettier
    • History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0
    Source code(tar.gz)
    Source code(zip)
  • v4.0.4(Mar 24, 2020)

    • middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496)
    • a few minor improvements and fixes
    • History of changes: see https://github.com/go-chi/chi/compare/v4.0.3...v4.0.4
    Source code(tar.gz)
    Source code(zip)
  • v4.0.3(Jan 9, 2020)

    • core: fix regexp routing to include default value when param is not matched
    • middleware: rewrite of middleware.Compress
    • middleware: suppress http.ErrAbortHandler in middleware.Recoverer
    • History of changes: see https://github.com/go-chi/chi/compare/v4.0.2...v4.0.3
    Source code(tar.gz)
    Source code(zip)
  • v4.0.2(Feb 26, 2019)

  • v4.0.1(Jan 21, 2019)

  • v4.0.0(Jan 10, 2019)

    • chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8
    • router: respond with 404 on router with no routes (#362)
    • router: additional check to ensure wildcard is at the end of a url pattern (#333)
    • middleware: deprecate use of http.CloseNotifier (#347)
    • middleware: fix RedirectSlashes to include query params on redirect (#334)
    • History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0
    Source code(tar.gz)
    Source code(zip)
  • v3.3.4(Jan 8, 2019)

    Minor middleware improvements. No changes to core library/router. Moving v3 into its own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11

    History of changes: https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4

    Master will switch into v4, where we will only support Go versions inline with Go's own policy, https://golang.org/doc/devel/release.html#policy (aka, last 2 versions)

    Source code(tar.gz)
    Source code(zip)
  • v3.3.3(Aug 27, 2018)

  • v3.3.2(Dec 22, 2017)

    • Support to route trailing slashes on mounted sub-routers (#281)
    • middleware: new ContentCharset to check matching charsets. Thank you @csucu for your community contribution!
    Source code(tar.gz)
    Source code(zip)
  • v3.3.1(Nov 20, 2017)

    • middleware: new AllowContentType handler for explicit whitelist of accepted request Content-Types
    • middleware: new SetHeader handler for short-hand middleware to set a response header key/value
    • Minor bug fixes
    Source code(tar.gz)
    Source code(zip)
  • v3.3.0(Oct 10, 2017)

    • New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage
    • Deprecated LINK and UNLINK methods from the default list, please use chi.RegisterMethod("LINK") and chi.RegisterMethod("UNLINK") in an init() function
    Source code(tar.gz)
    Source code(zip)
  • v3.1.3(Jul 25, 2017)

    func Walk(r Routes, walkFn WalkFunc) error
    
    type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error
    
    Source code(tar.gz)
    Source code(zip)
  • v3.1.0(Jul 10, 2017)

    • Fix a few minor issues after v3 release
    • Move docgen sub-pkg to https://github.com/go-chi/docgen
    • Move render sub-pkg to https://github.com/go-chi/render
    • Add new URLFormat handler to chi/middleware sub-pkg to make working with url mime suffixes easier, ie. parsing /articles/1.json and /articles/1.xml. See comments in https://github.com/go-chi/chi/blob/master/middleware/url_format.go for example usage.
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Jun 23, 2017)

    • Major update to chi library with many exciting updates, but also some breaking changes
    • URL parameter syntax changed from /:id to /{id} for even more flexible routing, such as /articles/{month}-{day}-{year}-{slug}, /articles/{id}, and /articles/{id}.{ext} on the same router
    • Support for regexp for routing patterns, in the form of /{paramKey:regExp} for example: r.Get("/articles/{name:[a-z]+}", h) and chi.URLParam(r, "name")
    • Add Method and MethodFunc to chi.Router to allow routing definitions such as r.Method("GET", "/", h) which provides a cleaner interface for custom handlers like in _examples/custom-handler
    • Deprecating mux#FileServer helper function. Instead, we encourage users to create their own using file handler with the stdlib, see _examples/fileserver for an example
    • Add support for LINK/UNLINK http methods via r.Method() and r.MethodFunc()
    • Moved the chi project to its own organization, to allow chi-related community packages to be easily discovered and supported, at: https://github.com/go-chi
    • NOTE: please update your import paths to "github.com/go-chi/chi"
    • NOTE: chi v2 is still available at https://github.com/go-chi/chi/tree/v2
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Mar 30, 2017)

    • Minor improvements and update to the chi core library
    • Introduced a brand new chi/render sub-package to complete the story of building APIs to offer a pattern for managing well-defined request / response payloads. Please check out the updated _examples/rest example for how it works.
    • Added MethodNotAllowed(h http.HandlerFunc) to chi.Router interface
    Source code(tar.gz)
    Source code(zip)
Owner
go-chi
go-chi
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

Ernest Micklei 4.7k Dec 2, 2022
Gerasimos (Makis) Maropoulos 23.3k Nov 28, 2022
Couper is a lightweight API gateway designed to support developers in building and operating API-driven Web projects

Couper Couper is a lightweight API gateway designed to support developers in building and operating API-driven Web projects. Getting started The quick

Avenga 73 Nov 18, 2022
Lightweight web framework based on net/http.

Goweb Light weight web framework based on net/http. Includes routing middleware logging easy CORS (experimental) Goweb aims to rely only on the standa

Travis Harmon 33 Sep 27, 2022
Building basic API with go, gin and gorm

Project Description Terima kasih sudah berkunjung ke halaman repositori ini, repositori ini berisi basic RESTFUL API dengan menggunakan teknologi seba

dev-beard 1 Nov 20, 2021
🐶 Next generation building tool for nothing

Oscar ?? Next generation building tool for nothing Motivation Imitation is the sincerest form of flattery. Oscar is yet another nonsense activity gene

dongdong 67 Nov 24, 2022
Flexible E-Commerce Framework on top of Flamingo. Used to build E-Commerce "Portals" and connect it with the help of individual Adapters to other services.

Flamingo Commerce With "Flamingo Commerce" you get your toolkit for building fast and flexible commerce experience applications. A demoshop using the

Flamingo 376 Nov 27, 2022
⚡ Rux is an simple and fast web framework. support middleware, compatible http.Handler interface. 简单且快速的 Go web 框架,支持中间件,兼容 http.Handler 接口

Rux Simple and fast web framework for build golang HTTP applications. NOTICE: v1.3.x is not fully compatible with v1.2.x version Fast route match, sup

Gookit 83 Oct 3, 2022
A minimal framework to build web apps; with handler chaining, middleware support; and most of all standard library compliant HTTP handlers(i.e. http.HandlerFunc).

WebGo v4.1.3 WebGo is a minimalistic framework for Go to build web applications (server side) with zero 3rd party dependencies. Unlike full-fledged fr

Kamaleshwar 262 Dec 1, 2022
Muxie is a modern, fast and light HTTP multiplexer for Go. Fully compatible with the http.Handler interface. Written for everyone.

Muxie ?? ?? ?? ?? ?? ?? Fast trie implementation designed from scratch specifically for HTTP A small and light router for creating sturdy backend Go a

Gerasimos (Makis) Maropoulos 279 Oct 5, 2022
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 33 Sep 27, 2022
Vektor - Build production-grade web services quickly

Vektor enables development of modern web services in Go. Vektor is designed to simplify the development of web APIs by eliminating boilerplate, using secure defaults, providing plug-in points, and offering common pieces needed for web apps. Vektor is fairly opinionated, but aims to provide flexibility in the right places.

Suborbital 86 Sep 14, 2022
Implementing back-end services for blog application

Go Blog API Real-world examples implementing back-end services for blog application using Go programming language to build RESTful API, all routes and

Achmad Chun-Chun Winata Adi 49 Oct 28, 2022
Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqL Database and Clean Architecture

GOLANG FIBER API (CLEAN ARCHITECTURE) Best simple, lightweight, powerful and really fast Api with Golang (Fiber, REL, Dbmate) PostgreSqLDatabase using

Elias Champi 3 Sep 2, 2022
Simple and lightweight Go web framework inspired by koa

VOX A golang web framework for humans, inspired by Koa heavily. Getting started Installation Using the go get power: $ go get -u github.com/aisk/vox B

An Long 79 Sep 27, 2022
Dragon 🐲 🐲 🐲 is a lightweight high performance web framework with Go for the feature and comfortable develop.

Dragon project new link start dragon ab performance Dragon ?? ?? ?? is a lightweight high performance web framework with Go for the feature and comfor

azerothyang 0 Sep 6, 2022
skr: The lightweight and powerful web framework using the new way for Go.Another go the way.

skr Overview Introduction Documents Features Install Quickstart Releases Todo Pull Request Issues Thanks Introduction The lightweight and powerful web

go-the-way 1 Jan 11, 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,

Geofrey Ernest 2.2k Nov 26, 2022