gofr Documentation

An opinionated GoLang framework for accelerated microservice development. Built in support for databases and observability.

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy gofr on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name gofr -p 4317:4317 --restart always otel/opentelemetry-collector-contrib:latest
Configured Services & Ports
โš™๏ธ otel-collector
image: otel/opentelemetry-collector-contrib:latest
๐Ÿ”Œ Ports: 4317:4317, 4318:4318, 8889:8889
โš™๏ธ prometheus
image: prom/prometheus:latest
๐Ÿ”Œ Ports: 9090:9090
docker-compose.yml
yaml (docker-compose.yml)
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:latest
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "4317:4317"   # OTLP gRPC (METRICS_URL target)
      - "4318:4318"   # OTLP HTTP
      - "8889:8889"   # collector's own Prometheus endpoint (received metrics)

  # Optional: scrape the collector's re-exposed metrics for a full local view.
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    depends_on:
      - otel-collector

File: README.md

Listed in the [CNCF Landscape](https://landscape.cncf.io/?selected=go-fr)

๐ŸŽฏ **Goal**

GoFr is designed to simplify microservice development, with a key focus on Kubernetes deployment and out-of-the-box observability. While capable of building generic applications, microservices remain at its core.


๐Ÿ’ก **Key Features**

  1. Simple API Syntax
  2. REST Standards by Default
  3. Configuration Management
  4. Observability (Logs, Traces, Metrics)
  5. Inbuilt Auth Middleware & Custom Middleware Support
  6. gRPC Support
  7. HTTP Service with Circuit Breaker Support
  8. Pub/Sub
  9. Health Check for All Datasources
  10. Database Migration
  11. Cron Jobs
  12. Support for Changing Log Level Without Restarting
  13. Swagger Rendering
  14. Abstracted File Systems
  15. Websockets

๐Ÿš€ **Getting Started**

**Prerequisites**

  • GoFr requires Go version 1.24 or above.

**Installation**

To get started with GoFr, add the following import to your code and use Goโ€™s module support to automatically fetch dependencies:

go
import "gofr.dev/pkg/gofr"

Alternatively, use the command:

bash
go get -u gofr.dev/pkg/gofr

๐Ÿƒ **Running GoFr**

Here's a simple example to get a GoFr application up and running:

go
package main

import "gofr.dev/pkg/gofr"

func main() {
	app := gofr.New()

	app.GET("/greet", func(ctx *gofr.Context) (any, error) {
		return "Hello World!", nil
	})

	app.Run() // listens and serves on localhost:8000
}

To run this code:

bash
$ go run main.go

Visit localhost:8000/greet to see the result.


๐Ÿ“‚ **More Examples**

Explore a variety of ready-to-run examples in the GoFr examples directory.


๐Ÿ‘ฉโ€๐Ÿ’ป **Documentation**


๐Ÿ‘ **Contribute**

Join Us in Making GoFr Better

Share your experience: If youโ€™ve found GoFr helpful, consider writing a review or tutorial on platforms like Medium, Dev.to, or your personal blog.
Your insights could help others get started faster!

Contribute to the project: Want to get involved? Check out our CONTRIBUTING.md
guide to learn how you can contribute code, suggest improvements, or report issues.


๐Ÿ”’ **Secure Cloning**

To securely clone the GoFr repository, you can use HTTPS or SSH:

Cloning with HTTPS

bash
git clone https://github.com/gofr-dev/gofr.git

Cloning with SSH

bash
git clone [email protected]:gofr-dev/gofr.git

๐ŸŽ **Get a GoFr T-Shirt & Stickers!**

If your PR is merged, or if you contribute by writing articles or promoting GoFr, we invite you to fill out this form to claim your GoFr merchandise as a token of our appreciation!

Partners


File: docs/datasources/surrealdb/page.md


nextjs:
metadata:
title: "SurrealDB in GoFr โ€” Multi-Model Datasource Integration"


SurrealDB

Configuration

To connect to SurrealDB, you need to provide the following environment variables:

  • HOST: The hostname or IP address of your SurrealDB server.
  • PORT: The port number.
  • USERNAME: The username for connecting to the database.
  • PASSWORD: The password for the specified user.
  • NAMESPACE: Top level container in SurrealDB that groups databases.
  • DATABASE: The name of the database to connect to.
  • TLSENABLED: TLS mode (e.g., disable, require)

Setup

GoFr supports injecting SurrealDB database that supports the following interface. Any driver that implements the interface can be added
using app.AddSurrealDB() method, and users can use Surreal DB across application through the gofr.Context.

go
// SurrealDB defines an interface representing a SurrealDB client with common database operations.
type SurrealDB interface {
    // Query executes a Surreal query with the provided variables and returns the query results as a slice of interfaces{}.
    // It returns an error if the query execution fails.
    Query(ctx context.Context, query string, vars map[string]any) ([]any, error)

    // Create inserts a new record into the specified table and returns the created record as a map.
    // It returns an error if the operation fails.
    Create(ctx context.Context, table string, data any) (map[string]any, error)

    // Update modifies an existing record in the specified table by its ID with the provided data.
    // It returns the updated record as an interface and an error if the operation fails.
    Update(ctx context.Context, table string, id string, data any) (any, error)

    // Delete removes a record from the specified table by its ID.
    // It returns the result of the delete operation as an interface and an error if the operation fails.
    Delete(ctx context.Context, table string, id string) (any, error)

    // Select retrieves all records from the specified table.
    // It returns a slice of maps representing the records and an error if the operation fails.
    Select(ctx context.Context, table string) ([]map[string]any, error)

    HealthChecker
}

// SurrealDBProvider is an interface that extends SurrealDB with additional methods for logging, metrics, or connection management.
// It is typically used for initializing and managing SurrealDB-based data sources.
type SurrealDBProvider interface {
    SurrealDB

    provider
}

Import the gofr's external driver for SurrealDB:

shell
go get gofr.dev/pkg/gofr/datasource/surrealdb

The following example demonstrates injecting an SurrealDB instance into a GoFr application.

go
package main

import (
	"gofr.dev/pkg/gofr"
	"gofr.dev/pkg/gofr/datasource/surrealdb"
)

type Person struct {
	ID    string `json:"id,omitempty"`
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Email string `json:"email,omitempty"`
}

type ErrorResponse struct {
	Message string `json:"message"`
}

func main() {
	app := gofr.New()

	client := surrealdb.New(&surrealdb.Config{
		Host:       app.Config.Get("HOST"),
		Port:       app.Config.Get("PORT"),
		Username:   app.Config.Get("USERNAME"),
		Password:   app.Config.Get("PASSWORD"),
		Namespace:  app.Config.Get("NAMESPACE"),
		Database:   app.Config.Get("DATABASE"),
		TLSEnabled: app.Config.Get("TLSENABLED"),
	})

	app.AddSurrealDB(client)

	// GET request to fetch person by ID
	app.GET("/person/{id}", func(ctx *gofr.Context) (any, error) {
		id := ctx.PathParam("id")

		query := "SELECT * FROM type::thing('person', $id)"
		vars := map[string]any{
			"id": id,
		}

		result, err := ctx.SurrealDB.Query(ctx, query, vars)
		if err != nil {
			return nil, err
		}

		return result, nil
	})

	// POST request to create a new person
	app.POST("/person", func(ctx *gofr.Context) (any, error) {
		var person Person

		if err := ctx.Bind(&person); err != nil {
			return ErrorResponse{Message: "Invalid request body"}, nil
		}

		result, err := ctx.SurrealDB.Create(ctx, "person", map[string]any{
			"name":  person.Name,
			"age":   person.Age,
			"email": person.Email,
		})

		if err != nil {
			return nil, err
		}

		return result, nil
	})

	app.Run()
}

File: docs/datasources/solr/page.md


nextjs:
metadata:
title: "Solr in GoFr โ€” Search Engine Datasource Integration"


Solr

Configuration

To connect to Solr DB, you need to provide the following environment variables:

  • HOST: The hostname or IP address of your Solr DB server.
  • PORT: The port number.

Setup

GoFr supports injecting Solr database that supports the following interface. Any driver that implements the interface can be added
using app.AddSolr() method, and user's can use Solr DB across application with gofr.Context.

go
type Solr interface {
	Search(ctx context.Context, collection string, params map[string]any) (any, error)
	Create(ctx context.Context, collection string, document *bytes.Buffer, params map[string]any) (any, error)
	Update(ctx context.Context, collection string, document *bytes.Buffer, params map[string]any) (any, error)
	Delete(ctx context.Context, collection string, document *bytes.Buffer, params map[string]any) (any, error)

	Retrieve(ctx context.Context, collection string, params map[string]any) (any, error)
	ListFields(ctx context.Context, collection string, params map[string]any) (any, error)
	AddField(ctx context.Context, collection string, document *bytes.Buffer) (any, error)
	UpdateField(ctx context.Context, collection string, document *bytes.Buffer) (any, error)
	DeleteField(ctx context.Context, collection string, document *bytes.Buffer) (any, error)
}

User's can easily inject a driver that supports this interface, this provides usability
without compromising the extensibility to use multiple databases.

Import the gofr's external driver for Solr:

shell
go get gofr.dev/pkg/gofr/datasource/solr@latest

Note : This datasource package requires the user to create the collection before performing any operations.
While testing the below code create a collection using :
curl --location 'http://localhost:2020/solr/admin/collections?action=CREATE&name=test&numShards=2&replicationFactor=1&wt=xml'

go
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"gofr.dev/pkg/gofr"
	"gofr.dev/pkg/gofr/datasource/solr"
)

