How to create a REST API in seconds

Quick Start (30 Seconds to Running API)

# Clone and setup
git clone https://github.com/nicolasbonnici/gorest.git
cd examples/basic-api
cp .env.dist .env
# Start database and generate API
docker compose up -d 
make generate
make run

Your API is now running at http://localhost:3000 with interactive documentation at http://localhost:3000/openapi.

Start with Your Data Model

Define your business objects and their relationships using standard SQL:

-- schema.sql
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    firstname TEXT NOT NULL,
    lastname TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    password TEXT,
    created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE todo (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id),
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Two tables with a foreign key relationship. That’s our entire starting point in this post.

What Gets Generated Automatically

Complete REST API Endpoints

For Users:

  • POST /users – Create user
  • GET /users – List users (with pagination, filtering, ordering)
  • GET /users/{id} – Get user
  • PUT /users/{id} – Update user
  • DELETE /users/{id} – Delete user

For Todos:

  • POST /todos – Create todo
  • GET /todos – List todos
  • GET /todos/{id} – Get todo
  • PUT /todos/{id} – Update todo
  • DELETE /todos/{id} – Delete todo

Code Structure

gorest/
├── cmd/
│   └── server/
│       └── main.go      # Application entry point
├── internal/
│   ├── models/          # Database models
│   │   ├── user.go
│   │   └── todo.go
│   ├── dto/             # Data Transfer Objects
│   │   ├── user_dto.go
│   │   └── todo_dto.go
│   ├── hooks/           # Your business logic goes here
│   │   ├── user.go
│   │   └── todo.go
│   └── resources/       # REST resource configurations
│       ├── user.go
│       └── todo.go
└── openapi.json         # API documentation

Key point: Generated files (models, DTOs, resources) are recreated when you run generate. Your custom business logic goes in hooks/, which are never overwritten.

Interactive API Documentation

Access at http://localhost:3000/openapi:

  • Browse all endpoints with request/response schemas
  • Test API calls directly from browser
  • Copy code examples in multiple languages
  • JSON spec available at /openapi.json for Postman, Insomnia, or client SDK generation

Authentication & Security

Built-in JWT Authentication

Registration and login endpoints are automatically generated:

# Register a new user
curl -X POST http://localhost:3000/register 
  -H "Content-Type: application/json" 
  -d '{
    "firstname": "John",
    "lastname": "Doe",
    "email": "john@example.com",
    "password": "secure123"
  }'

# Login and get JWT token
curl -X POST http://localhost:3000/login 
  -H "Content-Type: application/json" 
  -d '{
    "email": "john@example.com",
    "password": "secure123"
  }'

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "abc-123",
    "firstname": "John",
    "lastname": "Doe",
    "email": "john@example.com"
  }
}

All protected endpoints automatically require the JWT token:

curl -H "Authorization: Bearer YOUR_TOKEN" 
     http://localhost:3000/todos

Securing Sensitive Data with DTOs

Control exactly what data is exposed through Data Transfer Objects:

// Database model - contains ALL fields
type Todo struct {
    gorest.Model
    ID          string    `json:"id" gorm:"primaryKey"`
    Title       string    `json:"title"`
    Content     string    `json:"content"`
    Password    string    `json:"-"` // Never serialized
    UserID      string    `json:"user_id"`
    CreatedAt   time.Time `json:"created_at"`
}

// What clients see when reading
type TodoReadDTO struct {
    ID        string    `json:"id"`
    Title     string    `json:"title"`
    Content   string    `json:"content"`
    UserID    string    `json:"user_id"`
    CreatedAt time.Time `json:"created_at"`
    // Password is completely absent - type-safe
}

// What clients can send when creating
type TodoWriteDTO struct {
    Title    string `json:"title" validate:"required,min=3"`
    Content  string `json:"content"`
    Password string `json:"password" validate:"required,min=8"`
    // UserID is absent - auto-populated server-side
}

Benefits:

  • Type-safe: Compiler prevents exposing sensitive fields
  • Flexible: Different DTOs per operation (list, detail, create, update)
  • Self-documenting: API contract is explicit
  • Zero overhead: No runtime transformation

Advanced Querying Out of the Box

Filtering

GET /todos?title[like]=meeting              # Pattern matching
GET /todos?priority[gte]=5                  # Greater than or equal
GET /todos?status[]=active&status[]=pending # Multiple values (OR)

Ordering

GET /todos?order[priority]=desc&order[created_at]=asc

Pagination

GET /todos?page=2&limit=20

Relationship Expansion

# Automatically expand foreign keys
GET /todos?expand=user

