Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ The typical use case is as a RAG component: Create embeddings for your text coll

## Quick Start

### 0. Discover the API

```bash
# Get the service manifest with all available endpoints
curl http://localhost:8880/
# or
curl http://localhost:8880/v1

# Response includes:
# - Service name, version, and description (customizable via env variables)
# - Available API versions
# - Authentication schemes
# - Complete list of endpoints with descriptions
```

**Customize the manifest**: Set environment variables in your `.env` file:
```bash
SERVICE_API_NAME="My Custom API"
SERVICE_API_DESCRIPTION="My custom description"
SERVICE_API_DOC_URL="https://my-docs.example.com"
```

### 1. Start with Docker

```bash
Expand All @@ -46,6 +68,11 @@ The typical use case is as a RAG component: Create embeddings for your text coll
# Start services (includes PostgreSQL with pgvector)
docker-compose up -d

# Discover available API endpoints (at root or versioned endpoint)
curl http://localhost:8880/
# or
curl http://localhost:8880/v1

# Access the API documentation
curl http://localhost:8880/docs
```
Expand Down
41 changes: 35 additions & 6 deletions internal/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"

"github.com/jackc/pgx/v5/pgxpool"
"github.com/mpilhlt/embapi/internal/models"

huma "github.com/danielgtaylor/huma/v2"
)
Expand All @@ -17,14 +18,16 @@ type contextKey string

// Context keys
const (
PoolKey = contextKey("dbPool")
KeyGenKey = contextKey("keyGen")
PoolKey = contextKey("dbPool")
KeyGenKey = contextKey("keyGen")
OptionsKey = contextKey("options")
)

// Error responses
var (
ErrPoolNotFound = errors.New("database connection pool not found in context")
ErrKeyGenNotFound = errors.New("key generator not found in context")
ErrPoolNotFound = errors.New("database connection pool not found in context")
ErrKeyGenNotFound = errors.New("key generator not found in context")
ErrOptionsNotFound = errors.New("options not found in context")
)

// The type definitions and functions that follow are used to
Expand All @@ -45,8 +48,13 @@ func (s StandardKeyGen) RandomKey(len int) (string, error) {
}

// AddRoutes adds all the routes to the API
func AddRoutes(pool *pgxpool.Pool, keyGen RandomKeyGenerator, api huma.API) error {
err := RegisterUsersRoutes(pool, keyGen, api)
func AddRoutes(pool *pgxpool.Pool, keyGen RandomKeyGenerator, api huma.API, options *models.Options) error {
err := RegisterManifestRoutes(pool, api, options)
if err != nil {
fmt.Printf(" Unable to register Manifest routes: %v\n", err)
return err
}
err = RegisterUsersRoutes(pool, keyGen, api)
if err != nil {
fmt.Printf(" Unable to register Users routes: %v\n", err)
return err
Expand Down Expand Up @@ -111,6 +119,17 @@ func addKeyGenToContext[I any, O any](keyGen RandomKeyGenerator, next func(conte
}
}

// Middleware to add the options to the context
func addOptionsToContext[I any, O any](options *models.Options, next func(context.Context, *I) (*O, error)) func(context.Context, *I) (*O, error) {
return func(ctx context.Context, input *I) (*O, error) {
if options == nil {
return nil, fmt.Errorf("provided options is nil")
}
ctx = context.WithValue(ctx, OptionsKey, options)
return next(ctx, input)
}
}

// Get the database connection pool from the context
// (exported helper function so that blackbox testing can access it)
func GetDBPool(ctx context.Context) (*pgxpool.Pool, error) {
Expand All @@ -130,3 +149,13 @@ func GetKeyGen(ctx context.Context) (RandomKeyGenerator, error) {
}
return keyGen, nil
}

// Get the options from the context
// (exported helper function so that blackbox testing can access it)
func GetOptions(ctx context.Context) (*models.Options, error) {
options, ok := ctx.Value(OptionsKey).(*models.Options)
if !ok {
return nil, huma.NewError(http.StatusInternalServerError, ErrOptionsNotFound.Error())
}
return options, nil
}
4 changes: 2 additions & 2 deletions internal/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func startTestServer(t *testing.T, pool *pgxpool.Pool, keyGen handlers.RandomKey
*/

// Create a new router & API
config := huma.DefaultConfig("DHaMPS Vector Database API", "0.0.1")
config := huma.DefaultConfig(models.APIName, models.APIVersion)
config.Components.SecuritySchemes = auth.Config
router := http.NewServeMux()
api := humago.New(router, config)
Expand All @@ -191,7 +191,7 @@ func startTestServer(t *testing.T, pool *pgxpool.Pool, keyGen handlers.RandomKey
api.UseMiddleware(auth.EmbAPIKeyReaderAuth(api, pool, &options))
api.UseMiddleware(auth.AuthTermination(api, &options))

err := handlers.AddRoutes(pool, keyGen, api)
err := handlers.AddRoutes(pool, keyGen, api, &options)
if err != nil {
fmt.Printf("Unable to add routes to API: %v", err)
return err, func() {}
Expand Down
Loading