func main() {
	app := gofr.New()

	app.AddSolr(solr.New(solr.Config{
		Host: app.Config.Get("HOST"),
		Port: app.Config.Get("PORT"),
	}))

	app.POST("/solr", post)
	app.GET("/solr", get)

	app.Run()
}

type Person struct {
	Name string
	Age  int
}

func post(c *gofr.Context) (any, error) {
	p := []Person{{Name: "Srijan", Age: 24}}
	body, _ := json.Marshal(p)

	resp, err := c.Solr.Create(c, "test", bytes.NewBuffer(body), nil)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func get(c *gofr.Context) (any, error) {
	resp, err := c.Solr.Search(c, "test", nil)
	if err != nil {
		return nil, err
	}

	res, ok := resp.(solr.Response)
	if !ok {
		return nil, errors.New("invalid response type")
	}

	b, _ := json.Marshal(res.Data)
	err = json.Unmarshal(b, &Person{})
	if err != nil {
		return nil, err
	}

	return resp, nil
}

File: docs/datasources/scylladb/page.md


nextjs:
metadata:
title: "ScyllaDB in GoFr โ€” Cassandra-Compatible Datasource"


ScyllaDB

Configuration

To connect to ScyllaDB, you need to provide the following environment variables:

  • HOST: The hostname or IP address of your ScyllaDB server.
  • KEYSPACE: The top level namespace.
  • PORT: The port number.
  • USERNAME: The username for connecting to the database.
  • PASSWORD: The password for the specified user.

Setup

GoFr supports pluggable ScyllaDB drivers. It defines an interface that specifies the required methods for interacting
with ScyllaDB. Any driver implementation that adheres to this interface can be integrated into GoFr using the
app.AddScyllaDB() method.

text
/* Detailed source-code truncated for AI context efficiency. */

Import the gofr's external driver for ScyllaDB:

shell
go get gofr.dev/pkg/gofr/datasource/scylladb
go
package main

import (
	"github.com/gocql/gocql"
	"gofr.dev/pkg/gofr"
	"gofr.dev/pkg/gofr/datasource/scylladb"
	"gofr.dev/pkg/gofr/http"
)

type User struct {
	ID    gocql.UUID `json:"id"`
	Name  string     `json:"name"`
	Email string     `json:"email"`
}

func main() {
	app := gofr.New()

	client := scylladb.New(scylladb.Config{
		Host:     app.Config.Get("HOST"),
		Keyspace: app.Config.Get("KEYSPACE"),
		Port:     app.Config.Get("PORT"),
		Username: app.Config.Get("USERNAME"),
		Password: app.Config.Get("PASSWORD"),
	})

	app.AddScyllaDB(client)

	app.GET("/users/{id}", getUser)
	app.POST("/users", addUser)

	app.Run()
}

func addUser(c *gofr.Context) (any, error) {
	var newUser User
	err := c.Bind(&newUser)
	if err != nil {
		return nil, err
	}
	_ = c.ScyllaDB.ExecWithCtx(c, `INSERT INTO users (user_id, username, email) VALUES (?, ?, ?)`, newUser.ID, newUser.Name, newUser.Email)

	return newUser, nil
}

func getUser(c *gofr.Context) (any, error) {
	var user User
	id := c.PathParam("id")

	userID, err := gocql.ParseUUID(id)
	if err != nil {
		c.Logger.Error("Invalid UUID format:", err)
		return nil, err
	}

	err = c.ScyllaDB.QueryWithCtx(c, &user, "SELECT id, name, email FROM users WHERE id = ?", userID)
	if err != nil {
		c.Logger.Error("Error querying user:", err)
		return nil, err
	}

	return user, nil
}

File: docs/datasources/oracle/page.md


nextjs:
metadata:
title: "Oracle Database in GoFr โ€” Pluggable Driver Integration"


OracleDB

Configuration

To connect to OracleDB, you need to provide the following environment variables:

  • HOST: The hostname or IP address of your OracleDB server.
  • PORT: The port number.
  • USERNAME: The username for connecting to the database.
  • PASSWORD: The password for the specified user.
  • SERVICE: The specific Oracle database instance or service on the server that the client should connect to.

Setup

GoFr supports injecting OracleDB as a relational datasource through a clean, extensible interface. Any driver that implements the following interface can be added using the app.AddOracle() method, and users can access OracleDB throughout their application via gofr.Context.

go
type Oracle interface {
 Exec(ctx context.Context, query string, args ...any) error
 Select(ctx context.Context, dest any, query string, args ...any) error
}

This approach allows users to easily inject any compatible Oracle driver, providing both usability and the flexibility to use multiple databases in a GoFr application.

Important: Oracle Database Must Exist

Before running your GoFr application, you must ensure that the Oracle database and the required schema (such as the users table) are already created.

  • Oracle does not allow creating a database (PDB or CDB) via a simple SQL query from a standard client connection.
  • You must use Oracle tools (like DBCA, SQL*Plus as SYSDBA, or Docker container initialization) to create the database and pluggable database (PDB) before connecting your app.
  • Your application can create tables within an existing schema, but the database itself must be provisioned in advance.

Setting Up OracleDB with Docker

To help new users, the following steps outline how to quickly set up an OracleDB instance using Docker.

1. Prerequisites

  • Docker installed on your system.
  • An Oracle account (free) with access to the Oracle Container Registry.

2. Create Your Oracle Account

Visit the Oracle Container Registry and create or sign in to your account:

๐Ÿ‘‰ https://container-registry.oracle.com/ords/f?p=113:10:14574461221664:::::

3. Pull the Oracle Free Database Docker Image

In your terminal:

  1. Log in to the Oracle Container Registry using your Oracle account credentials:
sh
docker login container-registry.oracle.com
  1. After login, pull the Oracle Free Database image:
sh
docker pull container-registry.oracle.com/database/free:latest

4. Run the Oracle Database Container

You can now run the OracleDB container (replace YourPasswordHere with a suitable strong password):

sh
docker run -d --name oracle-free -p 1521:1521 -e ORACLE_PWD=YourPasswordHere container-registry.oracle.com/database/free:latest
  • The database will be available on port 1521
  • The default Pluggable Database (PDB) is FREEPDB1
  • The system user password is your ORACLE_PWD
  • The service name for connecting is FREEPDB1

You can verify the container is running:

sh
docker ps

5. Connect to the Oracle Database

Option 1: Direct SQL*Plus session from within the container:

sh
docker exec -it oracle-free sqlplus system/YourPasswordHere@localhost:1521/FREEPDB1

Option 2: Open bash shell inside the container and use SQL*Plus from there:

sh
docker exec -it oracle-free bash
sqlplus system/YourPasswordHere@localhost:1521/FREEPDB1

6. Create the `users` Table

Based on the Go struct:

go
type User struct {
 Id   string `db:"ID"`
 Name string `db:"NAME"`
 Age  int    `db:"AGE"`
}

Run the following SQL command in SQL*Plus:

sql
CREATE TABLE users (
 id   VARCHAR2(36) PRIMARY KEY,
 name VARCHAR2(100),
 age  NUMBER
);

This will create the required table for the GoFr application to interact with.

7. Sample OracleDB Config for GoFr

Setting Value
host localhost
port 1521
username system
password YourPasswordHere
service/SID FREEPDB1

Import the GoFr External Driver for OracleDB

bash
go get gofr.dev/pkg/gofr/datasource/oracle@latest

Example

go
package main

import (
 "gofr.dev/pkg/gofr"
 "gofr.dev/pkg/gofr/datasource/oracle"
)

type User struct {
 Id   string `db:"ID"`
 Name string `db:"NAME"`
 Age  int    `db:"AGE"`
}

func main() {
 app := gofr.New()

 app.AddOracle(oracle.New(oracle.Config{
  Host:     app.Config.Get("HOST"),
  Port:     app.Config.Get("PORT"),
  Username: app.Config.Get("USERNAME"),
  Password: app.Config.Get("PASSWORD")
  Service:  app.Config.Get("SERVICE"),
 }))

 app.POST("/user", Post)
 app.GET("/user", Get)

 app.Run()
}

func Post(ctx *gofr.Context) (any, error) {
 err := ctx.Oracle.Exec(ctx, "INSERT INTO users (id, name, age) VALUES (:1, :2, :3)",
  "8f165e2d-feef-416c-95f6-913ce3172e15", "aryan", 10)
 if err != nil {
  return nil, err
 }
 return "successfully inserted", nil
}

func Get(ctx *gofr.Context) (any, error) {
 var users []map[string]any
 err := ctx.Oracle.Select(ctx, &users, "SELECT id, name, age FROM users")
 if err != nil {
  return nil, err
 }
 return users, nil
}

Example API Usage

You can create a user and get users using the following commands on the command prompt:

  • Create a user:
sh
curl -X POST http://localhost:8000/user
  • Get all users:
sh
curl http://localhost:8000/user

File: docs/datasources/opentsdb/page.md


nextjs:
metadata:
title: "OpenTSDB in GoFr โ€” Time-Series Metrics Datasource"


OpenTSDB

Configuration

To connect to OpenTSDB, you need to provide the following environment variables:

  • HOSTS: The hostname or IP address of your OpenTSDB server.
  • MAXCONTENTLENGTH: Max length of the request body in bytes.
  • MAXPUTPOINTSNUM: Max number of data points that can be sent in a single PUT request.
  • DETECTDELTANUM: The number of data points that OpenTSDB looks at to spot unusual time gaps.

Setup

GoFr supports injecting OpenTSDB to facilitate interaction with OpenTSDB's REST APIs.
Implementations adhering to the OpenTSDB interface can be registered with app.AddOpenTSDB(),
enabling applications to leverage OpenTSDB for time-series data management through gofr.Context.

text
/* Detailed source-code truncated for AI context efficiency. */

Import the gofr's external driver for OpenTSDB:

go
go get gofr.dev/pkg/gofr/datasource/opentsdb

The following example demonstrates injecting an OpenTSDB instance into a GoFr application
and using it to perform a health check on the OpenTSDB server.

text
/* Detailed source-code truncated for AI context efficiency. */

File: docs/datasources/mongodb/page.md


nextjs:
metadata:
title: "MongoDB in GoFr โ€” Document Database Integration"


MongoDB

Configuration

To connect to MongoDB, you need to provide the following environment variables:

  • URI: Mongodb server URL that the client connects to.
  • DATABASE: The name of the database to connect to.
  • CONNECTIONTIMEOUT: The maximum time the client will wait while trying to establish a connection.

Setup

GoFr supports injecting MongoDB that supports the following interface. Any driver that implements the interface can be added
using app.AddMongo() method, and users can use MongoDB across application with gofr.Context.

go
type Mongo interface {
	Find(ctx context.Context, collection string, filter any, results any) error

	FindOne(ctx context.Context, collection string, filter any, result any) error

	InsertOne(ctx context.Context, collection string, document any) (any, error)

	InsertMany(ctx context.Context, collection string, documents []any) ([]any, error)

	DeleteOne(ctx context.Context, collection string, filter any) (int64, error)

	DeleteMany(ctx context.Context, collection string, filter any) (int64, error)

	UpdateByID(ctx context.Context, collection string, id any, update any) (int64, error)

	UpdateOne(ctx context.Context, collection string, filter any, update any) error

	UpdateMany(ctx context.Context, collection string, filter any, update any) (int64, error)

	CountDocuments(ctx context.Context, collection string, filter any) (int64, error)

	Drop(ctx context.Context, collection string) error
}

Users can easily inject a driver that supports this interface; this provides usability without compromising the extensibility to use multiple databases.

Import the gofr's external driver for MongoDB:

shell
go get gofr.dev/pkg/gofr/datasource/mongo@latest

Example

go
package main

import (
	"time"
	"go.mongodb.org/mongo-driver/bson"
	"gofr.dev/pkg/gofr/datasource/mongo"

	"gofr.dev/pkg/gofr"
)

type Person struct {
	Name string `bson:"name" json:"name"`
	Age  int    `bson:"age" json:"age"`
	City string `bson:"city" json:"city"`
}

func main() {
	app := gofr.New()

	db := mongo.New(mongo.Config{URI: app.Config.Get("URI"), Database: app.Config.Get("DATABASE"), ConnectionTimeout: app.Config.Get("CONNECTIONTIMEOUT")})

	// inject the mongo into gofr to use mongoDB across the application
	// using gofr context
	app.AddMongo(db)

	app.POST("/mongo", Insert)
	app.GET("/mongo/{name}", Get)

	app.Run()
}

func Insert(ctx *gofr.Context) (any, error) {
	var p Person
	err := ctx.Bind(&p)
	if err != nil {
		return nil, err
	}

	res, err := ctx.Mongo.InsertOne(ctx, "collection", p)
	if err != nil {
		return nil, err
	}

	return res, nil
}

func Get(ctx *gofr.Context) (any, error) {
	var result Person

	p := ctx.PathParam("name")

	err := ctx.Mongo.FindOne(ctx, "collection", bson.D{{"name", p}} /* valid filter */, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

File: docs/datasources/migrations/elasticsearch/page.md


nextjs:
metadata:
title: "Elasticsearch Migrations in GoFr โ€” Versioned Index Schemas"


Elasticsearch Migrations

Elasticsearch migrations in GoFr let you manage index schemas, mappings, settings and data in a version-controlled manner.
This guide explains how to implement and operate these migrations without breaking production.

Overview

Elasticsearch migrations help you:

  • Create and manage indices with proper mappings
  • Update index settings and configurations
  • Seed initial data or migrate existing data
  • Perform bulk operations efficiently
  • Maintain schema consistency across environments

Migration Tracking

GoFr automatically creates a gofr_migrations index in Elasticsearch to track applied migrations.
The index stores:

  • Migration version (timestamp)
  • Execution method (UP)
  • Start time and duration
  • Migration status

Basic Migration Structure

go
package main

import (
	"context"
	"gofr.dev/pkg/gofr"
	"gofr.dev/pkg/gofr/datasource/elasticsearch"
	"gofr.dev/pkg/gofr/migration"
)

func main() {
	app := gofr.New()

	// Configure Elasticsearch
	esClient := elasticsearch.New(elasticsearch.Config{
		Addresses: []string{"http://localhost:9200"},
	})
	app.AddElasticsearch(esClient)

	// Define migrations
	migrationsMap := map[int64]migration.Migrate{
		1640995200: {
			UP: func(d migration.Datasource) error {
				// Migration logic here
				return nil
			},
		},
	}

	// Register and run migrations
	app.Migrate(migrationsMap)
	app.Run()
}

Available Operations

Index Management

go
// Create an index with mappings and settings
CreateIndex(ctx context.Context, index string, settings map[string]any) error

// Delete an index
DeleteIndex(ctx context.Context, index string) error

Document Operations

go
// Index a single document
IndexDocument(ctx context.Context, index, id string, document any) error

// Delete a document by ID
DeleteDocument(ctx context.Context, index, id string) error

// Bulk operations for multiple documents
Bulk(ctx context.Context, operations []map[string]any) (map[string]any, error)

Migration Examples

1. Creating an Index with Mappings

go
1640995200: {
	UP: func(d migration.Datasource) error {
		settings := map[string]any{
			"mappings": map[string]any{
				"properties": map[string]any{
					"title": map[string]any{
						"type":     "text",
						"analyzer": "standard",
					},
					"price": map[string]any{
						"type": "float",
					},
					"category": map[string]any{
						"type": "keyword",
					},
					"created_at": map[string]any{
						"type": "date",
					},
					"tags": map[string]any{
						"type": "keyword",
					},
				},
			},
			"settings": map[string]any{
				"number_of_shards":   1,
				"number_of_replicas": 0,
				"analysis": map[string]any{
					"analyzer": map[string]any{
						"custom_text_analyzer": map[string]any{
							"type":      "standard",
							"stopwords": "_english_",
						},
					},
				},
			},
		}

		return d.Elasticsearch.CreateIndex(context.Background(), "products", settings)
	},
},

2. Seeding Initial Data

go
1640995300: {
	UP: func(d migration.Datasource) error {
		// Create sample products
		products := []map[string]any{
			{
				"title":      "Laptop",
				"price":      999.99,
				"category":   "electronics",
				"created_at": "2024-01-01T00:00:00Z",
				"tags":       []string{"computer", "portable"},
			},
			{
				"title":      "Coffee Mug",
				"price":      12.99,
				"category":   "kitchen",
				"created_at": "2024-01-01T00:00:00Z",
				"tags":       []string{"ceramic", "drink"},
			},
		}

		ctx := context.Background()
		for i, product := range products {
			err := d.Elasticsearch.IndexDocument(
				ctx,
				"products",
				fmt.Sprintf("%d", i+1),
				product,
			)
			if err != nil {
				return fmt.Errorf("failed to index product %d: %w", i+1, err)
			}
		}

		return nil
	},
},

3. Bulk Operations Migration

go
1640995400: {
	UP: func(d migration.Datasource) error {
		// Bulk index multiple documents efficiently
		operations := []map[string]any{
			// Index operation metadata
			{
				"index": map[string]any{
					"_index": "products",
					"_id":    "bulk_1",
				},
			},
			// Document data
			{
				"title":    "Bulk Product 1",
				"price":    19.99,
				"category": "bulk",
			},
			// Another index operation
			{
				"index": map[string]any{
					"_index": "products",
					"_id":    "bulk_2",
				},
			},
			// Document data
			{
				"title":    "Bulk Product 2",
				"price":    29.99,
				"category": "bulk",
			},
			// Delete operation
			{
				"delete": map[string]any{
					"_index": "products",
					"_id":    "old_product",
				},
			},
		}

		ctx := context.Background()
		result, err := d.Elasticsearch.Bulk(ctx, operations)
		if err != nil {
			return fmt.Errorf("bulk operation failed: %w", err)
		}

		// Check for errors in bulk response
		if errors, ok := result["errors"].(bool); ok && errors {
			return fmt.Errorf("bulk operation had errors: %v", result)
		}

		return nil
	},
},

4. Index Settings Update

go
1640995500: {
	UP: func(d migration.Datasource) error {
		// Create a new index with updated settings
		settings := map[string]any{
			"mappings": map[string]any{
				"properties": map[string]any{
					"title": map[string]any{
						"type":     "text",
						"analyzer": "custom_text_analyzer",
					},
					"description": map[string]any{
						"type":     "text",
						"analyzer": "standard",
					},
					"price": map[string]any{
						"type": "float",
					},
				},
			},
			"settings": map[string]any{
				"number_of_shards":   2, // Increased shards
				"number_of_replicas": 1, // Added replica
				"refresh_interval":   "30s",
			},
		}

		return d.Elasticsearch.CreateIndex(context.Background(), "products_v2", settings)
	},
},

5. Data Migration Between Indices

go
1640995600: {
	UP: func(d migration.Datasource) error {
		ctx := context.Background()

		// This would typically involve:
		// 1. Reading data from old index (using Search - not shown in interface yet)
		// 2. Transforming data if needed
		// 3. Bulk indexing to new index
		// 4. Deleting old index

		// For now, we'll create the new index structure
		newSettings := map[string]any{
			"mappings": map[string]any{
				"properties": map[string]any{
					"product_name": map[string]any{ // Renamed from 'title'
						"type": "text",
					},
					"product_price": map[string]any{ // Renamed from 'price'
						"type": "float",
					},
					"product_category": map[string]any{ // Renamed from 'category'
						"type": "keyword",
					},
				},
			},
		}

		err := d.Elasticsearch.CreateIndex(ctx, "products_new_schema", newSettings)
		if err != nil {
			return fmt.Errorf("failed to create new schema index: %w", err)
		}

		// Clean up old index
		return d.Elasticsearch.DeleteIndex(ctx, "products_old")
	},
},

Bulk Operations Format

Index Operation

go
{
	"index": map[string]any{
		"_index": "index_name",
		"_id":    "document_id",
	},
}
// Followed by document data
{
	"field1": "value1",
	"field2": "value2",
}

Update Operation

go
{
	"update": map[string]any{
		"_index": "index_name",
		"_id":    "document_id",
	},
}
// Followed by update data
{
	"doc": map[string]any{
		"field1": "new_value1",
	},
}

Delete Operation

go
{
	"delete": map[string]any{
		"_index": "index_name",
		"_id":    "document_id",
	},
}
// No document data needed for delete

Best Practices

1. Index Naming

  • Use descriptive names: users, products, orders
  • Consider versioning: products_v1, products_v2
  • Use consistent naming conventions

2. Mapping Design

  • Define explicit mappings rather than relying on dynamic mapping
  • Choose appropriate field types
  • Consider analyzer requirements for text fields
  • Plan for future field additions

3. Settings Configuration

  • Set appropriate shard and replica counts
  • Configure refresh intervals based on use case
  • Set up custom analyzers if needed

4. Migration Safety

  • Test migrations on non-production data first
  • Use bulk operations for large data sets
  • Implement proper error handling
  • Consider index aliases for zero-downtime migrations

5. Performance Considerations

  • Use bulk operations for multiple documents
  • Batch operations appropriately (1 000 โ€“ 5 000 docs per batch)
  • Monitor cluster health during migrations
  • Consider disabling replicas during large data migrations

Error Handling

go
UP: func(d migration.Datasource) error {
	ctx := context.Background()

	// Check if index already exists (idempotent migration)
	settings := map[string]any{
		"mappings": map[string]any{
			"properties": map[string]any{
				"name": map[string]any{"type": "text"},
			},
		},
	}

	err := d.Elasticsearch.CreateIndex(ctx, "users", settings)
	if err != nil {
		// Handle specific Elasticsearch errors
		if strings.Contains(err.Error(), "resource_already_exists_exception") {
			// Index already exists, this is okay
			return nil
		}
		return fmt.Errorf("failed to create users index: %w", err)
	}

	return nil
},

Monitoring Migration Logs

plaintext
INFO [15:09:13] running migration 1640995200
DEBU [15:09:13] CREATE INDEX products            ELASTIC   215759ยตs products             {"mappings":{"properties":{"price":{"type":"float"},"title":{"type":"text"}}},"settings":{"number_of_replicas":0,"number_of_shards":1}}
DEBU [15:09:13] INDEX DOCUMENT products/1        ELASTIC    87374ยตs 1                    {"price":19.99,"title":"Sample Product"}

The logs show:

  • Operation type โ€“ CREATE INDEX, INDEX DOCUMENT, BULK, etc.
  • Execution time โ€“ In microseconds
  • Target โ€“ Index name, document ID
  • Query/Data โ€“ Full JSON of the operation (no base64 encoding)

Complete Example

text
/* Detailed source-code truncated for AI context efficiency. */

Enjoy consistent, version-controlled Elasticsearch migrations with GoFr!


File: docs/datasources/influxdb/page.md


nextjs:
metadata:
title: "InfluxDB in GoFr โ€” Time-Series Datasource Integration"


InfluxDB

GoFr supports injecting InfluxDB using an interface that defines the necessary methods to interact with InfluxDB v2+.
Any driver that implements this interface can be injected via the app.AddInfluxDB() method.


Interface

go
// InfluxDB defines the methods for interacting with an InfluxDB database.
type InfluxDB interface {
    CreateOrganization(ctx context.Context, orgName string) (string, error)
    DeleteOrganization(ctx context.Context, orgID string) error
    ListOrganization(ctx context.Context) (map[string]string, error)

    CreateBucket(ctx context.Context, orgID, bucketName string) (string, error)
    DeleteBucket(ctx context.Context, bucketID string) error
    ListBuckets(ctx context.Context, org string) (map[string]string, error)

    Ping(ctx context.Context) (bool, error)
    HealthCheck(ctx context.Context) (any, error)

    Query(ctx context.Context, org string, fluxQuery string) ([]map[string]any, error)
    WritePoints(ctx context.Context, bucket string, org string, points []container.InfluxPoint) error
}

This structure supports all essential InfluxDB operations including organization/bucket management, health checks, and metrics ingestion.

Import the gofr's external driver for influxdb:

bash
go get gofr.dev/pkg/gofr/datasource/influxdb@latest

Example

go
package main

import (
"context"
"fmt"
"time"

	"gofr.dev/pkg/gofr"
	"gofr.dev/pkg/gofr/datasource/influxdb"
)

func main() {
	
    // Create a new GoFr application
    app := gofr.New() 
	
	// Initialize InfluxDB client
	client := influxdb.New(influxdb.Config{
		Url:      "http://localhost:8086",
		Username: "admin",
		Password: "admin1234",
		Token:    "<your-token>",
	})

	// Add InfluxDB to application context
	app.AddInfluxDB(client)

	// Sample route
	app.GET("/greet", func(ctx *gofr.Context) (any, error) {
		return "Hello World!", nil
	})

	// Ping InfluxDB
	ok, err := client.Ping(context.Background())
	if err != nil {
		app.Logger().Fatal(err)
	}
	app.Logger().Debug("InfluxDB connected: ", ok)

	// Create organization
	orgID, err := client.CreateOrganization(context.Background(), "demo-org")
	if err != nil {
		app.Logger().Fatal(err)
	}

	// List organizations
	orgs, _ := client.ListOrganization(context.Background())
	app.Logger().Debug("Organizations: ")
	for id, name := range orgs {
		app.Logger().Debug(id, name)
	}

	// Create bucket
	bucketID, err := client.CreateBucket(context.Background(), orgID, "demo-bucket")
	if err != nil {
		app.Logger().Fatal(err)
	}

	// List buckets for organization
	buckets, err := client.ListBuckets(context.Background(), "demo-org")
	if err != nil {
		app.Logger().Fatal(err)
	}
	app.Logger().Debug("Buckets:", buckets)

	// Delete bucket
	if err := client.DeleteBucket(context.Background(), bucketID); err != nil {
		app.Logger().Fatal(err)
	}
	app.Logger().Debug("Bucket deleted successfully")

	// Delete organization
	if err := client.DeleteOrganization(context.Background(), orgID); err != nil {
		app.Logger().Fatal(err)
	}
	app.Logger().Debug("Organization deleted successfully")
	// Start the server
	app.Run()
}

File: docs/datasources/getting-started/page.md


nextjs:
metadata:
title: "GoFr Datasources โ€” Getting Started with the Interface"


Getting Started

GoFr adopts an interface-driven architecture for datasource integration, providing a consistent way to work with various databases.
Each datasource implements predefined interfaces that define core functionality, enabling you to inject any database client that satisfies these interface contracts.
Users can inject any client that satisfies the base interface defined by GoFr, making it easy to swap out or add new datasources as needed.

Keeping in mind the size of the framework in the final build, it felt counter-productive to keep all the database drivers within
the framework itself. Keeping only the most used MySQL and Redis within the framework, users can now inject databases
in the server that satisfies the base interface defined by GoFr. This helps in reducing the build size and in turn build time
as unnecessary database drivers are not being compiled and added to the build.

We are planning to provide custom drivers for most common databases, and is in the pipeline for upcoming releases!

Supported Databases

{% table %}

  • Datasource
  • Health-Check
  • Logs
  • Metrics
  • Traces
  • Version-Migrations

  • MySQL
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • REDIS
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • PostgreSQL
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • CockroachDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • ArangoDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • BadgerDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • Cassandra
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • ClickHouse
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • DGraph
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • MongoDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • NATS KV
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • OpenTSDB
  • โœ…
  • โœ…
  • โœ…

  • ScyllaDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • Solr
  • โœ…
  • โœ…
  • โœ…

  • SQLite
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • SurrealDB
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ…

  • Elasticsearch
  • โœ…
  • โœ…
  • โœ…
  • โœ…
  • โœ