# Walkthrough: Parsley Integration with GoFiber

This guide demonstrates how to integrate the Parsley dependency injection framework with the GoFiber web framework. By following this example, you'll learn how to set up a GoFiber application with dependency injection managed by Parsley, making your codebase more modular, testable, and maintainable.

# Project Structure

The code for this example can be found at examples/integrations/gofiber it has the following structure:

gofiber
├── cmd
│   └── main.go
└── internal
    ├── application.go
    ├── modules
    │   ├── fiber_module.go
    │   ├── greeter_module.go
    │   └── route_handlers_module.go
    ├── route_handlers
    │   ├── greeter.go
    │   └── types.go
    └── services
        └── greeter.go

This article describes the project's structure and the purpose of each module in detail.

# Main Application

The main entry point of the application is in the cmd/main.go file:

package main

import (
	"context"

	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal"
	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal/modules"
	"github.com/matzefriedrich/parsley/pkg/bootstrap"
)

func main() {

	ctx := context.Background()

	err := bootstrap.RunParsleyApplication(ctx, internal.NewApp,
		modules.ConfigureFiber,
		modules.ConfigureGreeter)

	if err != nil {
		panic(err)
	}
}

In this file, the RunParsleyApplication function is called to bootstrap the application. It initializes the Parsley application context and configures the GoFiber server with the necessary services and route handlers.

# Modules

The modules package contains the service configurations required to set up the GoFiber application. The fiber_module.go module configures the Fiber application and registers it as a singleton service within the Parsley framework:

package modules

import (
	"github.com/gofiber/fiber/v2"
	"github.com/matzefriedrich/parsley/pkg/registration"
	"github.com/matzefriedrich/parsley/pkg/types"
)

var _ types.ModuleFunc = ConfigureFiber

// ConfigureFiber Configures all services required by the Fiber app.
func ConfigureFiber(registry types.ServiceRegistry) error {
	registration.RegisterInstance(registry, fiber.Config{
		AppName:   "parsley-fiber-demo",
		Immutable: true,
	})
	registry.Register(newFiber, types.LifetimeSingleton)
	registry.RegisterModule(RegisterRouteHandlers)
	return nil
}

func newFiber(config fiber.Config) *fiber.App {
	return fiber.New(config)
}

This configuration ensures that the Fiber instance is initialized and available for dependency injection.

# Services

Next, the internal/services/greeter.go file defines the Greeter service, which returns a greeting message based on the provided name and politeness flag.

package services

import "fmt"

type Greeter interface {
	SayHello(name string, polite bool) string
}

type greeter struct {
}

func (g *greeter) SayHello(name string, polite bool) string {
	if polite {
		return fmt.Sprintf("Good day, %s!\n", name)
	} else {
		return fmt.Sprintf("Hi, %s\n", name)
	}
}

func NewGreeter() Greeter {
	return &greeter{}
}

The Greeter service is registered by the greeter_module.go module.

package modules

import (
	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal/services"
	"github.com/matzefriedrich/parsley/pkg/types"
)

// ConfigureGreeter Configures the services.Greeter service dependencies.
func ConfigureGreeter(registry types.ServiceRegistry) error {
	registry.Register(services.NewGreeter, types.LifetimeTransient)
	return nil
}

# Route Handlers

In this example, route handlers are also services, structs implementing the RouteHandler interface, which register one or more route handlers with the Fiber application. The interface is defined as follows:

package route_handlers

import "github.com/gofiber/fiber/v2"

type RouteHandler interface {
	Register(app *fiber.App)
}

The internal/route_handlers/greeter.go file registers the route handler for the /say-hello endpoint, which returns a greeting message based on the query parameters provided in the request. The logic for the message generation is handled by the Greeter service, which is injected into the NewGreeterRouteHandler method.

package route_handlers

import (
	"github.com/gofiber/fiber/v2"
	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal/services"
)

type greeterRouteHandler struct {
	greeter services.Greeter
}

// Register Registers all greeterRouteHandler route handlers.
func (h *greeterRouteHandler) Register(app *fiber.App) {
	app.Get("/say-hello", h.HandleSayHelloRequest)
}

// HandleSayHelloRequest Handles "GET /say-hello" requests.
func (h *greeterRouteHandler) HandleSayHelloRequest(ctx *fiber.Ctx) error {
	name := ctx.Query("name")
	msg := h.greeter.SayHello(name, true)
	ctx.Send([]byte(msg))
	return ctx.SendStatus(fiber.StatusOK)
}

var _ RouteHandler = &greeterRouteHandler{}

func NewGreeterRouteHandler(greeter services.Greeter) RouteHandler {
	return &greeterRouteHandler{
		greeter: greeter,
	}
}

The route_handler_module.go file handles the registration of the RouteHandler services themselves.

package modules

import (
	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal/route_handlers"
	"github.com/matzefriedrich/parsley/pkg/features"
	"github.com/matzefriedrich/parsley/pkg/registration"
	"github.com/matzefriedrich/parsley/pkg/types"
)

// RegisterRouteHandlers Registers all route handlers of the GoFiber app.
func RegisterRouteHandlers(registry types.ServiceRegistry) error {
	features.RegisterList[route_handlers.RouteHandler](registry)
	registration.RegisterTransient(registry, route_handlers.NewGreeterRouteHandler)
	return nil
}

This configuration ensures that all route handlers the application requires are correctly registered and injected into the Fiber application instance. Since the application service expects a set of route handler services, the RegisterList method must be used to register a list activator for the RouteHandler type.

# Application Logic

The internal/application.go file contains the main application service:

package internal

import (
	"context"

	"github.com/gofiber/fiber/v2"
	"github.com/matzefriedrich/parsley-docs/examples/integrations/gofiber/internal/route_handlers"
	"github.com/matzefriedrich/parsley/pkg/bootstrap"
)

type parsleyApplication struct {
	app *fiber.App
}

var _ bootstrap.Application = &parsleyApplication{}

// NewApp Creates the main application service instance. This constructor function gets invoked by Parsley; add parameters for all required services.
func NewApp(app *fiber.App, routeHandlers []route_handlers.RouteHandler) bootstrap.Application {
	for _, routeHandler := range routeHandlers {
		routeHandler.Register(app)
	}
	return &parsleyApplication{
		app: app,
	}
}

// Run The entrypoint for the Parsley application.
func (a *parsleyApplication) Run(_ context.Context) error {
	return a.app.Listen(":5502")
}

This file defines the parsleyApplication struct as the main application service. It registers the route handlers and starts the GoFiber server on port 5502. However, aspects like having the listener port configurable or a graceful server shutdown are omitted here, but they could be addressed here as well.

# Running the Application

To run the application, navigate to the root directory and execute the following command:

go run main.go

As configured in the code, the application will start a GoFiber server on http://localhost:5502. You can then access the /say-hello endpoint:

curl "http://localhost:5502/say-hello?name=John"

This should return:

Good day, John!

For more details on Parsley, check out the Parsley GitHub repository.