Golang type-safe dependency injection

Overview

nject, npoint, nserve, & nvelope - dependency injection

GoDoc unit tests report card codecov FOSSA Status

Install:

go get github.com/muir/nject

This is a quartet of packages that together make up a most of a golang API server framework:

nject: type safe dependency injection w/o requiring type assertions.

npoint: dependency injection wrappers for binding http endpoint handlers

nvelope: injection chains for building endpoints

nserve: injection chains for for starting and stopping servers

Basic idea

Dependencies are injected via a call chain: list functions to be called that take and return various parameters. The functions will be called in order using the return values from earlier functions as parameters for later functions.

Parameters are identified by their types. To have two different int parameters, define custom types.

Type safety is checked before any functions are called.

Functions whose outputs are not used are not called. Functions may "wrap" the rest of the list so that they can choose to invoke the remaing list zero or more times.

Chains may be pre-compiled into closures so that they have very little runtime penealty.

nject example

func example() {
	// Sequences can be reused.
	providerChain := Sequence("example sequence",
		// Constants can be injected.
		"a literal string value",
		// This function will be run if something downstream needs an int
		func(s string) int {
			return len(s)
		})
	Run("example",
		providerChain,
		// The last function in the list is always run.  This one needs
		// and int and a string.  The string can come from the constant
		// and the int from the function in the provider chain.
		func(i int, s string) {
			fmt.Println(i, len(s))
		})
}

npoint example

CreateEndpoint is the simplest way to start using the npoint framework. It generates an http.HandlerFunc from a list of handlers. The handlers will be called in order. In the example below, first WriteErrorResponse() will be called. It has an inner() func that it uses to invoke the rest of the chain. When WriteErrorResponse() calls its inner() function, the db injector returned by InjectDB is called. If that does not return error, then the inline function below to handle the endpint is called.

mux := http.NewServeMux()
mux.HandleFunc("/my/endpoint", npoint.CreateEndpoint(
	WriteErrorResponse,
	InjectDB("postgres", "postgres://..."),
	func(r *http.Request, db *sql.DB, w http.ResponseWriter) error {
		// Write response to w or return error...
		return nil
	}))

WriteErrorResponse invokes the remainder of the handler chain by calling inner().

func WriteErrorResponse(inner func() nject.TerminalError, w http.ResponseWriter) {
	err := inner()
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
	}
}

InjectDB returns a handler function that opens a database connection. If the open fails, executation of the handler chain is terminated. InjectDB returns an injector so that it can be called with arguments -- injectors are functions, not invocations and so we need to return a function. InjectDB also closes the database connection.

func InjectDB(driver, uri string) func(func(*sql.DB) error) error {
	return func(inner func(*sql.DB) error) (finalError error) {
		db, err := sql.Open(driver, uri)
		if err != nil {
			return err
		}
		defer func() {
			err := db.Close()
			if err != nil && finalError == nil {
				finalError = err
			}
		}()
		return inner(db)
	}
}

nvelope example

Nvelope provides pre-defined handlers for basic endpoint tasks. When used in combination with npoint, all that's left is the business logic.

type ExampleRequestBundle struct {
	Request     PostBodyModel `nvelope:"model"`
	With        string        `nvelope:"path,name=with"`
	Parameters  int64         `nvelope:"path,name=parameters"`
	Friends     []int         `nvelope:"query,name=friends"`
	ContentType string        `nvelope:"header,name=Content-Type"`
}

func Service(router *mux.Router) {
	service := npoint.RegisterServiceWithMux("example", router)
	service.RegisterEndpoint("/some/path",
		nvelope.LoggerFromStd(log.Default()),
		nvelope.InjectWriter,
		nvelope.EncodeJSON,
		nvelope.CatchPanic,
		nvelope.Nil204,
		nvelope.ReadBody,
		nvelope.DecodeJSON,
		func (req ExampleRequestBundle) (nvelope.Response, error) {
			....
		},
	).Methods("POST")
}

nserve example

On thing you might want to do with nserve is to use a Hook to trigger per-library database migrations using libschema.

First create the hook:

package myhooks

import "github.com/nject/nserve"

var MigrateMyDB = nserve.NewHook("migrate, nserve.Ascending)

In each library, have a create function:

package users

import(
	"github.com/muir/libschema/lspostgres"
	"github.com/muir/nject/nserve"
)

func NewUsersStore(app *nserve.App) *Store {
	...
	app.On(myhooks.MigrateMyDB, func(database *libschema.Database) {
		database.Migrations("MyLibrary",
			lspostgres.Script("create users", `
				CREATE TABLE users (
					id	bigint PRIMARY KEY,
					name	text
				)
			`),
		)
	})
	...
	return &Store{}
}

Then as part of server startup, invoke the migration hook:

package main

import(
	"github.com/muir/libschema"
	"github.com/muir/libschema/lspostgres"
	"github.com/muir/nject/nject"
)

func main() {
	app, err := nserve.CreateApp("myApp", users.NewUserStore, ...)
	schema := libschema.NewSchema(ctx, libschema.Options{})
	sqlDB, err := sql.Open("postgres", "....")
	database, err := lspostgres.New(logger, "main-db", schema, sqlDB)
	myhooks.MigrateMyDB.Using(database)
	err = app.Do(myhooks.MigrateMyDB)

Development status

This repo represents continued development of Blue Owl's nject base. Blue Owl's code has been in production use for years and has been unchanged for years. The core of nject is mostly unchanged. Nvelope and nserve are new.

Go version

Due to the use of strconv.ParseComplex in nvelope, the minimum supported version of Go is 1.15

Issues
  • build(deps): bump github.com/muir/reflectutils from 0.0.1 to 0.0.2

    build(deps): bump github.com/muir/reflectutils from 0.0.1 to 0.0.2

    Bumps github.com/muir/reflectutils from 0.0.1 to 0.0.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 2
  • Add license scan report and status

    Add license scan report and status

    Your FOSSA integration was successful! Attached in this PR is a badge and license report to track scan status in your README.

    Below are docs for integrating FOSSA license checks into your CI:

    opened by fossabot 1
  • build(deps): bump github.com/muir/reflectutils from 0.3.1 to 0.4.0

    build(deps): bump github.com/muir/reflectutils from 0.3.1 to 0.4.0

    Bumps github.com/muir/reflectutils from 0.3.1 to 0.4.0.

    Release notes

    Sourced from github.com/muir/reflectutils's releases.

    time.Duration & flag.Value

    Add support for registering unpackers and use that to support time.Duration. Add support for unpacking into things that support the flag.Value interface

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • build(deps): bump github.com/muir/reflectutils from 0.2.2 to 0.3.0

    build(deps): bump github.com/muir/reflectutils from 0.2.2 to 0.3.0

    Bumps github.com/muir/reflectutils from 0.2.2 to 0.3.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • build(deps): bump github.com/muir/reflectutils from 0.2.1 to 0.2.2

    build(deps): bump github.com/muir/reflectutils from 0.2.1 to 0.2.2

    Bumps github.com/muir/reflectutils from 0.2.1 to 0.2.2.

    Release notes

    Sourced from github.com/muir/reflectutils's releases.

    expose TagSet.Tag

    also: split on "" means don't split

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • build(deps): bump github.com/muir/reflectutils from 0.1.0 to 0.2.1

    build(deps): bump github.com/muir/reflectutils from 0.1.0 to 0.2.1

    Bumps github.com/muir/reflectutils from 0.1.0 to 0.2.1.

    Release notes

    Sourced from github.com/muir/reflectutils's releases.

    tag.Fill can now split on tag values

    No release notes provided.

    Tag.Fill added

    Return from SplitTag now Tags instead of []Tag.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • build(deps): bump github.com/muir/reflectutils from 0.0.2 to 0.1.0

    build(deps): bump github.com/muir/reflectutils from 0.0.2 to 0.1.0

    Bumps github.com/muir/reflectutils from 0.0.2 to 0.1.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • Reflectutils

    Reflectutils

    Handle simple unpacking with reflectutils. Add support for array unpacking

    opened by muir 1
  • minor fix to mappable()

    minor fix to mappable()

    null

    opened by muir 1
  • fossa-action

    fossa-action

    null

    opened by muir 1
Releases(v0.1.1)
Owner
David Sharnoff
David Sharnoff
alog is a dependency free, zero/minimum memory allocation JSON logger with extensions

Alog (c) 2020-2021 Gon Y Yi. https://gonyyi.com. MIT License Version 1.0.0 Intro Alog was built with a very simple goal in mind: Support Tagging (and

Gon 13 Dec 13, 2021
Simple and blazing fast lockfree logging library for golang

glg is simple golang logging library Requirement Go 1.11 Installation go get github.com/kpango/glg Example package main import ( "net/http" "time"

Yusuke Kato 136 Jan 3, 2022
Logging library for Golang

GLO Logging library for Golang Inspired by Monolog for PHP, severity levels are identical Install go get github.com/lajosbencz/glo Severity levels Deb

Lajos Bencz 14 Aug 26, 2021
a golang log lib supports level and multi handlers

go-log a golang log lib supports level and multi handlers Use import "github.com/siddontang/go-log/log" //log with different level log.Info("hello wo

siddontang 29 Dec 28, 2021
LogVoyage - logging SaaS written in GoLang

No longer maintained, sorry. Completely rewritten v2 is going to be released soon. Please follow http://github.com/logvoyage LogVoyage - fast and simp

null 89 Aug 31, 2021
An golang log lib, supports tracking and level, wrap by standard log lib

Logex An golang log lib, supports tracing and level, wrap by standard log lib How To Get shell go get gopkg.in/logex.v1 source code import "gopkg.in/

chzyer 38 Jan 9, 2022
Utilities for slightly better logging in Go (Golang).

logutils logutils is a Go package that augments the standard library "log" package to make logging a bit more modern, without fragmenting the Go ecosy

HashiCorp 305 Jan 16, 2022
Dead simple, super fast, zero allocation and modular logger for Golang

Onelog Onelog is a dead simple but very efficient JSON logger. It is one of the fastest JSON logger out there. Also, it is one of the logger with the

Francois Parquet 401 Jan 4, 2022
A Go (golang) package providing high-performance asynchronous logging, message filtering by severity and category, and multiple message targets.

ozzo-log Other languages 简体中文 Русский Description ozzo-log is a Go package providing enhanced logging support for Go programs. It has the following fe

Ozzo Framework 118 Jan 11, 2022
Golang logging library

Golang logging library Package logging implements a logging infrastructure for Go. Its output format is customizable and supports different logging ba

Örjan Fors 1.7k Jan 5, 2022
A feature-rich and easy to use logger for golang

A feature-rich and easy to use logger for golang ?? Install ?? Common Logs lumber.Success() lumber.Info() lumber.Debug() lumber.Warning()

Matthew Gleich 47 Jan 3, 2022
A system and resource monitoring tool written in Golang!

Grofer A clean and modern system and resource monitor written purely in golang using termui and gopsutil! Currently compatible with Linux only. Curren

PES Open Source Community 190 Jan 12, 2022
Golang beautify data display for Humans

Golang beautify data display for Humans English 简体中文 Usage Examples package main import ( ffmt "gopkg.in/ffmt.v1" ) func main() { example() } typ

ffmt 248 Jan 12, 2022
Logstash like, written in golang

gogstash Logstash like, written in golang Download gogstash from github check latest version Use docker image tsaikd/gogstash curl 'https://github.com

Tsai KD 570 Jan 19, 2022
Open Source Supreme Monitor Based on GoLang

Open Source Supreme Monitor Based on GoLang A module built for personal use but ended up being worthy to have it open sourced.

SneakyKiwi 16 Dec 15, 2021
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.

Noodlog Summary Noodlog is a Golang JSON parametrized and highly configurable logging library. It allows you to: print go structs as JSON messages; pr

Gyoza Tech 35 Dec 29, 2021
HTTP request logger for Golang

Horus ?? Introduction Horus is a request logger and viewer for Go. It allows developers log and view http requests made to their web application. Inst

Michael Okoh 72 Dec 28, 2021
Example instrumentation of Golang Application with OpenTelemetry with supported configurations to export to Sentry.

Sentry + Opentelemetry Go Example Requirements To run this example, you will need a kubernetes cluster. This example has been tried and tested on Mini

Uddeshya Singh 9 Dec 17, 2021