2025-08-26 15:43:16 -03:00
|
|
|
# 
|
2025-02-27 16:21:01 -03:00
|
|
|
|
2025-08-26 16:03:13 -03:00
|
|
|
A small package to help on your routes, fully compatible to Go's standard library
|
|
|
|
|
[`net/http`](https://pkg.go.dev/net/http).
|
2025-02-27 16:21:01 -03:00
|
|
|
|
2025-08-26 16:03:13 -03:00
|
|
|
```go
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"slog"
|
|
|
|
|
"time"
|
|
|
|
|
|
2025-10-13 15:16:57 -03:00
|
|
|
"code.capytal.cc/loreddev/smalltrip"
|
|
|
|
|
"code.capytal.cc/loreddev/smalltrip/middleware"
|
|
|
|
|
"code.capytal.cc/loreddev/smalltrip/multiplexer"
|
|
|
|
|
"code.capytal.cc/loreddev/smalltrip/problem"
|
2025-08-26 16:03:13 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
mux := multiplexer.New()
|
|
|
|
|
|
|
|
|
|
mux = problem.Multiplexer(mux)
|
|
|
|
|
|
|
|
|
|
mux = multiplexer.WithPatternRules(mux,
|
|
|
|
|
multiplexer.EnsureMethod(),
|
|
|
|
|
multiplexer.EnsureStrictEnd(),
|
|
|
|
|
multiplexer.EnsureTrailingSlash(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
|
|
|
|
|
|
|
|
|
router := smalltrip.NewRouter(smalltrip.WithMultiplexer(mux))
|
|
|
|
|
|
|
|
|
|
router.Use(middleware.Logger(log.WithGroup("requests")))
|
|
|
|
|
router.Use(middleware.Cache(middleware.CacheMaxAge(time.Second * 10)))
|
|
|
|
|
|
|
|
|
|
router.HandleFunc("GET /hello/{$}", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
data := struct {
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
}{
|
|
|
|
|
Message: "Hello, World",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, err := json.Marshal(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
problem.NewInternalServerError(err).ServeHTTP(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
_, err = w.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
problem.NewInternalServerError(err).ServeHTTP(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
srv := http.Server{
|
|
|
|
|
Addr: "localhost:8080",
|
|
|
|
|
Handler: router,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start server...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> [!WARNING]
|
|
|
|
|
> **Here be dragons.**
|
|
|
|
|
>
|
|
|
|
|
> Smalltrip is still in very early development and is still a Work In Progress (WIP).
|
2025-10-13 15:16:57 -03:00
|
|
|
> It is being dog-feed on [Capytal's](https://code.capytal.cc/capytalcode) and
|
|
|
|
|
> [Lored's](https://code.capytal.cc/loreddev) projects, and most (if not all)
|
2025-08-26 16:03:13 -03:00
|
|
|
> of the development is focused on creating features for said projects as they are needed.
|
|
|
|
|
>
|
|
|
|
|
> Expect bugs, incomplete documentation, and breaking changes if you are going to
|
|
|
|
|
> use this package.
|
|
|
|
|
|
|
|
|
|
## About
|
|
|
|
|
|
|
|
|
|
Smalltrip is, as the name implies, a small Go (Golang) package focused on helping your HTTP routing,
|
|
|
|
|
request validation, responses building, and error handling. The way it accomplishes this
|
|
|
|
|
objective is by extending the standard library's `net/http` package and providing utility
|
|
|
|
|
functions, middleware and APIs to better structure how routes are made and managed.
|
|
|
|
|
|
|
|
|
|
### Features
|
|
|
|
|
|
|
|
|
|
- Fully compatible and built on top the standard library's `net/http`;
|
|
|
|
|
- Utility APIs to improve code structure and reduce repetition:
|
|
|
|
|
- Rules and options for [`http.ServerMux`](https://pkg.go.dev/net/http#ServeMux) patterns;
|
|
|
|
|
- Router API with support for `func(http.Handler) http.Handler` middlewares;
|
|
|
|
|
- Out-of-the-box middlewares for logging, panic recovering, caching, etc.;
|
|
|
|
|
- Error handling structs, following [RFC 9457 "Problems Details for HTTP APIs"](https://datatracker.ietf.org/doc/rfc9457);
|
|
|
|
|
- Ready-for-use 400s and 500s error codes, with parameters for required response headers;
|
|
|
|
|
- Defined interfaces for common patterns and standard library structs.
|
|
|
|
|
- Small code footprint and vendor lock-in;
|
|
|
|
|
- Customizable behaviour of APIs via functional options;
|
|
|
|
|
- Extendable via interfaces and dependency-injection;
|
|
|
|
|
- ~~**W.I.P.** Fully documented API.~~
|
|
|
|
|
|
|
|
|
|
### Why Another Package?
|
|
|
|
|
|
|
|
|
|
Most web frameworks and libraries in the Go ecosystem focus on creating a full overhaul on
|
|
|
|
|
how requests are handled, at least compared to what the language provides out-of-the-box. This
|
|
|
|
|
overhaul is normally done by completely ignoring the standard library's conventions and APIs,
|
|
|
|
|
using multiple dependencies, and/or abstracting all request handling behind some sort of context
|
|
|
|
|
structure.
|
|
|
|
|
|
|
|
|
|
We prefer to keep our projects as most dependency-free as possible and use the most of the language
|
|
|
|
|
conventions as possible, minimizing the amount of vendor lock-in that they have. That's why Go
|
|
|
|
|
has become our language of choice in the first place, since it's standard library already gives
|
|
|
|
|
so much, that we don't need to rely on other packages to do solve most problems.
|
|
|
|
|
|
|
|
|
|
However, the `net/http` package is not perfect, since it is in the standard library, it can't
|
|
|
|
|
give a lot of "convenience APIs" which would cause disagreement on the community as a whole. Most
|
|
|
|
|
of the APIs provided are a small abstraction on top of the HTTP protocol, which means subjects
|
|
|
|
|
such as error handling, middlewares, caching, logging, etc. are meant to the developer to choose how
|
|
|
|
|
to handle. In small projects this is fine and manually setting up HTTP error status codes in each request,
|
|
|
|
|
setting up common headers, and checking up [`http.ServerMux`](https://pkg.go.dev/net/http#ServeMux) patterns
|
|
|
|
|
isn't too big of a deal. But as projects grows and more developers contribute, the unstructured
|
|
|
|
|
nature of `net/http` starts to be a problem and conventions, abstractions and utility functions
|
|
|
|
|
will need to be created to make sure every HTTP handler has a similar structure.
|
|
|
|
|
|
|
|
|
|
Also, since the standard package is just an abstraction on top of the HTTP specification, other
|
|
|
|
|
useful W3C recommendations, proposed standards, and common web development conventions aren't backed-in.
|
|
|
|
|
Which means things such as, for example, [RFC 9457 "Problem Details for HTTP APIs"](https://datatracker.ietf.org/doc/rfc9457),
|
|
|
|
|
needs to be hard-coded in each request or have an abstraction implemented from scratch.
|
|
|
|
|
|
|
|
|
|
Smalltrip is Lored's (and Capytal's) decisions of said conventions, abstractions, utility functions and
|
|
|
|
|
response to the standard library's shortcomings. **It is then, by nature, very opinionated.**
|
|
|
|
|
|
|
|
|
|
### Should I Use This?
|
|
|
|
|
|
|
|
|
|
As mentioned above and as the time of writing, **this package is still in a unstable state and we
|
|
|
|
|
not recommend using it on any production code**. You may play with the package or use in non-critical
|
|
|
|
|
projects and code, mostly so you don't need to write middleware support, error handling, etc. as
|
|
|
|
|
a heads-up. Also you could use it as inspiration for your own conventions on top of the `net/http`
|
|
|
|
|
package.
|
|
|
|
|
|
|
|
|
|
We use on our own projects, because we can easily just fix or change the API without having to worry
|
|
|
|
|
about breaking changes, updating, or how it will affect other users. This is mostly a source-available
|
|
|
|
|
project with a permissive license, not a open-source project with community support and contributing
|
|
|
|
|
at it's core. **Breaking changes will be introduced if it means a better experience on for our developers
|
|
|
|
|
and needs**. See the ["Contributing" section](#contributing) for more details.
|
|
|
|
|
|
|
|
|
|
> [!TIP]
|
|
|
|
|
> If you still want to use it, we recommend using it by copying the parts of source code you want
|
|
|
|
|
> to use directly to your project, or vendor it under your organization by forking this repository and
|
|
|
|
|
> making any changes you deemed necessary. This way you have a more stable experience and don't need
|
|
|
|
|
> to worry about managing unstable versions, breaking changes, and incomplete APIs.
|
2025-02-27 16:21:01 -03:00
|
|
|
|
2025-08-26 16:03:37 -03:00
|
|
|
## Usage
|
|
|
|
|
|
|
|
|
|
> **RTFM:** Before going to this section, please make sure to not skip the section ["Should I Use This?"](#should-i-use-this)!
|
|
|
|
|
|
|
|
|
|
To use the package, first import it by adding it to your `go.mod` file:
|
|
|
|
|
|
|
|
|
|
```gomod
|
|
|
|
|
module example.com/package
|
|
|
|
|
|
|
|
|
|
go 1.24.5
|
|
|
|
|
|
|
|
|
|
require (
|
2025-10-13 15:16:57 -03:00
|
|
|
code.capytal.cc/loreddev/smalltrip v0.1.0
|
2025-08-26 16:03:37 -03:00
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
And/or by using to `go get` command:
|
|
|
|
|
|
|
|
|
|
```sh
|
2025-10-13 15:16:57 -03:00
|
|
|
go get -u code.capytal.cc/loreddev/smalltrip
|
2025-08-26 16:03:37 -03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> [!NOTE]
|
2025-10-13 15:16:57 -03:00
|
|
|
> The URL of the package `code.capytal.cc/loredev/smalltrip` **is subject to change in the coming
|
2025-08-26 16:03:37 -03:00
|
|
|
> future**, we are working on providing vanity URLs for our packages under a different schema.
|
|
|
|
|
|
|
|
|
|
Usage can be as simple as just using the provided router interface provided via the `NewRouter` method
|
|
|
|
|
at the top level of the package with some provided middlewares by the `middleware` directory:
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"slog"
|
|
|
|
|
"time"
|
|
|
|
|
|
2025-10-13 15:16:57 -03:00
|
|
|
"code.capytal.cc/loreddev/smalltrip"
|
|
|
|
|
"code.capytal.cc/loreddev/smalltrip/middleware"
|
2025-08-26 16:03:37 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
router := smalltrip.NewRouter()
|
|
|
|
|
|
|
|
|
|
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}))
|
|
|
|
|
|
|
|
|
|
router.Use(middleware.Logger(log.WithGroup("requests")))
|
|
|
|
|
router.Use(middleware.Cache(middleware.CacheMaxAge(time.Second * 10)))
|
|
|
|
|
|
|
|
|
|
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Write("Hello, world")
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
However, the constructor method accepts some functional options to extend it's capability and/or change
|
|
|
|
|
it's underlying behaviour and dependencies. For example, you can create a `multiplexer.Multiplexer` interface,
|
|
|
|
|
which has the same API as the standard library's `http.ServerMux`, with some pattern rules like "every pattern
|
|
|
|
|
needs to have a explicit defined method" and inject it into the `Router` interface implementation:
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"slog"
|
|
|
|
|
"time"
|
|
|
|
|
|
2025-10-13 15:16:57 -03:00
|
|
|
"code.capytal.cc/loreddev/smalltrip"
|
|
|
|
|
"code.capytal.cc/loreddev/smalltrip/multiplexer"
|
2025-08-26 16:03:37 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
mux := multiplexer.New() // Same as `http.NewServerMux`, `just with multiplexer.Multiplexer` as it return type.
|
|
|
|
|
mux = multiplexer.WithPatternRules(mux, multiplexer.EnsureMethod())
|
|
|
|
|
|
|
|
|
|
router := smalltrip.NewRouter(smalltrip.WithMultiplexer(mux))
|
|
|
|
|
|
|
|
|
|
router.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Write("Hello, world")
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
router.HandleFunc("POST /new-data", func(w http.ResponseWriter, r *http.Request) {})
|
|
|
|
|
|
|
|
|
|
router.HandleFunc("/no-method") // Panics, no method defined.
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is recommended that you to explore the API documentation, and even the source-code (it is very
|
|
|
|
|
small, and will not bite you) to know all the available functions, methods and interfaces of the package
|
|
|
|
|
that you can use on your HTTP handlers and routing. We hope to in the future provide, when the project
|
|
|
|
|
is more mature and stable, a more complete documentation about how we use and recommend using the
|
|
|
|
|
library and it's API, focusing more on it as a whole, other than the specific implementations and code.
|
|
|
|
|
|
|
|
|
|
### Versions & Support
|
|
|
|
|
|
|
|
|
|
At the time of writing, there isn't a stable `v1.0.0` version yet. **All breaking changes will be handled
|
|
|
|
|
as minor version bumps**, so if you plan on using this package, be very attentive on the changelogs
|
|
|
|
|
and Git history to know if there was one.
|
|
|
|
|
|
|
|
|
|
We normally try to work on the latest Go version, so support is limited to the two last minor language
|
|
|
|
|
versions.
|
|
|
|
|
|
2025-08-26 16:03:49 -03:00
|
|
|
## Contributing
|
|
|
|
|
|
|
|
|
|
Currently we are not focusing on Open Source contributions and are providing the project as
|
|
|
|
|
"Source Available, with a permissive license". The source-code is provided as is and licensed
|
|
|
|
|
under the Apache License (Version 2.0) as mentioned below. We hope to in the future provide
|
|
|
|
|
better support for open source contributions and improve the project with the community, however,
|
|
|
|
|
due to time and human power limitations on our end, we prefer to not accept bug and pull requests
|
|
|
|
|
without being able to properly provide maintenance and moderation.
|
|
|
|
|
|
|
|
|
|
Community reported issues and features requests via GitHub and alike are disabled, the package is
|
2025-10-13 15:16:57 -03:00
|
|
|
provided as is with it's [known bugs and missing features](https://code.capytal.cc/loreddev/smalltrip/issues)
|
2025-08-26 16:03:49 -03:00
|
|
|
|
|
|
|
|
You can still create pull requests, but it may just be rejected without further acknowledgment if
|
|
|
|
|
we judge it as not directly aligned with our goals to the project.
|
|
|
|
|
|
2025-10-13 15:16:57 -03:00
|
|
|
The canonical source-code is [located at our forge](https://code.capytal.cc/loreddev/smalltrip),
|
2025-08-26 16:03:49 -03:00
|
|
|
and changes goes downstream to [the GitHub mirror](https://github.com/loreddev/smalltrip).
|
|
|
|
|
|
2025-08-26 15:43:16 -03:00
|
|
|
## Licenses
|
|
|
|
|
|
|
|
|
|
Copyright © 2025-present Gustavo "Guz" L. de Mello & The Lored.dev Contributors
|
|
|
|
|
|
2025-08-26 16:03:13 -03:00
|
|
|
Source-code and code samples are licensed and distributed under the [Apache License (Version 2.0)](./LICENSE).
|
|
|
|
|
|
|
|
|
|
Documentation of the package APIs and usage, may it be either in code comments, this `README.md` file,
|
|
|
|
|
under the `docs/` directory, or in HTML form, is licensed under [Creative Commons 4.0 Attribution Share-Alike
|
|
|
|
|
(CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/).
|
2025-08-26 15:43:16 -03:00
|
|
|
|
2025-08-26 16:03:13 -03:00
|
|
|
The "Smalltrip.go" logo is licensed under the Creative Commons 4.0 Attribution Share-Alike
|
|
|
|
|
(CC BY-SA 4.0) license also, it features the [Go Gopher](https://go.dev/blog/gopher) designed by
|
|
|
|
|
[Renee French](https://wikipedia.org/wiki/Ren%C3%A9e_French)[^1] and licensed under the
|
|
|
|
|
[Creative Commons 4.0 Attributions (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/) license.
|
2025-08-26 15:43:16 -03:00
|
|
|
|
|
|
|
|
[^1]: A working link for Renée French's website or socials was unavailable at the time of writing.
|
2025-02-27 16:21:01 -03:00
|
|
|
|