grove

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 26, 2025 License: MIT Imports: 16 Imported by: 0

README

Grove

Grove is a lightweight Go web framework designed to minimize boilerplate and simplify the process of bootstrapping web applications. It provides a set of reasonable default implementations for common web application concerns, such as routing, middleware, dependency injection, logging, and authentication, while also allowing developers to manually implement or override these components as needed.

Vision

The vision behind Grove is to empower developers to build robust web applications quickly, without being locked into rigid conventions or heavyweight abstractions. Grove aims to strike a balance between productivity and flexibility by:

  • Reducing Boilerplate: Providing sensible defaults for common patterns, so you can get started quickly.
  • Encouraging Manual Control: Allowing you to override or extend any part of the framework with your own implementations.
  • Modular Design: Organizing core features into composable modules, making it easy to use only what you need.
  • Clear Interfaces: Defining clear interfaces for controllers, middleware, logging, and dependencies, so you can swap implementations as your application grows.
  • Secure by Default: Including built-in support for secure authentication and request handling.

Grove is ideal for developers who want a pragmatic starting point for Go web applications, with the freedom to customize and scale as requirements evolve.

Features

  • Minimal and extensible routing with scopes and controllers
  • Middleware chaining for request processing
  • Pluggable logging interface with a default logger
  • Simple dependency injection container
  • Secure JWT authentication with JWE encryption
  • Easy configuration and bootstrapping

Getting Started

Installation

To add Grove to your project, run:

go get github.com/StevenAlexanderJohnson/[email protected]

Optionally you can install the CLI to help with boilerplate generation:

go install github.com/StevenAlexanderJohnson/grove/cmd/[email protected]

Then, import Grove in your Go code:

import "github.com/StevenAlexanderJohnson/grove"
Basic Use

Once you've added Grove to your project, you can build APIs using the Builder Pattern methods provided, or just use the defaults.

import "github.com/StevenAlexanderJohnson/grove"

func main() {
	if err := grove.
		NewApp().
		WithRoute("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("Hello, Grove!"))
		})).Run(); err != nil {
		panic(err)
	}
}

Code Generation

If you installed the Grove CLI you can initialize and generate boilerplate. To generate a project go to the folder you want to initialize it within and run:

grove init <project_name>

The project_name will be used as the module name in go.mod. It will generate a new folder inside of the directory you ran the command.

Inside is a skeleton project that you can immediately start using. Once you're inside of that initialized project you can also use the CLI to generate boilerplate.

The CLI has two commands for generating boilerplate. The first is grove create resource. This will generate the model, the repository, and service for interacting with the resource.

It is important to note in order to use the CLI tool you have to be within a project initialized by Grove, or one that uses a similar file structure. Because Grove CLI looks for the mod file so it knows what the module is called and can then generate import paths.

Once in the root folder of the initialized project you can run:

grove create resource <ResourceName> field1:<go_type> field2:<go_type> ...

It will then generate all the above mentioned files in their respective folders.

The second command that grove has for generating boilerplate is grove create controller. This command will first run grove create resource first to generate all of the required files then it will generate a controller with CRUD methods defined.

You can then use this generated controller with scope.WithController() or app.WithController().

The command is used exactly like resource but you replace resource with controller:

grove create controller <ResourceName> field1:<go_type> field2:<go_type> ...

Documentation

Grove is designed to be simple, explicit, and unopinionated. You are encouraged to read the source code and the examples provided in the examples/ directory for real-world usage patterns.

Key Concepts
  • Bootstrapping: Grove helps you set up your project structure and common files, but does not hide or abstract your application logic.
  • Routing & Scopes: Use WithRoute and WithScope to register handlers and organize your routes. Scopes can have their own middleware chains.
  • Middleware: Middleware is just a function type. You can compose, chain, and write your own. Grove provides helpers, but you are free to implement your own logic.
  • Dependency Injection: Grove includes a simple DI container for wiring dependencies, but you can use your own patterns if you prefer.
  • Logging: Grove provides a pluggable logger interface. You can use the default logger or bring your own.
  • Authentication: Secure JWE authentication is available, but you can opt out or replace it.
Examples

See the examples/ directory for:

  • Basic app setup
  • Scoped routes
  • Dependency injection
  • Authentication
Philosophy

Grove does not enforce strict conventions or hide details. You are always in control of your application’s structure and logic. The framework’s goal is to reduce boilerplate and help you get started, not to lock you in.

API Reference

Full API documentation is coming soon. For now, refer to the GoDoc comments in the source code and the examples for usage guidance.

License

This project is licensed under the MIT License.
See the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AuthTokenKey = authTokenKeyType{}
View Source
var RequestIDKey = requestIDKeyType{}

Functions

func DependencyGet

func DependencyGet[T any](deps *Dependencies, key DependencyKey) (T, bool)

func DependencyMustGet

