Files
smalltrip/README.md

301 lines
12 KiB
Markdown

# ![Smalltrip](./docs/assets/logo.svg)
A small package to help on your routes, fully compatible to Go's standard library
[`net/http`](https://pkg.go.dev/net/http).
```go
package main
import (
"encoding/json"
"net/http"
"os"
"slog"
"time"
"code.capytal.cc/loreddev/smalltrip"
"code.capytal.cc/loreddev/smalltrip/middleware"
"code.capytal.cc/loreddev/smalltrip/multiplexer"
"code.capytal.cc/loreddev/smalltrip/problem"
)
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).
> 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)
> 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.
## 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 (
code.capytal.cc/loreddev/smalltrip v0.1.0
)
```
And/or by using to `go get` command:
```sh
go get -u code.capytal.cc/loreddev/smalltrip
```
> [!NOTE]
> The URL of the package `code.capytal.cc/loredev/smalltrip` **is subject to change in the coming
> 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"
"code.capytal.cc/loreddev/smalltrip"
"code.capytal.cc/loreddev/smalltrip/middleware"
)
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"
"code.capytal.cc/loreddev/smalltrip"
"code.capytal.cc/loreddev/smalltrip/multiplexer"
)
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.
## 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
provided as is with it's [known bugs and missing features](https://code.capytal.cc/loreddev/smalltrip/issues)
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.
The canonical source-code is [located at our forge](https://code.capytal.cc/loreddev/smalltrip),
and changes goes downstream to [the GitHub mirror](https://github.com/loreddev/smalltrip).
## Licenses
Copyright © 2025-present Gustavo "Guz" L. de Mello & The Lored.dev Contributors
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/).
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.
[^1]: A working link for Renée French's website or socials was unavailable at the time of writing.