blixt

command module
v0.0.0-...-c3a9e6c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2024 License: MIT Imports: 2 Imported by: 0

README

⚡️Blixt

[!CAUTION] Blixt is in early development, and is not yet fully functional. Additionally, the stdlib blocks are not yet all implemented.

Blixt is Firebase's evil relative, seen once a year at family reunions. Write a config file, and Blixt will generate an extensible Go backend for you. Use library blocks to add functionality, and deploy as a monolith or microservices with granular control.

Quickstart

Install Blixt:

go install github.com/Xavier-Maruff/blixt@latest

Create a new Blixt project, and follow the prompts:

blixt new myproject

Blocks

Blixt backends consist of blocks, which are logical units of functionality. Each block is a separate module that is declared in the config file, and connects to other blocks to provide rich functionality. Some common blocks are:

  • auth: User authentication and authorization
  • user: User management
  • file: File storage
  • db: SQLite database access
  • notify: Email notifications

Under the hood, each block is a separate ConnectRPC service, which makes it easy to segment groups of blocks over the network.

Examples

Blixt is configured through a Pkl file blixt.pkl, at the root of your project. In this file, you can import third-party blocks, define your own blocks, and set up connections between them.

Here is an example of a simple Blixt configuration that uses the standard lib to provide user management and authentication:

amends "https://lib.blxt.org/pkl/base.pkl"
import "https://lib.blxt.org/pkl/stdlib.pkl"

blocks = new {
    stdlib.auth
    stdlib.user
}

The standard library blocks automatically connect to each other, so in this case we don't need to define any connections between them.

This will generate a monolithic backend, which we can access like a regular REST API with JSON, or via gRPC. If we want to instead deploy it as two separate services, we can define 'groups':

// ... same as before

groups = new {
    (blixt.group) {
        name = "a_group"
        blocks = new {
            // the block.name field uniquely identifies a block
            stdlib.auth.name
        }
    }

    (blixt.group) {
        name = "b_group"
        blocks = new {
            stdlib.user.name
        }
    }
}

Every group produces an independent main package, which can be run as a separate service. Block method calls are automatically forwarded between groups via gRPC, so your code doesn't change at all, no matter how you define your groups.

We can extend the functionality by adding more blocks, or by creating our own. The 'stdlib.user' block, for example, connects optionally to the 'stdlib.notify' block to verify user emails. If we want to instead implement our own notification system, we can define a new block, and connect it to the user block in the slot where the 'stdlib.notify' block would go:

amends "https://lib.blxt.org/pkl/base.pkl"
import "https://lib.blxt.org/pkl/stdlib.pkl"

blocks = new {
    notify
    stdlib.auth
    stdlib.user {
        connections = (stdlib.user.connections) {
          ["notify"] = notify.name
        }

        config = (stdlib.user.config) {
          ["verify_users"] = true
        }
    }
}

//we define our own custom block like this
notify = (blixt.block) {
    name = "notify"
    //the go package where the block's methods are implemented
    package = "this/go/package"
    remote = false
    //we implement the same interface as the stdlib.notify block
    methods = stdlib.notify.methods
}

We could then implement the block in the package this/go/package, like this:

package notify

import (
	"context"
	"log"

	apiv1 "path/to/blixt/gen/proto/v1"
	"path/to/blixt/gen/notify"
)

type impl struct {
	//store any persistent state here
}

func New() (notify.Block, error) {
	//do any setup here
	return &impl{}, nil
}

func (s *state) Connect(ctx context.Context, block string, conn interface{}) error {
	//this is called when we register a connection from this block to another
	//in this case, we don't need to do anything
}

//now we implement the methods defined in the block
//the request/response types are named {block name}{method name}Request/Response
func (s *state) Send(ctx context.Context, req *apiv1.NotifySendRequest)
(*apiv1.NotifySendResponse, error) {
	//send the email
	log.Printf("Sending email to %s with subject %s and message %s", req.Address, req.Subject, req.Message)
	return &apiv1.NotifySendResponse{Success: true}, nil
}

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
gen
pkl/base
Code generated from Pkl module `base`.
Code generated from Pkl module `base`.
pkl/blixt
Code generated from Pkl module `blixt`.
Code generated from Pkl module `blixt`.
stdlib
gen/cmd/main command
test

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL