{"id":37103399,"url":"https://github.com/sktylr/routeit","last_synced_at":"2026-01-14T12:31:16.421Z","repository":{"id":312587884,"uuid":"1001719324","full_name":"sktylr/routeit","owner":"sktylr","description":"routeit is a lightweight web framework built in go","archived":false,"fork":false,"pushed_at":"2025-11-28T16:21:42.000Z","size":1874,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-30T20:47:02.512Z","etag":null,"topics":["go","http","http-library"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/sktylr/routeit","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sktylr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-13T22:09:07.000Z","updated_at":"2025-11-28T16:21:45.000Z","dependencies_parsed_at":"2025-08-31T19:09:55.361Z","dependency_job_id":"68e22e52-b43b-4679-a84a-d61c3256019a","html_url":"https://github.com/sktylr/routeit","commit_stats":null,"previous_names":["sktylr/routeit"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/sktylr/routeit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sktylr%2Frouteit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sktylr%2Frouteit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sktylr%2Frouteit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sktylr%2Frouteit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sktylr","download_url":"https://codeload.github.com/sktylr/routeit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sktylr%2Frouteit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28420775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","http","http-library"],"created_at":"2026-01-14T12:31:15.679Z","updated_at":"2026-01-14T12:31:16.406Z","avatar_url":"https://github.com/sktylr.png","language":"Go","readme":"## routeit [![Go Reference](https://pkg.go.dev/badge/github.com/sktylr/routeit.svg)](https://pkg.go.dev/github.com/sktylr/routeit) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![routeit](https://github.com/sktylr/routeit/actions/workflows/main.yml/badge.svg)](https://github.com/sktylr/routeit/actions/workflows/main.yml) [![examples](https://github.com/sktylr/routeit/actions/workflows/examples.yml/badge.svg)](https://github.com/sktylr/routeit/actions/workflows/examples.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/sktylr/routeit)](https://goreportcard.com/report/github.com/sktylr/routeit)\n\n`routeit` is a lightweight web framework built in Go that conforms to the [HTTP/1.1](https://www.rfc-editor.org/rfc/rfc9112.html) spec.\nThis is meant to be exploratory and educational and is not fit for production purposes.\n`routeit` can be seen as a simpler version of [`net/http`](https://pkg.go.dev/net/http) that allows for building simple servers and has been built from the ground up with no usage of non-standard libraries.\nIt includes some features, such as parameterised dynamic routing, built-in error handling and JSON handling that `net/http` does not handle out of the box.\n\n### Getting Started\n\nBefore beginning, make sure you have installed Go and setup your [GOPATH](http://golang.org/doc/code.html#GOPATH) correctly.\nCreate a `.go` file called `server.go` and add the following code:\n\n```go\npackage main\n\nimport \"github.com/sktylr/routeit\"\n\ntype HelloWorld struct {\n\tHello string `json:\"hello\"`\n}\n\nfunc main() {\n\tsrv := routeit.NewServer(routeit.ServerConfig{Debug: true})\n\tsrv.RegisterRoutes(routeit.RouteRegistry{\n\t\t\"/hello\": routeit.Get(func(rw *routeit.ResponseWriter, req *routeit.Request) error {\n\t\t\tbody := HelloWorld{Hello: \"World\"}\n\t\t\treturn rw.Json(body)\n\t\t}),\n\t})\n\tsrv.StartOrPanic()\n}\n```\n\nInstall `routeit` from the command line:\n\n```bash\n$ go get github.com/sktylr/routeit\n```\n\nRun the server:\n\n```bash\n$ go run server.go\n```\n\nThe server is now running on `localhost:8080` and will respond to `GET` requests on the `/hello` endpoint.\n\n```bash\n$ curl -D - http://localhost:8080/hello\nHTTP/1.1 200 OK\nContent-Type: application/json\nDate: Mon, 01 Sep 2025 12:06:32 GMT\nServer: routeit\nContent-Length: 17\n\n{\"hello\":\"World\"}\n```\n\nCheck out the [`examples/`](/examples/) directory for further examples of using `routeit`'s features.\n\n### Documentation\n\nDocumentation for the latest released version can be found on [`pkg.go.dev/github.com/sktylr/routeit`](https://pkg.go.dev/github.com/sktylr/routeit).\nThe repository also contains a [`docs/`](/docs/) directory that contains notes and further details regarding the library.\n\nDocumentation for the current development version can be generated using [`godoc`](https://pkg.go.dev/golang.org/x/tools/cmd/godoc) and requires the repository to be cloned.\n\n```bash\n# Install the package if not already installed\n$ go install golang.org/x/tools/cmd/godoc@latest\n\n# Run the documentation server on port 3000\n$ godoc -http=:3000\n```\n\nThe documentation can now be viewed at [`localhost:3000/pkg/github.com/sktylr/routeit/`](http://localhost:3000/pkg/github.com/sktylr/routeit/).\n\n### Features\n\n**HTTP Version Support**: Only HTTP/1.1 is supported. My implementation is mostly based off [RFC-9112](https://httpwg.org/specs/rfc9112.html) and [Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference) developer specs.\n\n| HTTP Method | Supported? | Notes                                                                                                                           |\n| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| GET         | ✅         |                                                                                                                                 |\n| HEAD        | ✅         | Cannot be implemented by the integrator, it is baked into the server implementation.                                            |\n| POST        | ✅         |                                                                                                                                 |\n| PUT         | ✅         |                                                                                                                                 |\n| DELETE      | ✅         |                                                                                                                                 |\n| CONNECT     | ❌         | Will never be implemented since I will not support tunnelling                                                                   |\n| OPTIONS     | ✅         | Baked into the server implementation.                                                                                           |\n| TRACE       | ✅         | Baked into the server implementation but is defaulted OFF. Can be turned on using the `AllowTraceRequests` configuration option |\n| PATCH       | ✅         |                                                                                                                                 |\n\n| Content Types      | Request supported? | Response supported? | Notes                                                                                                                                                                                                              |\n| ------------------ | ------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `application/json` | ✅                 | ✅                  | Parsing and encoding is handled automatically by `routeit`                                                                                                                                                         |\n| `text/plain`       | ✅                 | ✅                  |                                                                                                                                                                                                                    |\n| ...                | ✅                 | ✅                  | Any request or response type can be supported, but the integrator must handling the parsing and marshalling. The `ResponseWriter.RawWithContentType` and `Request.BodyFromRaw` methods can be used correspondingly |\n\n#### HTTPS\n\n`routeit` supports both HTTP and HTTPS.\nUnlike with `net/http` and other libraries, a single `routeit` server can support both HTTP and HTTPS without needing to explicitly manage the separate threads and ports.\nThe `HttpConfig` allows the HTTP(s) ports to be specified, as well as the TLS config required if a server wants to accept HTTPS communication.\n\nTLS is backed by the [`crypto/tls`](https://pkg.go.dev/crypto/tls) standard library, which is the same used in `net/http`.\nThis provides sensible out-of-the-box defaults while allowing high levels of cusomisation if required.\nOnce a TLS config is provided to a `routeit` server, the server will **only** listen for HTTPS communication, unless plain HTTP communication is explicitly enabled.\n`routeit` also comes with built-in HTTPS upgrade mechanisms, which will instruct clients to upgrade their connections to HTTPS before they will be accepted, which is controlled through the `HttpConfig.UpgradeToHttps` and `HttpConfig.UpgradeInstructionMaxAge` properties.\nCheck out [`examples/https`](/examples/https/) for example setups showcasing each of the 3 configuration options that use HTTPS.\n\n#### Handlers\n\nEach resource on the server is served by a `Handler`.\nHandlers can respond to multiple HTTP methods, or just one.\n`routeit` handles method delegation and will respond with a `405: Method Not Allowed` response with the correct `Allow` header if the resource exists but does not respond to the request method.\n\n`Get`, `Post`, `Put`, `Patch` and `Delete` can all be used to construct a handler that responds to a single method, and accept a function of `func(*routeit.ResponseWriter, *routeit.Request) error` signature.\nIf an endpoint should respond to multiple HTTP methods, `MultiMethod` can be used, which accepts a struct to allow selection of the methods the handler should respond to.\nThe `error` returned from the handler function does not need to be a specific `routeit` error in every situation.\n\n#### Middleware\n\n`routeit` gives the developer the ability to write custom middleware to perform actions such as rate-limiting or authorisation handling.\nExamples can be found in [`examples/middleware`](/examples/middleware).\nLinking is performed through a `Chain` interface which is passed as an argument to the middleware function.\nMultiple middleware functions can be attached to a single server.\nThe order of attachment is important, as that is the order used when processing the middleware for each incoming request.\nMiddleware can choose to block a request (by not invoking `Chain.Proceed`) but be aware that the server will always attempt to send a response to the client for every incoming request.\nIt is more common to block a request by returning a specific error that can be conformed to a HTTP response.\n\n#### Routing\n\n`routeit` supports both static and dynamic routing, as well as allowing for enforcing specific prefixes and/or suffixes to be part of a dynamic match.\nRoutes are registered to the server using `Server.RegisterRoutes` and `Server.RegisterRoutesUnderNamespace` using a map from route to handler.\nThe server setup allows for namespaces - both global and local.\nThese reduce the complexity of the routing setup by avoiding redundant duplication.\nFor example, we might set the server's global namespace to `\"api\"`, meaning that all routes are registered with `/api` as the prefix, without needing to write it out for each route.\n\nDynamic matches are denoted using colon (`:`) syntax, with the characters after the colon used as the name for the dynamic component.\nA dynamic component can be forced to contain a required prefix and/or suffix using pipe (`|`) notation, as shown in the example below.\nOnce matched, the handler (or middleware) can use `Request.PathParam` to extract the name path segment.\nCritically, `Request.PathParam` will always return a non-empty string so long as the provided name does appear in the matched path.\nIf prefixes or suffixes are enforced, the path component will be the entire component - including a prefix and/or suffix.\n\n```go\n\"/:foo|pre/bar/:baz||suf/:qux/:corge|pre|suf\": routeit.Get(func(rw *routeit.ResponseWriter, req *routeit.Request) error {\n\t// The first path segment - this must be prefixed by \"pre\" to match\n\tfoo := req.PathParam(\"foo\")\n\t// The third path segment - this must end with \"suf\" to match\n\tbaz := req.PathParam(\"baz\")\n\t// The fourth path segment\n\tqux := req.PathParam(\"qux\")\n\t// The fifth path segment - this must start with \"pre\" and end with \"suf\",\n\t// with at least 1 character between them to match\n\tcorge := req.PathParam(\"corge\")\n\n\t// ...\n\treturn nil\n})\n```\n\nIn the above example, the router would invoke this handler if a URI such as `/prefix/bar/my-suf/anything/prefix-then-suf` was received at the edge.\nIn this case, `foo` would be `\"prefix\"`, `baz` would be `\"my-suf\"`, `qux` would be `\"anything\"` and `corge` would be `\"prefix-then-suf\"`.\n\nRoutes can also be rewritten before being routed or processed.\nFor example, we might want to rewrite `/` to `/static/index.html`, as `/` is what the browser will request and is a much cleaner URI than the actual URI needed for the resource.\nRewriting is covered in [`docs/rewrites.md`](/docs/rewrites.md) as well as by example in [`examples/static/rewrites`](/examples/static/rewrites/).\n\nFurther details about the structure of routing can be found in [`docs/trie.md`](/docs/trie.md), which also covers key information such as prioritisation of routing if multiple routes can match the incoming URI.\n\n#### Testing\n\nTesting is baked into the `routeit` library and can be used to increase confidence in the server.\nThere are two level of tests supported, both of which are currently experimental.\n\nThe `TestClient` allows for E2E-like tests and operates on a full server instance.\nTo reduce flakiness, TCP connections are not opened, however every other piece of the server is tested - parsing, URI rewriting, routing, middleware, handling, error management and static asset loading.\nMaking a request with a `TestClient` instance will return a `TestResponse` which allows assertions to be made on the status code, headers, and response body.\nUsage of `TestClient` is recommended for high-level validation that all moving parts of the server work as expected, without caring about the specific implementation details.\nEach example project in [`examples/`](/examples/) contains E2E tests using `TestClient`.\n\nFor finer isolation, middleware and handlers can both be tested independently, using `TestMiddleware` and `TestHandler` respectively.\nBoth functions accept a handler or middleware instance, and a `TestRequest`, which can be constructed using `NewTestRequest`.\nThey will also return an `error` and `TestResponse` for making assertions.\nIn the case of testing middleware, a boolean is also returned to indicate whether the middleware proceeded to the next piece of middleware or not.\n\nA key point to note is that, unlike with `TestClient`, the `error` is not run through any error mapping or handling.\nSo if the `error` is returned from `TestHandler` or `TestMiddleware`, it is the exact `error` that the corresponding handler or middleware returned.\nIn these cases, `TestResponse` will **not** be `nil`, but most meaningful assertions will not pass, unless the handler or middleware explicitly wrote a header or response body before returning an `error`.\n\n#### Errors\n\nApplication code can return errors of any type to the library in their handlers.\nA number of helpful error functions are exposed which allow the application code to conform their errors to HTTP responses.\nIf non-library errors are returned (or the application code panics with an error), we attempt to infer the reason or cause and map that to a HTTP error.\nThe integrator can provide a custom mapper using the `ServerConfig.ErrorMapper` which can provide additional custom logic in mapping from an `error` type to an error the server understands.\nIf the `ErrorMapper` cannot assign a more appropriate error type, it can return `nil` which will pass off the default inference which maps common errors to sensible defaults.\nFor example, if an `ErrNotExist` error is returned, we map that to a `404: Not Found` HTTP error.\nWe fallback to mapping to a `500: Internal Server Error` if we cannot establish a mapping.\n\nAdditionally, custom handling can be provided for specific HTTP status codes, if `routeit`'s default response is not sufficient.\nThis allows for additional logging, new headers, or transformation of the response body to something more meaningful than `routeit`'s default response, for example.\nCommon use cases include for `404: Not Found`, and `500: Internal Server Error`.\nThese can be registered using `Server.RegisterErrorHandlers`.\n\n[`examples/errors`](/examples/errors/) contains examples for how custom error handling and mapping can be performed using `routeit`.\n\n#### Logging\n\nEach valid incoming request is logged with the corresponding method, path (both edge and rewritten) and response status.\n4xx responses are logged using the `WARN` log level, 5xx responses are logged using `ERROR` and all other responses are logged using `INFO`.\nOnly requests that can be successfully parsed are logged.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsktylr%2Frouteit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsktylr%2Frouteit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsktylr%2Frouteit/lists"}