Quick Start Guide

Add REST Handlers

GoFr simplifies the process of implementing CRUD (Create, Read, Update, Delete) operations by enabling the automatic generation of handlers directly from Go structs. This feature eliminates the need for writing repetitive boilerplate code, allowing developers to focus on application logic.

Default Behaviour

If the custom handlers ain't implemented on the struct, GoFr provides default handlers for each CRUD operation. These handlers handle basic database interactions:

  • Create: /entity Inserts a new record based on data provided in a JSON request body.
  • Read:
    • GET: /entity Retrieves all entities of the type specified by the struct.
    • GET: /entity/{id} Retrieves a specific entity identified by the {id} path parameter.
  • Update: /entity/{id} Updates an existing record identified by the {id} path parameter, based on data provided in a JSON request body.
  • Delete /entity/{id} Deletes an existing record identified by the {id} path parameter.

NOTE: The registered routes will have the same name as the given struct, but if you want to change route name, you can implement RestPath method in the struct:

type userEntity struct {
	Id         int    `json:"id"`
	Name       string `json:"name"`
	Age        int    `json:"age"`
	IsEmployed bool   `json:"isEmployed"`
}

func (u *userEntity) RestPath() string {
	return "users"
}

Overriding Default Handlers

While the default handlers provide basic functionality, user might want to customize their behavior for specific use cases. The AddRESTHandlers feature allows user to override these handlers by implementing methods within the struct itself.

Database Table Name

By default, GoFr assumes the struct name in snake-case matches the database table name for querying data. For example, UserEntity struct matches user_entity database table, cardConfig struct matches card_config database table, etc. To change table name, you need to implement TableName method in the struct:

type userEntity struct {
	Id         int    `json:"id"`
	Name       string `json:"name"`
	Age        int    `json:"age"`
	IsEmployed bool   `json:"isEmployed"`
}

func (u *userEntity) TableName() string {
	return "user"
}

Adding Database Constraints

By default, GoFr assumes to have manual insertion of id for a given struct, but to support SQL constraints like auto-increment, not-null user can use the sql tag while declaring the struct fields.

type user struct {
	ID         int    `json:"id"  sql:"auto_increment"`
	Name       string `json:"name"  sql:"not_null"`
	Age        int    `json:"age"`
	IsEmployed bool   `json:"isEmployed"`
}

Now when posting data for the user struct, the Id we be auto-incremented and the Name will be a not-null field in table.

Benefits of Adding REST Handlers of GoFr

  1. Reduced Boilerplate Code: Eliminate repetitive code for CRUD operations, freeing user to focus on core application logic.
  2. Consistency: Ensures consistency in CRUD operations across different entities by using a standardized approach.
  3. Flexibility: Allows developers to customize CRUD behavior as per application requirements, providing flexibility and extensibility.

Example

package main

import (
	"gofr.dev/examples/using-crud-from-struct/migrations"
	"gofr.dev/pkg/gofr"
)

type user struct {
	Id         int    `json:"id"`
	Name       string `json:"name"`
	Age        int    `json:"age"`
	IsEmployed bool   `json:"isEmployed"`
}

// GetAll : User can overwrite the specific handlers by implementing them like this
func (u *user) GetAll(c *gofr.Context) (interface{}, error) {
	return "user GetAll called", nil
}

func main() {
	// Create a new application
	a := gofr.New()

	// Add migrations to run
	a.Migrate(migrations.All())

	// AddRESTHandlers creates CRUD handles for the given entity
	err := a.AddRESTHandlers(&user{})
	if err != nil {
		return
	}

	// Run the application
	a.Run()
}

In this example, we define a user struct representing a database entity. The GetAll method in the provided code demonstrates how to override the default behavior for retrieving all entities. This method can be used to implement custom logic for filtering, sorting, or retrieving additional data along with the entities.

Few Points to Consider:

1. Passing Struct by Reference

The struct should always be passed by reference in the method AddRESTHandlers.

2. Field Naming Convention

GoFr assumes that struct fields in snake_case match the database column names.

  • For example, the IsEmployed field in the struct matches the is_employed column in the database.
  • Similarly, the Age field matches the age column.

3. Primary Key

The first field of the struct is typically used as the primary key for data operations. However, this behavior can be customized using GoFr's features.

4. Datatype Conversions

Go TypeSQL TypeDescription
uuid.UUID (from github.com/google/uuid or github.com/satori/go.uuid)CHAR(36) or VARCHAR(36)UUIDs are typically stored as 36-character strings in SQL databases.
stringVARCHAR(n) or TEXTUse VARCHAR(n) for fixed-length strings, while TEXT is for longer, variable-length strings.
int, int32, int64, uint, uint32, uint64INT, BIGINT, SMALLINT, TINYINT, INTEGERUse INT for general integer values, BIGINT for large values, and SMALLINT or TINYINT for smaller ranges.
boolBOOLEAN or TINYINT(1)Use BOOLEAN (supported by most SQL databases like PostgreSQL, MySQL) or TINYINT(1) in MySQL (where 0 is false, and 1 is true).
float32, float64FLOAT, DOUBLE, DECIMALUse DECIMAL for precise decimal numbers (e.g., financial data), FLOAT or DOUBLE for approximate floating-point numbers.
time.TimeDATE, TIME, DATETIME, TIMESTAMPUse DATE for just the date, TIME for the time of day, and DATETIME or TIMESTAMP for both date and time.

Check out the example on how to add REST Handlers in GoFr: Visit GitHub