# Response includes nested user data
{
  "id": "todo-123",
  "title": "Buy groceries",
  "user": {
    "id": "user-456",
    "firstname": "John",
    "email": "john@example.com"
  }
}

Adding Business Logic With Hooks

Hooks execute at key moments in the request lifecycle without modifying generated code.

Available Hooks

  • StateProcessor – Validate/transform data before Create/Update/Delete
  • SQLQueryOverride – Replace default queries with custom SQL
  • SQLQueryListener – Intercept queries before/after execution
  • Authorize – Add authentication/authorization logic

Example 1: Validation and Auto-Population

func (h *TodoHooks) StateProcessor(ctx context.Context, operation hooks.Operation, id any, todo *models.Todo) error {
    switch operation {
    case hooks.OperationCreate:
        // Validate
        if todo.Title == "" {
            return fmt.Errorf("title is required")
        }
        if len(todo.Title) < 3 {
            return fmt.Errorf("title must be at least 3 characters")
        }

        // Auto-populate user_id from JWT authentication
        // This happens server-side - clients cannot spoof it
        if userID := ctx.Value("user_id"); userID != nil {
            todo.UserID = userID.(string)
        }
    }
    return nil
}

Result: Users can’t assign todos to each other, and data is validated before reaching the database.

Example 2: Custom Query Logic

func (h *TodoHooks) SQLQueryOverride(ctx context.Context, operation hooks.Operation, id any) (*sqlbuilder.SelectQuery, error) {
    if operation == hooks.OperationList {
        // Only show the authenticated user's incomplete todos
        userID := ctx.Value("user_id")

        query := sqlbuilder.
            Select("id", "title", "content", "user_id").
            From("todo").
            Where("completed = FALSE AND user_id = ?", userID)

        return query, nil
    }
    return nil, nil
}

Result: Every GET /todos automatically filters to the authenticated user’s incomplete tasks.

Example 3: Authorization Control

func (h *TodoHooks) Authorize(ctx context.Context, operation hooks.Operation, id any) error {
    userID := ctx.Value("user_id")
    if userID == nil {
        return fmt.Errorf("authentication required")
    }

    // Custom permission checks
    if operation == hooks.OperationDelete {
        // Only admins can delete
        if !isAdmin(userID) {
            return fmt.Errorf("insufficient permissions")
        }
    }

    return nil
}

Why Go and GoREST?

Language and Framework Choice

To build a REST API, you need both a language and framework. We chose Go with GoREST based on these technical advantages:

Performance Benchmarks

  • Response times: Sub-millisecond under normal load
  • Concurrent requests: Handle thousands simultaneously via goroutines
  • Startup time: ~1ms (vs. 1-2 seconds for interpreted languages)
  • Compilation: Direct to machine code with no runtime overhead

Development Experience

  • Clean syntax and strong typing reduce bugs
  • Built-in testing frameworks and profiling tools
  • Excellent standard library for HTTP, JSON, and database operations
  • Simple concurrency with goroutines and channels
  • Code remains maintainable months later

Production Benefits

  • Single binary deployment (detailed in next section)
  • Cross-platform compilation built-in
  • No dependency version conflicts
  • Trivial CI/CD pipelines

GoREST Framework Goals

GoREST focuses on eliminating boilerplate while maintaining flexibility:

  1. Code Generation – Define your schema once, get a complete API
  2. Consistency – Predictable routes, uniform behavior, automatic validation
  3. Extensibility – Add business logic via hooks without modifying generated code
  4. Performance – Leverage Go’s speed and efficiency
  5. Database Flexibility – Works with PostgreSQL, MySQL, SQLite

Deployment: The Single Binary Advantage

One of Go’s most powerful features is that your entire application compiles into a single, self-contained executable. This fundamentally changes how you deploy and manage applications.

Production Deployment Scenarios

GoREST deployment:

# Build once
make build

# Deploy
scp ./bin/gorest user@server:/opt/gorest/
./gorest

# That's it. No dependencies. No runtime.

1. Bare Metal / VM with systemd

[Unit]
Description=GoREST API Server
After=network.target postgresql.service

[Service]
Type=simple
User=api
WorkingDirectory=/opt/gorest
EnvironmentFile=/opt/gorest/.env
ExecStart=/opt/gorest/bin/gorest
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
sudo systemctl enable gorest
sudo systemctl start gorest

2. Docker (Multi-stage Build)

# Build stage
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN make build

# Final stage - just the binary
FROM scratch
COPY --from=builder /app/bin/gorest /gorest
EXPOSE 3000
CMD ["https://dev.to/gorest"]

Result: 10-20MB Docker image (vs. 500MB+ for Node.js/Python apps)

3. Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gorest-api
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: gorest
        image: your-registry/gorest:latest
        ports:
        - containerPort: 3000
        resources:
          requests:
            memory: "20Mi"
            cpu: "100m"
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: url

Each pod starts in milliseconds and uses minimal resources.

4. AWS Lambda (Custom Runtime)

# Package as custom runtime
zip function.zip gorest
aws lambda create-function --runtime provided.al2

# Cold start: ~100ms (vs. seconds for interpreted languages)

5. Edge & Serverless Platforms

Deploy to Cloudflare Workers, Fly.io, or any edge platform. Small binary size and instant startup are ideal for serverless environments.

Cross-Platform Compilation

Build for any platform from any platform:

# Build for Linux from your Mac
GOOS=linux GOARCH=amd64 make build

# Build for Windows
GOOS=windows GOARCH=amd64 make build

# Build for ARM (Raspberry Pi, M1 Mac)
GOOS=linux GOARCH=arm64 make build

CI/CD Pipeline Example

# GitHub Actions
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Build
        run: make build

      - name: Test
        run: ./bin/gorest --version

      - name: Deploy
        run: scp ./bin/gorest server:/app/

No dependency caching, no runtime installation, no complex build pipelines.

Version Management

# No runtime version conflicts
# Each binary is independent and portable
/opt/api-v1/gorest  # Old version
/opt/api-v2/gorest  # New version

# Instant rollback
ln -sf /opt/api-v1/gorest /usr/local/bin/gorest

# Instant upgrade
ln -sf /opt/api-v2/gorest /usr/local/bin/gorest

Why This Matters

  • Zero Dependencies: No runtime installation, no package managers, no dependency hell
  • Consistent Behavior: Same binary runs identically across dev, staging, and production
  • Reduced Attack Surface: No runtime vulnerabilities, minimal OS dependencies
  • Cost Efficiency: Smaller instances, cheaper hosting, more instances per server
  • Faster Deployment: Copy one file, run it
  • Simplified Operations: No dependency updates, no runtime patches

Complete Example: From Schema to Production

Let’s walk through a complete workflow:

Step 1: Define Schema

CREATE TABLE todo (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    user_id UUID REFERENCES users(id),
    created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Step 2: Generate and Build

make generate  # Generates models, DTOs, resources, OpenAPI docs
make build     # Compiles to single binary in ./bin/gorest

Step 3: Deploy

# Copy to server
scp ./bin/gorest user@server:/opt/api/

# Run
ssh user@server
cd /opt/api
./gorest

Step 4: Use Your API

# Register and login (see Authentication section for details)
TOKEN="your-jwt-token-here"

# Create todo
curl -X POST http://your-server:3000/todos 
  -H "Authorization: Bearer $TOKEN" 
  -H "Content-Type: application/json" 
  -d '{"title":"Buy groceries","content":"Milk, eggs, bread"}'

# List todos with filtering and ordering
curl -H "Authorization: Bearer $TOKEN" 
     "http://your-server:3000/todos?title[like]=groceries&order[created_at]=desc"

# Get specific todo
curl -H "Authorization: Bearer $TOKEN" 
     "http://your-server:3000/todos/{id}"

# Update todo
curl -X PUT http://your-server:3000/todos/{id} 
  -H "Authorization: Bearer $TOKEN" 
  -H "Content-Type: application/json" 
  -d '{"title":"Buy groceries ASAP"}'

# Delete todo
curl -X DELETE http://your-server:3000/todos/{id} 
  -H "Authorization: Bearer $TOKEN"

View interactive documentation at http://your-server:3000/openapi.

Getting Started

Ready to try GoREST?

View the basic-api example

Explore the full source code:

GitHub Repository

Found this helpful? Please star the project to show your support!

Summary

GoREST transforms your database schema into a production-ready REST API compiled as a single executable binary.

What you get:

  • Complete CRUD operations for all tables
  • JWT authentication with register/login endpoints
  • Advanced filtering, ordering, pagination, and relationship expansion
  • Interactive OpenAPI documentation
  • Type-safe DTOs for security and flexibility
  • Extensible hooks system for business logic
  • Production-ready error handling and validation

Deployment benefits:

  • Single self-contained binary with no dependencies
  • Deploy anywhere: bare metal, Docker, Kubernetes, serverless, edge
  • 10-20MB total size with sub-millisecond response times
  • Instant startup and minimal resource footprint
  • Simple CI/CD and version management

Your database schema becomes a production-grade REST API. Define your data model, run make generate && make build, and ship anywhere.

Contribute to GoREST

All contributions welcome – code, documentation, ideas, suggestions:

Thanks for reading! 🚀

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Latest Emerging Tech News Transforming the Digital Landscape

Related Posts