Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 [Feature]: Unified binary support out of the box #394

Open
3 tasks done
the-hotmann opened this issue Sep 14, 2024 · 8 comments
Open
3 tasks done

🚀 [Feature]: Unified binary support out of the box #394

the-hotmann opened this issue Sep 14, 2024 · 8 comments

Comments

@the-hotmann
Copy link

the-hotmann commented Sep 14, 2024

Feature Description

I often create small Fiber apps, and I've been thinking about ways to make them easier to deploy and move around.

One of the main challenges I face is that certain files, like:

  • HTML templates,
  • CSS files,
  • JavaScript files
  • other static assets like favicons

are not included in the statically compiled binary. If all these files could be bundled within the binary, it would significantly simplify deployment. A few considerations arise once these assets are included in the binary:

Can CSS files still be served compressed after inclusion?
If so, it would be ideal to compress all static files at startup and store them in cache.
I’ve reviewed the following discussions:

From these, I believe the templ package could be a good solution for embedding HTML templates into the binary. I also read this comment #302 (comment) but thought it was a bad example. It only demonstrated rendering static text without passing variables, using templates/partials, or including other static files (CSS, JS, icons). It also didn’t address whether those static assets can still be served compressed or cached.

While I'm not specifically set on templ, despite its increasing popularity, what I'd really like is a simple way to bundle HTML templates, CSS, JS, and other static files into the final statically compiled binary for Fiber apps.

I think this is something many developers would find useful. Ideally, it would be as simple as importing a Fiber sub-package or enabling the feature like this:

app := fiber.New(fiber.Config{
    IncludeAssets: true,
    IncludeTemplates: true,
})

This way, all assets and templates would be included in the binary. I understand implementing this isn’t straightforward, but a possible approach could involve:

  • using the embed package, or
  • using spf13/afero.

However, I'm not familiar with those libraries.
From what I've gathered from other discussions, it seems that some of these features might already be achievable. However, they wouldn’t work seamlessly with Fiber and would require a considerable amount of workaround to implement effectively.

I would love to have a discussion about and see if that is something the maintainers of fiber are considering beeing useful, or not.

This is not primarily about using templ. The main focus is on finding a easy straightforward way to include all assets - typically residing outside the statically compiled binary - into the binary itself.

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my suggestion prior to opening this one.
  • I understand that improperly formatted feature requests may be closed without explanation.
@the-hotmann the-hotmann changed the title 🚀 [Feature]: Unified binary support 🚀 [Feature]: Unified binary support out of the box Sep 14, 2024
@idearat
Copy link

idearat commented Sep 14, 2024

I literally just finished playing with this while playing with combining Fiber and Hugo (and HTMX).

Got it working in a few minutes after reading: https://dev.to/aryaprakasa/serving-single-page-application-in-a-single-binary-file-with-go-12ij#serve-with-fiber-framework.

Note that it works as described for v2, but v3's change to remove filesystem and replace it with static had a side-effect that the static FS parameter won't accept an http.FS. I'm not a Go expert by any means... barely past beginner, so I just stayed with v2 for now.

Would love to see this documented formally though, it's actually pretty cool.

@gaby
Copy link
Member

gaby commented Sep 14, 2024

@idearat Can you open a ticket in https://github.com/gofiber/fiber, so we can look into that (http.FS) param.

Thanks

@the-hotmann
Copy link
Author

I actually also intended to create this issue at gofiber/fiber directly and not in gofiber/template, as I think the unification into one single static binary is not just about templates, but about all types of assets.

Anyway, I currently already switched to v3 (which is in beta) and would love to see an implementation which would let you include all types of assets into the binary.

@gaby
Copy link
Member

gaby commented Sep 14, 2024

@the-hotmann You can use embed.FS with the new static middleware.

//go:embed static.go config.go
var fsTestFilesystem embed.FS

app := fiber.New()

app.Get("/embed*", New("", static.Config{
    FS:     fsTestFilesystem,
	Browse: true,
}))

@the-hotmann
Copy link
Author

the-hotmann commented Sep 15, 2024

Ok it works. I have tried these two:

  • embed
  • os.DirFS

With os.DirFS I was able to server under this path /*
With embed I was not able to serve under this path /* - iit always defaulted to /static/*. The files have just been accessable through this prefix.

It works, but how can I serve a native favicon.ico under /favicon.ico if all static files are just served under /static/?
I understand that I can set the head-link like this:

<link rel="icon" href="favicon.png">

But if you open a picture in a new tab, your browser will not load the favicon, as it now does not know where it is. Just if html is served the head-link can be set.

Is there a workaround for this? Can files that are embedded also be served under the root-path?
Can I also embed template files like this?

I am talking about this:

var (
	engine      = html.New(template_path, ".html")
	fiberConfig = fiber.Config{
		Views:           engine,
	}
	app = fiber.New(fiberConfig)
)

@the-hotmann
Copy link
Author

the-hotmann commented Sep 15, 2024

Ok, got it to work on Fiber v3 aswell:

//go:embed static
embedDirStatic       embed.FS

func main() {

	var (
		newembedDirStatic fs.FS
		err               error
	)

	if newembedDirStatic, err = fs.Sub(embedDirStatic, "static"); err != nil {
		log.Panic(err)
	}

	app.Get("/*", static.New("/", static.Config{
		FS:        newembedDirStatic,
		Compress:  true,
		ByteRange: true,
	}))
	
	[...]
	
}

Hope this helps. As I mentioned above: everything is a little "hacky" and it would be cool if all this would be more tightly integrated into fiber :)

Now just the template embedding it missing

@the-hotmann
Copy link
Author

Now I have figured out how to also include the html/templates:

//go:embed templates
embedDirTemplates embed.FS
		
func main() {

	newembedDirTemplates, err := fs.Sub(embedDirTemplates, "templates")
	if err != nil {
		log.Panic(err)
	}

	var (
		engine      = html.NewFileSystem(http.FS(newembedDirTemplates), ".html")
		fiberConfig = fiber.Config{
			Views:           engine,
		}
		app               = fiber.New(fiberConfig)
	)
	
	[...]

}

I hope this helps someone.

@gaby
Copy link
Member

gaby commented Sep 15, 2024

Ok, got it to work on Fiber v3 aswell:

//go:embed static
embedDirStatic       embed.FS

func main() {

	var (
		newembedDirStatic fs.FS
		err               error
	)

	if newembedDirStatic, err = fs.Sub(embedDirStatic, "static"); err != nil {
		log.Panic(err)
	}

	app.Get("/*", static.New("/", static.Config{
		FS:        newembedDirStatic,
		Compress:  true,
		ByteRange: true,
	}))
	
	[...]
	
}

Hope this helps. As I mentioned above: everything is a little "hacky" and it would be cool if all this would be more tightly integrated into fiber :)

Now just the template embedding it missing

From my research golang doesnt support conditional embed. It's a compile time, not runtime feature. This limits how much we can do in Fiber. Will report back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants