Go4lage logo

Go4lage Documentation

Guide


In this guide, we explain general aspects of Go4lage. The main idea is that classic documentation is unnecessary as the code is so simple. Just read the source code and see how this documentation is made as well.

Lightning-Fast In-Memory Processing with Smart Goroutine Communication

Go4lage leverages a powerful feature that sets it apart from traditional web backends: smart communication between concurrent processes (goroutines). While most frameworks process requests in isolation, our goroutines work together through a sophisticated shared memory system.

How it works:

When requests come in, they're processed concurrently in separate goroutines - a core feature of Go. What makes go4lage special is that these goroutines maintain constant communication with each other. This creates a natural, built-in caching layer that lives in memory, dramatically reducing database load.

Benefits:

  • Even without caching, go4lage outperforms Java, JavaScript, PHP, and Python
  • With our shared memory system, the performance gap widens significantly
  • Database queries are minimized through intelligent caching
  • Zero configuration needed - it works out of the box

Real-World Application:

We've implemented this system for user management and permissions, demonstrating its simplicity and effectiveness. Developers can easily extend this pattern to other parts of their application, gaining immediate performance benefits.

Technical Consideration:

This feature is optimized for monolithic applications, as the inter-goroutine communication requires processes to run on the same instance. While horizontal scaling isn't supported at the application layer, you maintain full flexibility to scale your database layer, including compatibility with managed database services.

Ideal Use Case:

Choose go4lage when you need exceptional performance in a monolithic architecture. The built-in memory sharing system provides a powerful advantage for applications that prioritize speed and efficient resource usage.

Think of it like this: In your frontend or app you want to reduce backend calls. In go4lage you want to reduce db calls.

Go4lage as Static File Server

In the directory, there is a 'root' folder. Go4lage will copy the content from this folder into the cache. It will replace components with a Jinja-like syntax and will replace API endpoints accordingly.

  • index.html is the default entry point for the plain URL.
  • The admin/ folder is the default folder for the admin dashboard.

The files are also cache-busted - they are renamed at startup, and a random string is added so you can cache aggressively with your main reverse proxy.

The main goal of the static file server is to be able to serve the complete project from one package. A landing page and the main app as well. See React Vite Integration

Groups and Permissions

Go4lage comes with a group and permission system.

In go4lage, groups and permissions are simple strings.

Users have groups and groups have permissions. A group is also a permission.

If you have a simple app, using only groups might be good enough.

AuthMiddleware comes with two string options: groups and permissions.

go
r.Use(app.AuthMiddleware(utils.StaffGroup, "")) 

In this example, every user that has the group utils.StaffGroup can use the endpoint.

go
r.Use(app.AuthMiddleware("controller", "can_do_something")) 

In this example, every user with the group "controller" can access the endpoint, AND every user with the permission "can_do_something"

Also, normal users have to be set to active. Superusers can always access everything.

If you need to go more complex, you can add the permissions also to groups. For this you can code this in setup.go - there is an example provided. See func SetupGroupsAndPermissions(). Or you can manage this manually later via the admin dashboard GUI.

If you need to implement permissions on specific db objects, you need to implement your own checks.

Organizations

Go4lage includes multi-tenancy support through organizations. Each user can belong to one organization, creating isolated data clusters within your application.

The AuthMiddleware automatically injects organizational context into each request, making tenant-scoped queries straightforward:

go
type InfoKey struct {
    User         db.User
    Organization db.Organization
    Groups       []string
    Permissions  []string
}

rinfo, ok := r.Context().Value(utils.InfoContextKey).(utils.InfoKey)

This context provides everything needed for multi-tenant business logic: the authenticated user, their organization, and their authorization scope. Superusers bypass organizational boundaries and can access all data across organizations.

Use rinfo.Organization.ID to filter queries by tenant, ensuring data isolation without additional boilerplate.

Go4lage Serving React Vite

Go4lage can serve a React Vite PWA directly. But this is not a must, as a PWA can be served from another URL as well.

For this to happen, simply adjust the vite config to build in the root folder and disable Vite's cache busting and use an app name instead. Go4lage has its own cache busting.

#vite.config.ts
export default defineConfig({
  plugins: [react()],
  build: {
    outDir: '../root',
    emptyOutDir: false,
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name]-app----31fbbe----76b764.js',
        chunkFileNames: 'assets/[name]-app----31fbbe----76b764.js',
        assetFileNames: 'assets/[name]-app.[ext]',
      },
    },
  },
})

In the API class or function you need to have the const apiUrlstring = '{ % A piurl % }' without space. This will be automatically replaced in production code with the API URL and still work in dev mode.

#api.tsx
class API {
  apiUrl: string
  constructor() {
    const apiUrlstring = '{ %A piurl % }' // without space of course ;-)

    const trimmedString = apiUrlstring.slice(2, -2).trim()

    if (trimmedString === 'Apiurl') {
      this.apiUrl = 'http://127.0.0.1:8080/adminapi'
    } else {
      this.apiUrl = apiUrlstring + '/adminapi'
    }
  }

Go4lage Built-in Commands

Go4lage comes with CLI commands powered by Cobra that help you develop or get things done. They are defined in the main.go file.

startserver

./go4lage startserver - This starts the server. It is often used straight after build: go build && ./go4lage startserver

createsuperuser

./go4lage createsuperuser - This creates a superuser. Use this at least once, then you can use the /admin dashboard.

createfakeusers

./go4lage createfakeusers 100 - This creates 100 fake users for testing.

setupgp

./go4lage setupgp - This creates hard-coded groups and permissions. It is only additive and will not delete anything.

rungoose

This is your wrapper on the database. You can very simply reset your data or remigrate with this. These are the most important commands, and even faster than switching a SQLite db:

  • ./go4lage rungoose up
  • ./go4lage rungoose down - This runs one down migration; you have to confirm this with y.

Nginx Config

Go4lage can run with any reverse proxy or even without one. However, I recommend using nginx for SSL and as a load balancer if you want to run more than one instance of Go4lage on a server.

We warmly recommend certbot https://certbot.eff.org/ for SSL.

This is Go4lage's nginx config - you can probably cache for longer:

#nginx config
server {
    listen 80;
    server_name go4lage.com www.go4lage.com;
    return 301 https://go4lage.com$request_uri;
}

server {
    listen 443 ssl;
    server_name www.go4lage.com;
    ssl_certificate /etc/letsencrypt/live/go4lage.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/go4lage.com/privkey.pem;
    return 301 https://go4lage.com$request_uri;
}

server {
    listen 443 ssl;
    server_name go4lage.com;
    client_max_body_size 10M;
    ssl_certificate /etc/letsencrypt/live/go4lage.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/go4lage.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    add_header Cache-Control "public, max-age=3600";

    location / {
        proxy_pass http://127.0.0.1:8088;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}