Introduction

Get started with Go-Mojito, a web framework that allows you to write dependency-independent code by using dependency injection. Mix and match your dream-stack, change it anytime.

Features

Perfect for beginners and experts alike

  • Fully Customizable
  • Dependency Injection
  • Dynamic Handlers
  • Sane Defaults
  • Time Saving
  • Unopinionated Core

First Steps

Go-Mojito's core is unopinionated. This means that the built-in implementations are built using the standard library. We also provide opinionated default implementations that are implemented in Go-Mojito Defaults. They are highly recommended and will be used during this tutorial.

No worries, you can always swap out parts of the default stack and keep the rest!

Before we can begin, you need to initialize the go project and pull in the required dependencies. To do that, create a new empty folder and execute the following commands.

go mod init github.com/example/project
go get github.com/go-mojito/mojito
go get github.com/go-mojito/defaults

Replace github.com/example/project with your own project url

Go-Mojito expects a certain file structure by default for the resources folder. You can change that too of course. Apart from that, the file tree below shows our suggestion for a solid project structure.

  • handlers Handler Code
  • models DB Models
  • resources Static Files
    • assets CSS, Images, Scripts
    • templates Mojito Templates
  • services Data Providers
  • go.mod
  • go.sum
  • main.go Starting Point

The main.go file is the heart of your project. It defines available routes, dependencies, database connections and more. It also needs to import our default stack to enable it, which is what we are going to do.

package main

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

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

var (
	address string = "0.0.0.0:8123"
)

func main() {
	log.Info("Registering application routes...")

	log.Infof("Server has started on %s", address)
	log.Error(mojito.ListenAndServe(address))
}

Creating a Layout

Every website has a base, a common ground, a layout. You wouldn't want to copy your layout into every page of your site, so we will create a layout template. Or a so-called extendable template. Extendable templates allow other views to embedd themselves into it. Kind of like a generic class.

resources/templates/layouts/default.mojito

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Go-Mojito Quickstart</title>
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link href="/assets/style.css" rel="stylesheet">
</head>
<body>
    {{{subview}}}
</body>
</html>

That's all you need to create an extendable template. If your template contains , other templates will be able to embed themselves. Of course you can have mulitple of those, including mulitple Layouts for different pages.

Add your first page

You have a layout, but without content it's of little use. We will create a page that will embed itself into your layout and will introduce the use of variables. Variables are placeholders that will be replaced at render-time with the value you supply from inside the handler. We will get to that later.

resources/templates/home.mojito

{{#extends "Layouts/Default"}}
<div class="text-center" style="height: 100vh; display: flex; flex-flow: column; justify-content: center;">
    <img class="d-block mx-auto mb-4" src="https://github.com/go-mojito/mojito/raw/main/.github/assets/gopher.png" alt="" width="300">
    <h1 class="display-5 fw-bold">Go-Mojito Quickstart</h1>
    <div class="col-lg-6 mx-auto">
      <p class="lead mb-4">Congratulations, you have succesfully setup a go-mojito project that makes use of routing, extending templates, dependency injection, caching, dynamic handlers, middleware and the asset handler. Want to do more?</p>

      <div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
        <a href="https://go-mojito.infinytum.co/docs">Check the Docs</a>
      </div>
      <p class="mt-3">Last visited at {{formatdate lastVisit "15:04:05 on the 2 Jan 2006"}}</p>
    </div>
</div>
{{/extends}}

You may have noticed the formatdate helper being used in the template. This helper is one of many that are shipped with the default stack. It allows you to format unix timestamps (integers) or time.Time objects to a given format before rendering them into the template. Handy!

Give it some style

You have a layout, you have a page. We even imported bootstrap in the layout already so you can get started quickly. To show how you can add assets, we will add a small CSS file that will add a bit of custom styling to the mix. Extend it as you like!

resources/assets/style.css

.btn-primary {
    background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(146,13,233,1) 0%, rgba(21,54,241,1) 100%);
}

It's not very long, we know. Bootstrap did most of the heavy lifting, just like Go-Mojito.

Keeping it under control

We are almost ready to tie it all together. Your views are styled, but they are missing data. Your handler will give them the data they need. You will write a small handler that will use the Go-Mojito Cache to store the last access time, so we can display it.

handlers/handler.go

package handlers

import (
	"time"

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

func HomeHandler(ctx mojito.RendererContext, cache mojito.Cache) {
	var lastVisit time.Time
	cache.GetOrDefault("lastVisit", &lastVisit, time.Now())
	cache.Set("lastVisit", time.Now())

	ctx.ViewBag().Set("lastVisit", lastVisit)
	ctx.MustView("Home")
}

All the arguments in the handler are highly optional, depending on your use-case. See how Go-Mojito just injected the cache? All handlers and middleware behave like that. As long as you register all your custom services and dependencies with Go-Mojito, your handlers will be able to use them. Regardless of which routing stack you choose.

You can even register custom request and response types that will be filled in as if they were always there.

It's all coming together

Your main.go will need some changes to register the view you created in the last few steps. We will also register the asset handler so your custom styling will be served where the view expects it.

main.go

package main

import (
	"github.com/example/project/handlers"
	"github.com/go-mojito/mojito"
	"github.com/go-mojito/mojito/handler"
	"github.com/go-mojito/mojito/log"
	"github.com/go-mojito/mojito/middleware"

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

var (
	address string = "0.0.0.0:8123"
)

func main() {
	log.Info("Registering application routes...")
	mojito.GET("/", handlers.HomeHandler)
	mojito.WithMiddleware(middleware.RequestLogger)
	handler.HandleAssets()

	log.Infof("Server has started on %s", address)
	log.Error(mojito.ListenAndServe(address))
}

For future routes, you just need to register them with mojito.GET. The assets handler is optional if you don't need any assets. The request logger is also optional.

See the results

Congratulations, the project you just completed made use of a lof of core features Go-Mojito provides.

Caching

The last time of access is stored in and read from the cache.

Dependency Injection

The handler magically received a cache instance.

Dynamic Handlers

You added a dependency as a handler argument and it just worked.

Logging

You use the logger in main.go and the RequestLogger.

Middleware

The request logger is a builtin but optional middleware.

Modular

You replaced all builtin implementations with our opinionated default stack.

Routing

The routing implementation routes "/" to your view handler.

Templating

You used our opinionated handlebars system to create your templates.

Running the project

Start your project by running go run main.go in the root directory of your project. You should receive a log message indicating that Go-Mojito has started up. You can now visit it on: