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**
- Simple API Syntax
- REST Standards by Default
- Configuration Management
- Observability (Logs, Traces, Metrics)
- Inbuilt Auth Middleware & Custom Middleware Support
- gRPC Support
- HTTP Service with Circuit Breaker Support
- Pub/Sub
- Health Check for All Datasources
- Database Migration
- Cron Jobs
- Support for Changing Log Level Without Restarting
- Swagger Rendering
- Abstracted File Systems
- Websockets
๐ **Getting Started**
**Prerequisites**
**Installation**
To get started with GoFr, add the following import to your code and use Goโs module support to automatically fetch dependencies:
import "gofr.dev/pkg/gofr"Alternatively, use the command:
go get -u gofr.dev/pkg/gofr๐ **Running GoFr**
Here's a simple example to get a GoFr application up and running:
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:
$ go run main.goVisit localhost:8000/greet to see the result.
๐ **More Examples**
Explore a variety of ready-to-run examples in the GoFr examples directory.
๐ฉโ๐ป **Documentation**
- GoDoc: Official API documentation.
- GoFr Documentation: Comprehensive guides and resources.
๐ **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
git clone https://github.com/gofr-dev/gofr.gitCloning with SSH
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.
// 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:
go get gofr.dev/pkg/gofr/datasource/surrealdbThe following example demonstrates injecting an SurrealDB instance into a GoFr application.
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.
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:
go get gofr.dev/pkg/gofr/datasource/solr@latestNote : 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'
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 theapp.AddScyllaDB() method.
/* Detailed source-code truncated for AI context efficiency. */Import the gofr's external driver for ScyllaDB:
go get gofr.dev/pkg/gofr/datasource/scylladbpackage 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.
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:
- Log in to the Oracle Container Registry using your Oracle account credentials:
docker login container-registry.oracle.com- After login, pull the Oracle Free Database image:
docker pull container-registry.oracle.com/database/free:latest4. Run the Oracle Database Container
You can now run the OracleDB container (replace YourPasswordHere with a suitable strong password):
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
systemuser password is yourORACLE_PWD - The service name for connecting is
FREEPDB1
You can verify the container is running:
docker ps5. Connect to the Oracle Database
Option 1: Direct SQL*Plus session from within the container:
docker exec -it oracle-free sqlplus system/YourPasswordHere@localhost:1521/FREEPDB1Option 2: Open bash shell inside the container and use SQL*Plus from there:
docker exec -it oracle-free bash
sqlplus system/YourPasswordHere@localhost:1521/FREEPDB16. Create the `users` Table
Based on the Go struct:
type User struct {
Id string `db:"ID"`
Name string `db:"NAME"`
Age int `db:"AGE"`
}Run the following SQL command in SQL*Plus:
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
go get gofr.dev/pkg/gofr/datasource/oracle@latestExample
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:
curl -X POST http://localhost:8000/user- Get all users:
curl http://localhost:8000/userFile: 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 singlePUTrequest.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.
/* Detailed source-code truncated for AI context efficiency. */Import the gofr's external driver for OpenTSDB:
go get gofr.dev/pkg/gofr/datasource/opentsdbThe following example demonstrates injecting an OpenTSDB instance into a GoFr application
and using it to perform a health check on the OpenTSDB server.
/* 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.
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:
go get gofr.dev/pkg/gofr/datasource/mongo@latestExample
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
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
// 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) errorDocument Operations
// 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
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
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
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
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
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
{
"index": map[string]any{
"_index": "index_name",
"_id": "document_id",
},
}
// Followed by document data
{
"field1": "value1",
"field2": "value2",
}Update Operation
{
"update": map[string]any{
"_index": "index_name",
"_id": "document_id",
},
}
// Followed by update data
{
"doc": map[string]any{
"field1": "new_value1",
},
}Delete Operation
{
"delete": map[string]any{
"_index": "index_name",
"_id": "document_id",
},
}
// No document data needed for deleteBest 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
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
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
/* 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
// 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:
go get gofr.dev/pkg/gofr/datasource/influxdb@latestExample
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
- โ
- โ
- โ
- โ
- โ