Simple guides introducing Fabrica with hands-on examples.
Goal: Build and run a working REST API without learning Kubernetes concepts or advanced patterns.
This guide treats Fabrica as a code generator for simple CRUD APIs. We’ll hide the advanced features and focus on getting you productive quickly.
A simple REST API for managing products with these endpoints:
POST /products - Create a productGET /products - List all productsGET /products/{id} - Get a specific productPUT /products/{id} - Update a productDELETE /products/{id} - Delete a productNo databases to configure. Everything runs in-memory to keep it simple.
go install github.com/openchami/fabrica/cmd/fabrica@latest
Verify installation:
fabrica --version
# Output: fabrica version v0.4.5
Create a new project with minimal complexity:
# Initialize simple project (creates myshop directory)
fabrica init myshop
# Enter project directory
cd myshop
gh repo create)If you’ve already created a repository with GitHub CLI or template:
# Create repo from template (example)
gh repo create myshop --template myorg/template --public
cd myshop
# Initialize Fabrica in current directory
fabrica init .
This will preserve existing files like .git, README.md, LICENSE, etc.
By default, fabrica init creates a project with a single v1 API version and the group example.fabrica.dev. You can customize this:
# Create project with custom API group
fabrica init myshop \
--group myorg.api
# Or use interactive mode for guided setup
fabrica init myshop --interactive
Available init flags:
--group - API group name (default: example.fabrica.dev)--module - Go module path--validation-mode - Validation: strict, warn, or disabled (default: strict)--events - Enable CloudEvents support--storage-type - Storage backend: file or ent (default: file)--db - Database driver for ent: sqlite, postgres, or mysql (default: sqlite)Both options create:
.fabrica.yaml with project configurationapis.yaml with API group and version configuration (default: example.fabrica.dev group, v1 version)go.mod with necessary dependenciescmd/, apis/, etc.)apis/example.fabrica.dev/v1/You’ll see:
🚀 Creating myshop project...
├─ Created .fabrica.yaml
├─ Created apis.yaml (group example.fabrica.dev, storage v1)
✅ Project initialized successfully!
Next steps:
1. Add resources with 'fabrica add resource <name>'
2. Define types in apis/example.fabrica.dev/<version>/*_types.go
3. Run 'fabrica generate' to generate code
4. Run 'go mod tidy' to update dependencies
5. Start development with 'go run ./cmd/server/'
Use the Fabrica CLI to create a Product resource:
fabrica add resource Product
This command creates a resource definition in your project’s versioned API directory. By default, resources are created in the hub (storage) version at apis/example.fabrica.dev/v1/product_types.go:
Available add resource flags:
--version - Target API version (default: storage version from apis.yaml)--force - Force adding to non-alpha versions--with-validation - Include validation tags (default: true)--with-status - Include Status struct (default: true)If you open the newly generated resource definition file in your preferred editor, you’ll see:
package v1
import (
"context"
"github.com/openchami/fabrica/pkg/fabrica"
)
// Product represents a product resource
type Product struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Metadata fabrica.Metadata `json:"metadata"`
Spec ProductSpec `json:"spec" validate:"required"`
Status ProductStatus `json:"status,omitempty"`
}
// ProductSpec defines the desired state of Product
type ProductSpec struct {
Description string `json:"description,omitempty" validate:"max=200"`
// Add your spec fields here
}
// ProductStatus defines the observed state of Product
type ProductStatus struct {
Phase string `json:"phase,omitempty"`
Message string `json:"message,omitempty"`
Ready bool `json:"ready"`
// Add your status fields here
}
// Validate implements custom validation logic for Product
func (r *Product) Validate(ctx context.Context) error {
// Add custom validation logic here
return nil
}
// All content beyond this point has been omitted for brevity
You can edit the fields in ProductSpec as needed. Your resource definitions stay in the versioned API directory (apis/<group>/<version>/). Let’s add several additional fields and ensure the updated struct definition resembles the code below:
// ProductSpec defines the desired state of Product
type ProductSpec struct {
Description string `json:"description,omitempty" validate:"max=200"`
Name string `json:"name" validate:"required,min=1,max=100"`
Price float64 `json:"price" validate:"min=0"`
InStock bool `json:"inStock"`
}
Now generate the REST API handlers, storage, and routes:
fabrica generate
This command:
Product resourcecmd/server/openapi_extensions.go (once) for you to add custom routes to the specYou’ll see:
🔧 Generating code...
📦 Found 1 resource(s): Product
├─ Registering Product...
├─ Generating handlers...
├─ Generating storage...
├─ Generating OpenAPI spec...
└─ Done!
✅ Code generation complete!
After code generation, update your Go module dependencies:
go mod tidy
This resolves all the new imports that were added by the code generator.
Start the server:
go run ./cmd/server/
You’ll see:
[rocky@localhost myshop]$ go run ./cmd/server/
2026/04/02 11:19:31 Starting myshop server...
2026/04/02 11:19:31 File storage initialized in ./data
2026/04/02 11:19:31 Server starting on 0.0.0.0:8080
2026/04/02 11:19:31 Storage: file backend in ./data
Your API is now running at http://localhost:8080!
Open a new terminal and try the API:
curl -X POST http://localhost:8080/products \
-H "Content-Type: application/json" \
-d '{
"spec": {
"name": "laptop-pro",
"displayName": "MacBook Pro",
"description": "15-inch MacBook Pro with M2 chip",
"price": 1999.99,
"inStock": true
}
}'
Response:
{
"apiVersion": "v1",
"kind": "Product",
"metadata": {
"name": "laptop-pro",
"uid": "pro-abc123def456",
"createdAt": "2025-10-15T10:30:00Z",
"updatedAt": "2025-10-15T10:30:00Z"
},
"spec": {
"name": "MacBook Pro",
"description": "15-inch MacBook Pro with M2 chip",
"price": 1999.99,
"inStock": true
},
"status": {
"ready": true,
}
}
curl http://localhost:8080/products
Response (flat JSON array):
[
{
"apiVersion": "v1",
"kind": "Product",
"metadata": {
"name": "laptop-pro",
"uid": "pro-abc123def456",
"createdAt": "2025-10-15T10:30:00Z",
"updatedAt": "2025-10-15T10:30:00Z"
},
"spec": {
"name": "MacBook Pro",
"description": "15-inch MacBook Pro with M2 chip",
"price": 1999.99,
"inStock": true
},
"status": {
"ready": true,
}
}
]
curl http://localhost:8080/products/pro-abc123def456
curl -X PUT http://localhost:8080/products/pro-abc123def456 \
-H "Content-Type: application/json" \
-d '{
"spec": {
"name": "laptop-pro",
"displayName": "MacBook Pro M3",
"description": "Latest 15-inch MacBook Pro with M3 chip",
"price": 2199.99,
"inStock": true
}
}'
curl -X DELETE http://localhost:8080/products/pro-abc123def456
Response:
{
"message": "Product deleted successfully"
}
Let’s peek under the hood (but don’t worry, you don’t need to edit these files):
myshop/
├── apis
│ └── example.fabrica.dev
│ └── v1
│ └── product_types.go
├── apis.yaml
├── cmd
│ ├── client
│ │ └── main.go
│ └── server
│ ├── main.go
│ ├── models_generated.go
│ ├── openapi_generated.go
│ ├── product_handlers_generated.go
│ └── routes_generated.go
├── data
│ └── products
│ └── product-62ed4401.json
├── go.mod
├── go.sum
├── internal
│ ├── middleware
│ │ ├── conditional_middleware_generated.go
│ │ ├── validation_middleware_generated.go
│ │ └── versioning_middleware_generated.go
│ └── storage
│ ├── storage_generated.go
│ └── storage.go
├── pkg
│ ├── apiversion
│ │ └── registry_generated.go
│ ├── client
│ │ ├── client_generated.go
│ │ └── models_generated.go
│ └── resources
│ └── register_generated.go
└── README.md
cmd/server/product_handlers_generated.go):
internal/storage/storage_generated.go):
cmd/server/routes_generated.go):
pkg/client/client_generated.go):
cmd/server/openapi_generated.go):
/docsJust the Product struct definitions! That’s about 20 lines of code to get a complete REST API with documentation, validation, and client libraries.
Need users? Orders? Categories?
# Add a new resource type (automatically added to the hub version)
fabrica add resource Order
# Edit the generated apis/<group>/<version>/order_types.go
# Add your OrderSpec and OrderStatus fields
# Regenerate all code
fabrica generate
Each resource gets its own complete set of CRUD endpoints automatically.
Ready to add a new version for evolving your API?
# Step 1: Add a new API version to your project
# This creates the version directory and updates apis.yaml
fabrica add version v2
# Step 2: Add resources to the new version
# For alpha versions, resources are added without --force
fabrica add resource Device --version v2alpha1
# For stable versions, you need --force to acknowledge breaking changes
fabrica add resource Device --version v2 --force
# Step 3: Regenerate - handlers for all versions are created automatically
fabrica generate
Important: You must use fabrica add version <version> to create the version in apis.yaml before you can add resources to it. If you try to add a resource to a non-existent version, you’ll get an error:
Error: version v2 not found in apis.yaml (available: [v1])
Version naming conventions:
v1alpha1, v2alpha1 - Alpha versions (unstable, can change freely)v1beta1, v2beta1 - Beta versions (more stable, breaking changes discouraged)v1, v2 - Stable versions (require --force flag for changes)Fabrica uses hub/spoke versioning where all requests are converted to the hub (storage) version internally, allowing you to evolve your API gracefully.
Want to validate input? Add struct tags:
type ProductSpec struct {
Name string `json:"name" validate:"required,min=3,max=100"`
Description string `json:"description" validate:"max=500"`
Price float64 `json:"price" validate:"required,gt=0"`
InStock bool `json:"inStock"`
}
Validation happens automatically - invalid requests return 400 errors with detailed messages!
Visit these URLs while your server is running:
When you’re ready to explore more of Fabrica’s capabilities:
README.md in your projectfabrica --help or fabrica <command> --helpdocs/ in the Fabrica repositoryexamples/ for working code samplesIn 30 minutes, you’ve:
✅ Installed Fabrica CLI ✅ Created a new project with simple mode ✅ Defined a data structure (7 lines of code) ✅ Generated a complete REST API ✅ Ran and tested your API ✅ Learned how to add more resources
You now have a working REST API!
When you’re ready to go deeper and unlock Fabrica’s full power (labels, conditions, events, reconciliation), continue to the Resource Management Tutorial.
Happy coding! 🚀