DEV Community

Cover image for Building a full-stack app with Bud and Go
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

Building a full-stack app with Bud and Go

Written by Alex Merced✏️

Full-stack frameworks come in many shapes and sizes, but they often come in two general molds. The first mold includes frameworks like Ruby on Rails and Laravel, which are backend-centric with features to help you build your frontend within the same project, and easy-to-use conventions and templating. Then there is the second mold, which includes Next.js, Nuxt.js, and SvelteKit, which are more frontend-centric but allow you to create serverless functions as your backend within the same project.

New Go framework Bud differs from these full-stack frameworks by having a framework that starts quite minimalistic but allows frontend and backend code to expand based on the needs of its users instead of sprawling out a complicated CLI with potentially unnecessary files and configurations. Bud is a framework that keeps things simple but can scale up when needed.

Bud comes with built-in functionality for frontend development with Svelte and uses Go for the backend, using the faster toolsets for their respective parts of the web stack. Let’s take Bud for a test spin and create a page that calls to some backend endpoints.

To jump ahead in this article:

Setting up the Bud framework

Prerequisites:

  • Curl installed in your terminal environment
  • Go v1.6+
  • Node.js

To install Bud, run the following command: curl -sf https://raw.githubusercontent.com/livebud/bud/main/install.sh | sh

Confirm that all has gone well using command bud -h. Then, create a new app with bud create first-app.

This will give you a module name. The standard for module names in Go is the name of the repository you’re pushing it to, such as github.com/username/repo-name. Let’s change directories into our new app folder, cd first-app.

Run npm install to install all dependencies.

To run the development server, run the command bud run, and a server should allow your web app to be visible at localhost:3000.

Bud folder structure

When you examine your project, you’ll see the following structure:

/first-app
├─ /bud 
├─ /node_modules 
├─ .gitignore
├─ go.mod
├─ go.sum
├─ package.json
└─ package.lock.json
Enter fullscreen mode Exit fullscreen mode

Whenever you run bud run, the framework will look at your code and generate the necessary Go app in this folder, and you won’t need to touch the files in this folder. When you're ready to deploy the application, run bud build, which will bundle everything into a single binary in the /bud/app folder.

The following directories can be added and will be recognized by the framework:

  • controller: this folder will contain controller files that represent the backend routes of your app. Each file can provide standard CRUD routes for an endpoint by the name of the file
  • view: this folder can contain Svelte files and will auto-generate routes for them based on the file name
  • public: hosts static assets like images and CSS files
  • internal: this directory will be ignored, so it is a good place to host application-specific code that shouldn’t be treated as a backend or frontend route

Creating the root page in Bud

Although our pages are defined using Svelte, we should always have a corresponding controller for the page. Let’s first create a controller folder and, in that folder, create a file called controller.go. This will handle the endpoint /.

For all other controllers, you will create a folder with a similarly named Go file inside of it.

For example, the endpoint /posts would be /controller/posts/posts.go, and so forth.

In each of these files, we should declare a Controller struct, then define methods that have this struct as a receiver. Here is what the controller.go for our root route would look like:

package Controller
// Controller Struct to house all Action methods
type Controller struct {}
// Index shows a list of users
// Will automatically render view/index.svelte
// GET /users
func (c *Controller) Index() {}
Enter fullscreen mode Exit fullscreen mode

The name of the methods trigger is what you’d expect from RESTful conventions without us having to type out all the code. Here, the index method would return a view from a file called index.svelte in our view folder.

Views are essentially done through file-based routing, which will be familiar if you’ve ever used Next, Nuxt, or SvelteKit. Because Bud uses Svelte, index.svelte should represent your main page. So, create a view folder in your project and, inside it, create index.svelte with the following code.

<div>
    <h1>Here is the Main page of your app</h1>
    <section>
        <button on:click={handleClick}>Click This</button>
    </section>
</div>
<script>
    const handleClick = (event) => {
        alert("You Clicked the button")
    }
</script>
<style>
    h1, section {
        text-align: center;
    }
    button {
        border: none;
        padding: 10px;
        font-size: 1em;
        background-color: brown;
        color: white;
        border-radius: 40px;
    }
</style>
Enter fullscreen mode Exit fullscreen mode

Now, if we run our Bud server and head over to localhost:3000, we will see our page in action. Right now, Bud can handle the following actions in any controller:

Action method HTTP method URL Template rendered
index GET /endpoint /view/endpoint/index.svelte
show GET /endpoint/:id /view/endpoint/show.svelte
new GET /endpoint/new /view/endpoint/new.svelte
create POST /endpoint no template
edit GET /endpoint/:id/edit /view/endpoint/edit.svelte
update PUT /endpoint/:id no template
delete DELETE /endpoint/:id no template

Delivering data to your view

The return value of a method that renders a view will be passed as a prop to the Svelte template:

  • If the action returns a single struct, such as a Dog struct, it is delivered as a prop called dog
  • If the action returns an array of Dog structs, the prop is called dogs
  • If the action returns named return values, then each return value is a prop of the matching name

Let’s send some props to our root view and update controller/controller.go:

package controller
// Controller Struct to house all Action methods
type Controller struct {}
// Define a Struct of data to send to template
type Alex struct {
    Name string
    Age int
}
// Index shows a list of users
// Will automatically render view/index.svelte
// Define our struct as the return value and return it
// GET /users
func (c *Controller) Index() Alex {
    return Alex{"Alex Merced", 37}
}
Enter fullscreen mode Exit fullscreen mode

Now, let’s update our view to bring in the prop and use it when we click the button. Update view/index.svelte accordingly:

<div>
    <h1>Here is the Main page of your app</h1>
    <section>
        <button on:click={handleClick}>Click This</button>
    </section>
</div>
<script>
    export let alex;
    const handleClick = (event) => {
        alert(`My name is ${alex.Name} and age is ${alex.Age}`)
    }
</script>
<style>
    h1, section {
        text-align: center;
    }
    button {
        border: none;
        padding: 10px;
        font-size: 1em;
        background-color: brown;
        color: white;
        border-radius: 40px;
    }
</style>
Enter fullscreen mode Exit fullscreen mode

Click on the button. Notice the data returned by our Go method is being used in our Svelte template. Neat!

How Bud compares to other frameworks

There are still many features Bud doesn’t have yet, but given that it's early in its life, it is quite impressive. Let’s compare Bud to other frameworks and features we hope to see with Bud in the future.

Bud vs. Ruby on Rails and Laravel

These frameworks can easily add features like web sockets, channels, and more. Bud has reserved some future directory names that allude to eventually including these features.

Bud vs. Next.js, Nuxt.js, and SvelteKit

While allowing some level of server-side rendering, Next, Nuxt, and SvelteKit still heavily rely on the client-side nature of React, Vue, and Svelte, respectively. This enables the possibility of using state management solutions like Redux inside of them. Meanwhile, Bud uses the more traditional approach of letting the backend manage state and doesn’t yet have a clear path to similar state management patterns. In its reliance on RESTful conventions and web standards, it is probably more similar to Remix.js.

Conclusion

Bud embraces many of the conventions, such as file-based routing and RESTful controller methods, that we see in frameworks like Next and Ruby on Rails, and bundles them into one framework. While Bud is still working on building more functionality and features to be a robust framework with a solution for all scenarios like Laravel, Bud provides a simple convention-based workflow, making working with and getting the benefits of Go and Svelte a breeze.

Top comments (0)