BFF is a tool for making user interfaces

21 Oct 2024

Bff is a tool for making user interfaces

Tired of wrestling with HTML, CSS, and JavaScript just to create a simple user interface? Meet github.com/ebuckley/bff , the Go developer's new best friend for backend flows.

BFF is a powerful library that lets you build functional UIs and forms without writing a single line of frontend code. It's designed for Gophers who need quick, efficient internal tools without the hassle of learning new frameworks.

Key features:

  • Create multi-step forms and data pages with pure Go
  • Built-in websocket protocol for real-time updates
  • Suite of react components for display and input capturing
  • Focus on your logic, not on vite webpack and fotm JS libraries.

Let's dive into how BFF can streamline your development process and save you valuable time.

Write Go, get a free user interface

Our example will be a process that updates this Google Sheet. You can try out this exact code which I have integrated into the site here.

Creating the handler

func(ctx context.Context, io *bff.Io) error {
		io.Display.Markdown(`
# Update a Google Sheet

This little action exists to show you how you can update a Google Sheet from a BFF action.`)

Here, we set up our handler which simply takes an bff.Io. The Io struct is our portal into the UI, it creates components on the screen and lets us capture input from the person on the website. The interface for BFF is inspired by the http.Handler we know and love, but better because you can just return an error if you want to end execution.

Capturing input

    row, err := io.Input.Number("What Row would you like to update?")
    if err != nil {
        return err
    }
    col, err := io.Input.Text("What Column would you like to update?")
    if err != nil {
        return err
    }
    val, err := io.Input.Text("What value would you like to set?")
    if err != nil {
        return err
    }
    io.Display.Markdown(fmt.Sprintf("Updating row %d, column %s with value %s", row, col, val))

The Input struct encapsulates a bunch of handy form components. Right now you can choose from text,textarea,number,email,datetime and slider variants.

All the ceremony

      keyPath := os.Getenv("GOOGLE_API_KEY_PATH")
      if len(keyPath) == 0 {
          return fmt.Errorf("GOOGLE_API_KEY_PATH is not set")
      }
      keyJSON, err := os.ReadFile(keyPath)
      if err != nil {
          return err
      }

      srv, err := sheets.NewService(ctx, option.WithCredentialsJSON(keyJSON))
      if err != nil {
          return err
      }
      spreadsheetID := "1Uol7hBRk65_FVs5Z13WeZ2rJ2yv5z-OHKhi33fo2yIw"
      writeRange := fmt.Sprintf("Sheet1!%s%d", col, row)
      vr := &sheets.ValueRange{
          Values: [][]interface{}{{val}},
      }

      // Perform the update
      _, err = srv.Spreadsheets.Values.Update(spreadsheetID, writeRange, vr).
          ValueInputOption("RAW").Do()
      if err != nil {
          return err
      }

      io.Display.Markdown("# 🍬 Updated the Google Sheet 🍬")
      io.Display.Link("Do another update?", "/backend/a/update-a-google-sheet")

      return nil
   }

All the rest of this is about setting up and making the API call for updating the google sheet. This is a good thing. We want our handlers to not be about UI framework but about doing the work that’s actually delivering value. Finally we let the user know that the process is complete and invite them to do it again by showing a link.

All wrapped up in a websocket

The run of this entire handler function is within a single websocket connection, as soon as you call a function on the IO it will update the user interface. If you need to run a slow query – that’s totally fine the display function will be waiting to show your content as soon as it is ready for the user.

Do and Do not

Do use this for quickly standing up UI for your internal team to run processes and automations. Don’t run it for the public like I’m doing in this example. This thing is designed to be the retool alternative that doesn’t require any learning and clicking in a new UI.

Here’s a quick list of ideas you could use bff for today.

  • Build quick and helpful tools for your sales team to manage trials and feature flags.
  • Let the CMO run that big dirty SQL script with custom parameters which spits out a customer report from the database.
  • Quickly ban and unban users.
  • Publish notes/banners on the frontend
  • Send a quick email to a set of the existing customer base.
  • Run a script to spin up a new cloud environment.
  • Break-in-case-of-emergency button to call all the developers at once.
  • Process a customer refund

What’s next?

It is early days for this little library inspired by interval. I’m really interested in helping people like you get started with it.

  • Get the code github.com/ebuckley/bff . Feel free to create issues or ask questions through github.
  • Sign up to my list vv I email infrequently, but would love to keep in touch with the people that read and appreciate this blog.

Subscribe to my Newsletter

Want to know more? Put your email in the box for updates on my blog posts and projects. Emails sent a maximum of twice a month.