Files
smalltrip/README.md

12 KiB

Smalltrip

A small package to help on your routes, fully compatible to Go's standard library net/http.

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 and Lored's 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 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";
    • 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 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", 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 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?"!

To use the package, first import it by adding it to your go.mod file:

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:

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:

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:

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

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, and changes goes downstream to the GitHub mirror.

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).

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).

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 designed by Renee French1 and licensed under the Creative Commons 4.0 Attributions (CC BY 4.0) license.


  1. A working link for Renée French's website or socials was unavailable at the time of writing. ↩︎