func DependencyMustGet[T any](deps *Dependencies, key DependencyKey) T

func ParseJsonBodyFromRequest added in v0.0.3

func ParseJsonBodyFromRequest[T any](request *http.Request) (T, error)

func WriteErrorToResponse added in v0.0.3

func WriteErrorToResponse(response http.ResponseWriter, statusCode int, message string)

func WriteJsonBodyToResponse added in v0.0.3

func WriteJsonBodyToResponse[T any](response http.ResponseWriter, body T) error

Types

type App

type App struct {
	// contains filtered or unexported fields
}

func NewApp

func NewApp(appName string) *App

func (*App) Run

func (app *App) Run() error

Run starts to listen the HTTP server on the specified port. It applies all registered middleware to the handler. It returns an error if the server fails to start.

func (*App) ServeHTTP

func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface. It allows the App to be used as a handler in an HTTP server. It delegates the request handling to the ServeMux.

func (*App) WithController

func (app *App) WithController(controller IController) *App

WithController registers a controller with the application. It calls the RegisterRoutes method of the controller to set up its routes. If the controller is nil, it logs an error and does not register it. Controller routes that are registered directly to the App will use all the middleware registered to the app.

func (*App) WithControllerFactory

func (app *App) WithControllerFactory(factory ControllerFactory) *App

WithControllerFactory registers a controller using a factory function. The factory is called with the application's dependencies to create the controller. If the factory returns nil, it logs an error and does not register the controller. This method allows for dynamic controller creation based on the application's dependencies. This method should be used if you are not manually bootstrapping the controller.

func (*App) WithDependencies

func (app *App) WithDependencies(deps *Dependencies) *App

func (*App) WithLogger

func (app *App) WithLogger(logger ILogger) *App

WithLogger sets the logger for the application. If the logger is nil, it logs a warning and uses the default logger. This method allows the application to use a custom logger for logging messages.

func (*App) WithMiddleware

func (app *App) WithMiddleware(mw Middleware) *App

WithMiddleware registers a middleware function to the application. If the middleware is nil, it logs a warning and does not register it. This method is used to add middleware that can modify the request/response cycle. The middleware will be applied to all routes handled by the application.

func (*App) WithMux added in v0.0.3

func (app *App) WithMux(mux *http.ServeMux) *App

WithMux sets the ServeMux for the application. If the provided mux is nil, it logs a warning and uses the existing mux. This method allows the application to use a custom ServeMux for routing. A default mux which is a simple http.ServeMux is created when the app is initialized.

func (*App) WithPort

func (app *App) WithPort(port string) *App

WithPort sets the port for the application. If the port is empty, it logs a warning and defaults to "8080". This method allows the application to listen on a specific port for incoming HTTP requests.

func (*App) WithRoute

func (app *App) WithRoute(path string, handler http.Handler) *App

WithRoute registers a handler for a specific path. It ensures the path starts and ends with a slash. If the handler is nil, it logs a warning and does not register the route. This method is used to add routes outside of controllers. The it will use all the middleware registered to the app.

func (*App) WithScope

func (app *App) WithScope(path string, scope *Scope) *App

WithScope registers a scope at the specified path. It ensures the path starts and ends with a slash. If the scope is nil, it logs a warning and does not register the scope. This method is used to add scopes that can be used for grouping routes or resources.

type Authenticator

type Authenticator[T jwt.Claims] struct {
	*AuthenticatorConfig
}

func NewAuthenticator

func NewAuthenticator[T jwt.Claims](config *AuthenticatorConfig) *Authenticator[T]

func (*Authenticator[T]) GenerateToken

func (a *Authenticator[T]) GenerateToken(claims T) (string, error)

GenerateToken creates a new JWT token with the provided claims, signs it, and encrypts it. The token is signed using the configured key and encrypted using JWE with RSA-OAEP and A128GCM. The generated token is suitable for use in authentication and authorization processes.

func (*Authenticator[T]) ParseToken

func (a *Authenticator[T]) ParseToken(token string, claims T) (*jwt.Token, error)

ParseToken decrypts the token and parses it into the provided claims. It does not validate the claims, allowing for custom validation logic to be applied later.

func (*Authenticator[T]) VerifyToken

func (a *Authenticator[T]) VerifyToken(token string, claims T) (T, error)

VerifyToken decrypts the token, parses it, and validates the claims. It checks the audience and issuer against the configured values. If the token is valid, it returns the claims; otherwise, it returns an error. This method is used to ensure that the token is valid and can be trusted for authentication.

type AuthenticatorConfig

type AuthenticatorConfig struct {
	JWEPrivateKey *rsa.PrivateKey
	Lifetime      time.Duration
	Issuer        string
	Audience      []string
	Key           string
}

func LoadAuthenticatorConfigFromEnv

func LoadAuthenticatorConfigFromEnv() (*AuthenticatorConfig, error)

