Building a User CRUD API in Kashvi (5 Minutes)

Welcome to Kashvi! If you're a fresher or transitioning from PHP/Laravel, you'll feel right at home. We're going to build a fully functional User API in just a few minutes.

No advanced features (like WebSockets or gRPC) here—just standard, clean RESTful architecture.

1. Create the Project

First, scaffold a fresh project. This creates a ready-to-use folder structure for you.

kashvi new my-api
cd my-api

This command generates your main.go, app/ folder for logic, database/ for schemas, and a .env file pre-configured for SQLite.

2. Generate the Resource

We need a Model (to represent the user), a Controller (to handle HTTP requests), a Migration (to create the database table), and a Seeder (to add dummy data).

Instead of creating these manually, Kashvi's CLI does it in one command:

kashvi make:resource User
  • app/models/user.go: Your data structure.
  • app/controllers/user_controller.go: Where your API logic lives.
  • database/migrations/xxxx_create_users_table.go: Instructions for creating the database table.
  • database/seeders/user_seeder.go: A place to create fake users for testing.

3. Define the Database Table

Open the newly generated migration file inside the database/migrations/ folder and add name and email columns.

database/migrations/xxxx_create_users_table.go
func (m *Migration) Up() {
    table := m.CreateTable("users")
    table.String("name").NotNull()
    table.String("email").Unique().NotNull()
}

Run the migration to create the table in SQLite:

kashvi migrate

4. Write the Controller Logic

Open app/controllers/user_controller.go and implement Store. Kashvi has built-in JSON validation—missing fields will automatically return a 422.

app/controllers/user_controller.go
package controllers

import (
    "github.com/shashiranjanraj/kashvi/pkg/ctx"
    "my-api/app/models"
    "my-api/database" // Assuming you export your db connection here
)

func (c *UserController) Store(ctx *ctx.Context) {
    var input struct {
        Name  string `json:"name" validate:"required,min=2"`
        Email string `json:"email" validate:"required,email"`
    }

    if !ctx.BindJSON(&input) {
        return
    }

    user := models.User{Name: input.Name, Email: input.Email}
    database.DB.Create(&user)

    ctx.Created(user)
}

5. Register the Route

Map a URL (like POST /api/users) to the controller. Add this inside your route registration function.

app/routes/api.go
import "my-api/app/controllers"

func RegisterAPI(r *router.Router) {
    api := r.Group("/api")

    userCtrl := controllers.NewUserController()
    api.Post("/users", "users.store", ctx.Wrap(userCtrl.Store))
}

6. Run the Server

kashvi serve

7. Test It Out

Create a user:

curl -X POST http://localhost:8080/api/users \
     -H "Content-Type: application/json" \
     -d '{"name": "Rahul", "email": "rahul@example.com"}'

Success response:

go
{
  "name": "Rahul",
  "email": "rahul@example.com"
}

Validation test (missing email):

curl -X POST http://localhost:8080/api/users \
     -H "Content-Type: application/json" \
     -d '{"name": "Rahul"}'

Error response:

go
{
  "error": "validation failed",
  "details": {
    "email": "email is required"
  }
}