Go-Mojito: Your quick-start into Golang-based Websites and APIs

Go-Mojito: Your quick-start into Golang-based Websites and APIs

Golang has been on the rise ever since it's release and managed to make a name for itself in the cloud development space. Popular software like Docker, Kubernetes and Caddy have all been written in Golang, bringing joy to developers at home or carrying big corporate clusters that serve our everyday needs.

Introducing Go-Mojito, a simple framework that will help you create websites and APIs with ease. Go-Mojito is the official successor to the PHP-based framework "Scalar" and is almost fully compatible with existing Website templates.

Features

While this is not a complete list of all features, it should give you an idea of what Go-Mojito is capable of (And possibly what it's not capable of).

Dependency Injection

One of the most annoying things to do is managing all your database connections, caches, logging instances, configurations... You get the idea. Go-Mojito can manage singleton as well as transient dependencies for you, and inject them where-ever you need them. This includes all handlers that are called by Go-Mojito.

package main

// Dependencies can be either inline like this
func InlineDependency(ctx mojito.Context, cache mojito.Cache) {
	...
}

// Or they can be collected in a struct
type Dependencies struct {
	Cache  mojito.Cache  `injector:"type"`
	Logger mojito.Logger `injector:"type"`
}

func StructDependency(ctx mojito.Context, ctx Dependencies) {
	...
}
Inline dependencies cannot be named dependencies as we have no way of knowing if you wanted a type-based or name-based dependency

In this example, you can see that dependencies can be either directly in the method signature or separated out into a struct.

Keen eyes will spot the injector:"type" tag in the struct, this tag tells the dependency injector to fill the field based on its type. That means, that whatever is registered as default for the type will be injected. The other option would be, injector:"name" which would resolve the dependency based on its type and name. This is necessary, because there can be multiple dependencies of the same type under a different name. A good example is database connections or caches.

package main

import (
	"github.com/go-mojito/mojito"
)

func main() {
	// This is how you register a singleton
	mojito.Register(func() YourType {
		var yourType YourType
		// Construct your type
		return yourType
	}, true)
    
    // And this is how you register a transient
    mojito.Register(func() YourType {
		var yourType YourType
		// Construct your type
		return yourType
	}, false)
}
The important part here is the return type of the function. It defines what type you are registering the dependency for

Registering a new dependency is as easy as the above. You tell Go-Mojito how to construct your dependency when it's needed by providing a constructor function, the boolean tells Mojito whether this is a singleton or a transient dependency. This will register the default dependency for the returned type. If you want to have multiple dependencies of the same type under different names, you have to register it in the following way.

package main

import (
	"github.com/go-mojito/mojito"
)

func main() {
	// This is how you register a named singleton
	mojito.RegisterNamed("name", func() YourType {
		var yourType YourType
		// Construct your type
		return yourType
	}, true)
}
If there is already a dependency with that name, you override that named dependency

And that is all there is about dependency injection. It's simple, but very powerful, and will surely lessen the pain of managing your application-wide connections and objects.

Extensibility & Modularity

The framework has been designed with extensibility and modularity in mind. This is why you can replace any part of the Mojito framework with your own. Don't like the default logger? Swap it out. Fancy a different template language? Sure, replace the built-in renderer. Or just register a second one and use two template formats at the same time, it's up to you! The only thing you cannot replace is the dependency injector, as it would need to inject itself, which would make for a great paradox but not for great code :)

package main

import (
	"github.com/go-mojito/mojito"
)

func main() {
	mojito.Register(func() mojito.Logger {
		var yourType YourLogger
		// Construct your logger
		return yourType
	}, true)
}
Usually you won't need to do this, unless you are not satisfied with the provided defaults

And just like that, the default logger has been replaced, all of Go-Mojito will automatically adjust to that change. It works best when you do this in an init method, then even the very first log messages will be printed using your logger.

In fact, that is how you replace everything in Go-Mojito, just override its default dependency. Of course, if you end up replacing everything, then I would ask why you even use Mojito, perhaps because of the handlers that work the same way even when you change the router? That's right, the only thing you cannot replace is the custom handler and middleware design. This is to ensure consistency across different routers and of course to retain the dependency injection ability.

Middleware Support

Speaking of middleware, Mojito has full support for early and late middleware, meaning before and after the handler executed. Middlewares have full access to the ViewBag and any metadata set on the request. They can also modify both objects or replace them completely if needed. They can even have dependencies injected just like handlers!

package main

import (
	"fmt"

	"github.com/go-mojito/mojito"
)

func main() {
	// Enable middleware on all routes
	mojito.WithMiddleware(RequestLogger)
    
    // Or just for a subset of routes
    mojito.WithGroup("/group", func(group router.Group) {
		group.WithMiddleware(RequestLogger)
	})
}

func RequestLogger(ctx mojito.Context, next func() error) (err error) {
	var start = time.Now()
	err = next()
	mojito.DefaultLogger().Fields(LogFields{
		"ms":   time.Since(start).Milliseconds(),
		"path": ctx.Request().GetRequest().URL.Path,
	}).Info(ctx.Request().GetRequest().Method)
	return
}
RequestLogger is part of the mojito package and can be used as mojito.RequestLogger

Template Rendering

Of course, there is no website without HTML code. To help you reduce duplicate code and get your content into your pages, Go-Mojito ships with a template renderer. By default, standard go templates can be used. We recommend using our Handlebars renderer though. There are a few benefits to the Handlebars renderer.

First, the partial system has been extended to dynamically load partials from your template directory. That means that you do not have to register your partials before using them, but instead you can just put in the relative file path of your partial.

Second, you can extend templates! It's like reverse partials. For example, your layout can be extended by all your subpages.

<html>
    <head>
    	<title>Layout</title>
    </head>
    <body>
    	<h1> Fancy Website Here </h1>
    	{{{subview}}}
    </body>
</html>
This could be a potential extenable view. Easily spotted by the {{{subview}}}

And then you can extend the above using template code like this.

{{#extends "Path/To/Above"}}
<h2>And this is a page of this website</h2>
{{/extends}}
You can dynamically extend templates by replacing the "" with a variable containing the template name

As you can see, everything wrapped in the extends helper will be injected into the extended template, if the extended template contains a {{{subview}}} placeholder.

Try it yourself

Using the above and the example code in the repository, you should be able to get something up and running in no time. If not, just open up an issue and tell us what went wrong. Please also consult our documentation before opening a ticket.

GitHub - go-mojito/mojito: Code your next Go web project with (a) Mojito! Mojito is a super-modular, fast, opinion-less framework to bootstrap your next Go web project.
Code your next Go web project with (a) Mojito! Mojito is a super-modular, fast, opinion-less framework to bootstrap your next Go web project. - GitHub - go-mojito/mojito: Code your next Go web proj...

For a bit of additional help to get started, please take a look at this tutorial written with Mojito. It will show you how all of the above features work and should give a good example on how to start with your app.

Go-Mojito - Code your next Go web project with (a) Mojito!
Go-Mojito is a fully customizable and modular web framework that allows you to write projects that do not hard-depend on any router, template engine, cache or logger.