func NewAuthenticatorConfig

func NewAuthenticatorConfig(jwePrivateKey *rsa.PrivateKey, lifetime time.Duration, issuer string, audience []string, key string) *AuthenticatorConfig

func (*AuthenticatorConfig) Validate

func (config *AuthenticatorConfig) Validate() error

type ControllerFactory

type ControllerFactory func(*Dependencies) IController

type DefaultLogger

type DefaultLogger struct {
	// contains filtered or unexported fields
}

func (*DefaultLogger) Debug

func (l *DefaultLogger) Debug(v ...interface{})

func (*DefaultLogger) Debugf

func (l *DefaultLogger) Debugf(format string, v ...interface{})

func (*DefaultLogger) Error

func (l *DefaultLogger) Error(v ...interface{})

func (*DefaultLogger) Errorf

func (l *DefaultLogger) Errorf(format string, v ...interface{})

func (*DefaultLogger) Fatal

func (l *DefaultLogger) Fatal(v ...interface{})

func (*DefaultLogger) Fatalf

func (l *DefaultLogger) Fatalf(format string, v ...interface{})

func (*DefaultLogger) Info

func (l *DefaultLogger) Info(v ...interface{})

func (*DefaultLogger) Infof

func (l *DefaultLogger) Infof(format string, v ...interface{})

func (*DefaultLogger) Log

func (l *DefaultLogger) Log(v ...interface{})

func (*DefaultLogger) Logf

func (l *DefaultLogger) Logf(format string, v ...interface{})

func (*DefaultLogger) Trace

func (l *DefaultLogger) Trace(v ...interface{})

func (*DefaultLogger) Tracef

func (l *DefaultLogger) Tracef(format string, v ...interface{})

func (*DefaultLogger) Warning

func (l *DefaultLogger) Warning(v ...interface{})

func (*DefaultLogger) Warningf

func (l *DefaultLogger) Warningf(format string, v ...interface{})

type Dependencies

type Dependencies struct {
	// contains filtered or unexported fields
}

func NewDependencies

func NewDependencies() *Dependencies

func (*Dependencies) Set

func (d *Dependencies) Set(key DependencyKey, value interface{})

type DependencyKey

type DependencyKey string

type IController

type IController interface {
	// RegisterRoutes registers the controller's routes with the provided ServeMux.
	// It is called by the Grove application to set up the HTTP routes for the controller.
	RegisterRoutes(mux *http.ServeMux)
}

IController defines the interface for controllers in the Grove application. Controllers implement this interface to register their routes with the application's HTTP multiplexer. The RegisterRoutes method is called with the application's ServeMux and Dependencies to set up the routes and any necessary dependencies for the controller.

type ILogger

type ILogger interface {
	Log(v ...interface{})
	Logf(format string, v ...interface{})
	Info(v ...interface{})
	Infof(format string, v ...interface{})
	Error(v ...interface{})
	Errorf(format string, v ...interface{})
	Debug(v ...interface{})
	Debugf(format string, v ...interface{})
	Warning(v ...interface{})
	Warningf(format string, v ...interface{})
	Trace(v ...interface{})
	Tracef(format string, v ...interface{})
	Fatal(v ...interface{})
	Fatalf(format string, v ...interface{})
}

func NewDefaultLogger

func NewDefaultLogger(appName string) ILogger

type Middleware

type Middleware func(next http.Handler) http.Handler

Middleware defines a function type that takes an http.Handler and returns an http.Handler. This allows for chaining middleware functions in the Grove application.

func DefaultAuthMiddleware

func DefaultAuthMiddleware[T jwt.Claims](authenticator *Authenticator[T], logger ILogger, claimsFactory func() T) Middleware

DefaultAuthMiddleware is a middleware that provides default authentication logic. It checks for a valid token in the request header and denies access if the token is missing

func DefaultRequestLoggerMiddleware

func DefaultRequestLoggerMiddleware(logger ILogger) Middleware

DefaultRequestLoggerMiddleware logs the request details including a unique request ID. It uses the Logger instance to log the start and completion of each request. The request ID is generated using the uuid package and is stored in the request context. This middleware can be used to trace requests through the application. It logs the request method, URL, and duration of the request. This is useful for debugging and monitoring purposes.

type Scope

type Scope struct {
	// contains filtered or unexported fields
}

func NewScope

func NewScope() *Scope

func (*Scope) ServeHTTP

func (s *Scope) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Scope) WithController added in v0.0.2

func (s *Scope) WithController(controller IController) *Scope

func (*Scope) WithMiddleware added in v0.0.2

func (s *Scope) WithMiddleware(mw Middleware) *Scope

func (*Scope) WithRoute

func (s *Scope) WithRoute(pattern string, handler http.Handler) *Scope

Directories

Path Synopsis
cmd
grove command
examples
authenticator command
parsing_body command
scoped_routes command
skeleton command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL