rest

package module
v0.6.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 29, 2025 License: BSD-3-Clause Imports: 26 Imported by: 5

README

GoDoc Go Report Card

KarpelesLab/rest

A comprehensive Go client for interacting with RESTful API services. This package simplifies making HTTP requests to REST endpoints, handling authentication, token renewal, and response parsing.

Features

  • Simple API for RESTful requests with JSON encoding/decoding
  • Support for context-based configuration and cancellation
  • Multiple authentication methods:
    • OAuth2 token management with automatic renewal
    • API key authentication with secure request signing
  • Robust error handling with unwrapping to standard errors
  • Generic response parsing with type safety (Go 1.18+)
  • Large file uploads with multi-part and AWS S3 support
  • Automatic retry on token expiration

Installation

go get github.com/KarpelesLab/rest

Basic Usage

package main

import (
    "context"
    "fmt"
    "log"
    
    "github.com/KarpelesLab/rest"
)

func main() {
    ctx := context.Background()
    
    // Simple GET request with response as map
    var result map[string]interface{}
    err := rest.Apply(ctx, "Endpoint/Path", "GET", nil, &result)
    if err != nil {
        log.Fatalf("API request failed: %s", err)
    }
    fmt.Printf("Result: %+v\n", result)
    
    // Using the generic As method (Go 1.18+)
    type User struct {
        ID   string `json:"id"`
        Name string `json:"name"`
    }
    
    user, err := rest.As[User](ctx, "Users/Get", "GET", rest.Param{"userId": "123"})
    if err != nil {
        log.Fatalf("Failed to get user: %s", err)
    }
    fmt.Printf("User: %s (%s)\n", user.Name, user.ID)
}

Parameter Order

This library was built based on the original JSON library, where HTTP method was an optional argument. As such it was placed after the path, and this pattern was kept in the Go version.

While methods like http.NewRequest take the method followed by the URL, remembering that the method is sometimes optional in API implementations is a good way to remember the argument order in this library:

rest.Apply(ctx, "Path/Endpoint", "GET", params, &result)
//                  ^path        ^method  ^params ^result

File Uploads

The package provides robust file upload capabilities:

// Upload a file
file, _ := os.Open("largefile.dat")
defer file.Close()

res, err := rest.Upload(ctx, "Files/Upload", "POST", 
    rest.Param{"filename": "myfile.dat"}, file, "application/octet-stream")
if err != nil {
    log.Fatalf("Upload failed: %s", err)
}

Command-line Upload Tool

restupload is a command-line tool for uploading large files to specific APIs.

Installation:

go install github.com/KarpelesLab/rest/cli/restupload@latest

Authentication

OAuth2 Token Authentication
// Create a token
token := &rest.Token{
    AccessToken: "your-access-token",
    RefreshToken: "your-refresh-token",
    ClientID: "your-client-id",
    Expires: 3600,
}

// Use the token in the context
ctx := token.Use(context.Background())

// Make authenticated requests
result, err := rest.Do(ctx, "Protected/Resource", "GET", nil)
API Key Authentication
// Create an API key with the key ID and secret
apiKey, err := rest.NewApiKey("key-12345", "your-secret")
if err != nil {
    log.Fatalf("Failed to create API key: %v", err)
}

// Use the API key in the context
ctx := apiKey.Use(context.Background())

// Make authenticated requests
// The request will be automatically signed using the API key
result, err := rest.Do(ctx, "Protected/Resource", "GET", nil)

#### Complete API Key Authentication Example

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    
    "github.com/KarpelesLab/rest"
)

func main() {
    // Read API key ID and secret from environment
    keyID := os.Getenv("API_KEY_ID")
    keySecret := os.Getenv("API_KEY_SECRET")
    if keyID == "" || keySecret == "" {
        log.Fatal("API_KEY_ID and API_KEY_SECRET environment variables are required")
    }
    
    // Create an API key instance with the key ID and base64-encoded secret
    apiKey, err := rest.NewApiKey(keyID, keySecret)
    if err != nil {
        log.Fatalf("Failed to create API key: %v", err)
    }
    
    // Create a context with the API key
    ctx := context.Background()
    ctx = apiKey.Use(ctx)
    
    // Define the request parameters
    params := map[string]interface{}{
        "limit": 10,
        "filter": "active",
    }
    
    // Make the API call - the request will be automatically signed
    var users []map[string]interface{}
    err = rest.Apply(ctx, "User:list", "GET", params, &users)
    if err != nil {
        log.Fatalf("API request failed: %v", err)
    }
    
    // Process the response
    fmt.Printf("Found %d users\n", len(users))
    for i, user := range users {
        fmt.Printf("User %d: %s (%s)\n", i+1, user["name"], user["email"])
    }
}

The authentication process happens automatically:

  1. The library adds the API key ID as _key parameter
  2. It adds the current timestamp as _time parameter
  3. It generates a unique nonce as _nonce parameter
  4. It builds a signature string from the method, path, query parameters, and request body
  5. It signs this string with the API key's secret using Ed25519
  6. It adds the signature as _sign parameter to complete the authentication

Error Handling

Errors from REST APIs are automatically parsed and can be handled with the standard Go errors package:

_, err := rest.Do(ctx, "Protected/Resource", "GET", nil)
if err != nil {
    if errors.Is(err, os.ErrPermission) {
        // Handle permission denied (403)
    } else if errors.Is(err, fs.ErrNotExist) {
        // Handle not found (404)
    } else {
        // Handle other errors
    }
}

API Discovery

To discover available endpoints and their parameters, you can use the @karpeleslab/klbfw-describe tool:

npx @karpeleslab/klbfw-describe SomeEndpoint/Path

This will display detailed information about the endpoint, including:

  • Available methods
  • Required and optional parameters
  • Return types
  • Access requirements

This is especially useful when exploring new APIs or understanding existing endpoints.

Testing

The package includes a comprehensive test suite that can be run using:

go test -v github.com/KarpelesLab/rest

License

This package is distributed under the terms of the license found in the LICENSE file.

Documentation

Overview

Package rest provides a client for interacting with RESTful API services.

Package rest provides a client for interacting with RESTful API services.

Package rest provides a client for interacting with RESTful API services. It simplifies making HTTP requests to REST endpoints, handling authentication, token renewal, and response parsing.

Example

Example provides a documented example of how API key authentication works. This is not a real test but serves as documentation for users.

// API key authentication is automatically handled by the library
// You obtain your API key (key ID and secret) from the service provider
//
// 1. Load your API key into your application
// 2. Create a context with your API key
// ctx := context.Background()
// ctx = apiKey.Use(ctx)
//
// 3. Use the context with any API call
// result, err := Do(ctx, "User:get", "GET", nil)
//
// The library automatically:
// - Adds the key ID to the request parameters
// - Adds a timestamp and nonce for security
// - Calculates a cryptographic signature
// - Ensures the signature is the last parameter in the URL
//
// All of this happens behind the scenes when you use the API key context
// with rest.Do(), rest.Apply(), or rest.As() functions.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// Debug enables verbose logging of REST API requests and responses
	Debug = false
	// Scheme defines the URL scheme for API requests (http or https)
	Scheme = "https"
	// Host defines the default hostname for API requests
	Host = "www.atonline.com"
)
View Source
var (
	// ErrNoClientID is returned when token renewal is attempted without a client ID
	ErrNoClientID = errors.New("no client_id has been provided for token renewal")
	// ErrNoRefreshToken is returned when token renewal is attempted without a refresh token
	ErrNoRefreshToken = errors.New("no refresh token is available and access token has expired")
)
View Source
var ErrLoginRequired = errors.New("login required")

ErrLoginRequired is returned when an API endpoint requires authentication but no valid token was provided.

View Source
var ErrUploadStalled = errors.New("upload stalled: transferred less than 150KB in 30 seconds")
View Source
var RestHttpClient = &http.Client{
	Transport: RestHttpTransport,
	Timeout:   300 * time.Second,
}

RestHttpClient is the default HTTP client used for all REST API requests. It uses RestHttpTransport and has a 5-minute overall timeout.

View Source
var RestHttpTransport = &http.Transport{
	Proxy:                 http.ProxyFromEnvironment,
	MaxIdleConns:          100,
	MaxIdleConnsPerHost:   50,
	MaxConnsPerHost:       200,
	IdleConnTimeout:       90 * time.Second,
	ResponseHeaderTimeout: 90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 5 * time.Second,
}

RestHttpTransport is the configured HTTP transport used for all REST API requests. It's optimized with connection pooling and appropriate timeouts.

View Source
var (
	SystemProxy = &httputil.ReverseProxy{
		Director:  systemProxyDirector,
		Transport: RestHttpClient.Transport,
	}
)
View Source
var UploadHttpClient = &http.Client{
	Transport: RestHttpTransport,
	Timeout:   time.Hour,
}

UploadHttpClient is the HTTP client used for upload requests, which might need a longer timeout due to the actual upload.

Functions

func Apply

func Apply(ctx context.Context, path, method string, param any, target any) error

Apply makes a REST API request and unmarshals the response data into the target object. It handles authentication, error parsing, and JSON unmarshaling.

Parameters:

- ctx: Context for the request, may contain authentication tokens - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content - target: Destination object for unmarshaled response data

Returns: an error if the request fails or response cannot be unmarshaled.

func As added in v0.5.26

func As[T any](ctx context.Context, path, method string, param any) (T, error)

As makes a REST API request and returns the response data unmarshaled into the specified type T. This is a generic version of Apply that returns the target object directly.

Parameters:

- ctx: Context for the request, may contain authentication tokens - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content

Returns: the unmarshaled object of type T and any error encountered.

func ResponseAs added in v0.5.26

func ResponseAs[T any](r *Response) (T, error)

ResponseAs is a generic helper that unmarshals a response into type T.

Parameters:

- r: The Response object containing data to unmarshal

Returns: the unmarshaled object of type T and any error encountered

func SpotApply added in v0.5.23

func SpotApply(ctx context.Context, client SpotClient, path, method string, param any, target any) error

SpotApply makes a REST API request through a SpotClient and unmarshals the response into target. This is similar to Apply but uses a SpotClient for the request.

Parameters:

- ctx: Context for the request - client: SpotClient to use for the API request - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content - target: Destination object for unmarshaled response data

Returns: an error if the request fails or response cannot be unmarshaled.

func SpotAs added in v0.5.26

func SpotAs[T any](ctx context.Context, client SpotClient, path, method string, param any) (T, error)

SpotAs makes a REST API request through a SpotClient and returns the response data unmarshaled into type T. This is a generic version of SpotApply that returns the target object directly.

Parameters: - ctx: Context for the request - client: SpotClient to use for the API request - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content

Returns: the unmarshaled object of type T and any error encountered.

Types

type ApiKey added in v0.5.32

type ApiKey struct {
	KeyID     string
	SecretKey []byte
}

ApiKey represents an API key with its secret for signing requests. It contains the key ID and secret key used for request signing.

func NewApiKey added in v0.5.32

func NewApiKey(keyID, secret string) (*ApiKey, error)

NewApiKey creates a new ApiKey instance from separate key ID and secret parameters. This is the recommended way to create an ApiKey instance.

Parameters: - keyID: The API key identifier - secret: The secret key as a base64-encoded string

Returns: - An ApiKey instance and nil if successful - nil and an error if the secret cannot be properly decoded or is invalid

The secret is a base64url-encoded (using - and _ characters) Ed25519 private key provided by the server.

func (*ApiKey) Use added in v0.5.32

func (a *ApiKey) Use(ctx context.Context) context.Context

Use returns a new context that includes this API key for authentication. The API key will be used for all REST API calls that use this context.

type ContextRequest added in v0.5.6

type ContextRequest int
const (
	BackendURL     ContextRequest = 1
	SkipDebugLog   ContextRequest = 2
	UploadProgress ContextRequest = 3
)

type Error added in v0.2.2

type Error struct {
	Response *Response
	// contains filtered or unexported fields
}

Error represents an error returned by a REST API endpoint. It wraps the Response object and provides standard error interfaces.

func (*Error) Error added in v0.2.2

func (r *Error) Error() string

Error returns a string representation of the REST API error.

func (*Error) Unwrap added in v0.4.5

func (r *Error) Unwrap() error

Unwrap implements the errors.Unwrapper interface to allow error checking with errors.Is. It maps REST API errors to standard Go errors where applicable (e.g., 403 to os.ErrPermission).

type HttpError added in v0.5.15

type HttpError struct {
	Code int
	Body []byte
	// contains filtered or unexported fields
}

HttpError represents an HTTP transport error that occurred during a REST API request. It captures HTTP status codes and response bodies for debugging.

func (*HttpError) Error added in v0.5.15

func (e *HttpError) Error() string

Error returns a string representation of the HTTP error.

func (*HttpError) Unwrap added in v0.5.15

func (e *HttpError) Unwrap() error

Unwrap implements the errors.Unwrapper interface to allow error checking with errors.Is. It returns the underlying error, if any.

type Param added in v0.2.2

type Param map[string]any

Param is a convenience type for parameters passed to REST API requests.

type Response added in v0.2.2

type Response struct {
	Result string           `json:"result"` // "success" or "error" (or "redirect")
	Data   pjson.RawMessage `json:"data,omitempty"`
	Error  string           `json:"error,omitempty"`
	Code   int              `json:"code,omitempty"` // for errors
	Extra  string           `json:"extra,omitempty"`
	Token  string           `json:"token,omitempty"`

	Paging any `json:"paging,omitempty"`
	Job    any `json:"job,omitempty"`
	Time   any `json:"time,omitempty"`
	Access any `json:"access,omitempty"`

	Exception    string `json:"exception,omitempty"`
	RedirectUrl  string `json:"redirect_url,omitempty"`
	RedirectCode int    `json:"redirect_code,omitempty"`

	RequestID string `json:"-"` // X-Request-Id header from HTTP response
	// contains filtered or unexported fields
}

Response represents a REST API response with standard fields. It handles different result types and provides methods to access response data.

func Do

func Do(ctx context.Context, path, method string, param any) (*Response, error)

Do executes a REST API request and returns the raw Response object. It handles token authentication, token renewal, parameter encoding, and error parsing.

Parameters:

- ctx: Context for the request, may contain authentication tokens - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content

Returns: the raw Response object and any error encountered during the request.

func SpotDo added in v0.5.23

func SpotDo(ctx context.Context, client SpotClient, path, method string, param any) (*Response, error)

SpotDo executes a REST API request through a SpotClient and returns the raw Response object. This is the base function used by SpotApply and SpotAs.

Parameters:

- ctx: Context for the request - client: SpotClient to use for the API request - path: API endpoint path - method: HTTP method (GET, POST, PUT, etc.) - param: Request parameters or body content

Returns: the raw Response object and any error encountered during the request.

func SpotUpload added in v0.5.24

func SpotUpload(ctx context.Context, client SpotClient, req, method string, param Param, f io.Reader, mimeType string) (*Response, error)

SpotUpload uploads a file using a SpotClient. This is similar to Upload but uses a specific SpotClient for the request.

Parameters:

- ctx: Context for the request - client: SpotClient to use for the API request - req: API endpoint path - method: HTTP method for the initial API request - param: Parameters for the initial API request - f: Reader for the file content to upload - mimeType: MIME type of the file content

Returns: the API response after upload completion or an error.

func Upload added in v0.4.0

func Upload(ctx context.Context, req, method string, param Param, f io.Reader, mimeType string) (*Response, error)

Upload uploads a file to a REST API endpoint. It automatically selects the best upload method based on file size and server capabilities (direct PUT, multi-part, or AWS S3).

Parameters:

- ctx: Context for the request - req: API endpoint path - method: HTTP method for the initial API request - param: Parameters for the initial API request - f: Reader for the file content to upload - mimeType: MIME type of the file content

Returns: the API response after upload completion or an error.

func (*Response) Apply added in v0.4.2

func (r *Response) Apply(v any) error

Apply unmarshals the response data into the provided value.

Parameters:

- v: The target object to unmarshal into

Returns: an error if unmarshaling fails

func (*Response) ApplyContext added in v0.5.3

func (r *Response) ApplyContext(ctx context.Context, v any) error

ApplyContext unmarshals the response data into the provided value using a context.

Parameters:

- ctx: Context for unmarshaling - v: The target object to unmarshal into

Returns: an error if unmarshaling fails

func (*Response) FullRaw added in v0.5.10

func (r *Response) FullRaw() (map[string]any, error)

FullRaw returns the complete response as a map, including both the data payload and all metadata fields (result, error, code, etc.).

func (*Response) Get added in v0.4.4

func (r *Response) Get(v string) (any, error)

Get retrieves a value from the response data by a slash-separated path. For example, "user/name" would access the "name" field inside the "user" object.

Parameters:

- v: Slash-separated path to the requested value

Returns: the value at the specified path and any error encountered

func (*Response) GetString added in v0.4.4

func (r *Response) GetString(v string) (string, error)

GetString retrieves a string value from the response data by a slash-separated path. This is a convenience method that calls Get() and converts the result to a string.

Parameters:

- v: Slash-separated path to the requested string value

Returns: the string value at the specified path and any error encountered

func (*Response) OffsetGet added in v0.5.11

func (r *Response) OffsetGet(ctx context.Context, key string) (any, error)

OffsetGet implements the typutil.Getter interface for Response objects. It allows accessing response fields by key, with special handling for metadata keys prefixed with '@' (e.g., @error, @code).

func (*Response) ParseData added in v0.5.12

func (r *Response) ParseData()

ParseData parses the JSON data in the response. This is called automatically by Value() and ValueContext() methods.

func (*Response) Raw added in v0.5.7

func (r *Response) Raw() (any, error)

Raw returns the parsed data from the response. It's implemented as r.Value() for compatibility with older code.

func (*Response) ReadValue added in v0.2.2

func (r *Response) ReadValue(ctx context.Context) (any, error)

ReadValue returns the parsed data from the response. It's an alias for Value() that satisfies interfaces requiring a context parameter.

func (*Response) Value added in v0.4.4

func (r *Response) Value() (any, error)

Value returns the parsed data from the response. It lazily parses the JSON data on first access and caches the result.

Returns: the parsed data and any error encountered during parsing

func (*Response) ValueContext added in v0.5.3

func (r *Response) ValueContext(ctx context.Context) (any, error)

ValueContext returns the parsed data from the response, similar to Value(). It's provided for interface compatibility with methods requiring a context.

Parameters:

- ctx: Context (not used internally but provided for interface compatibility)

Returns: the parsed data and any error encountered during parsing

type RouterType added in v0.1.3

type RouterType struct {
}
var Router *RouterType = &RouterType{}

func (*RouterType) ServeHTTP added in v0.1.3

func (h *RouterType) ServeHTTP(w http.ResponseWriter, req *http.Request)

type SenderInterface added in v0.5.1

type SenderInterface interface {
	Send(from string, to []string, msg io.WriterTo) error
}
var Sender SenderInterface = restSender{}

type SpotClient added in v0.5.23

type SpotClient interface {
	Query(ctx context.Context, target string, body []byte) ([]byte, error)
}

SpotClient is an interface fulfilled by spotlib.Client that provides the necessary functionality for making API requests through a Spot connection. Using this interface helps avoid dependency loops between packages.

type Time added in v0.1.4

type Time struct {
	time.Time
}

func (Time) MarshalContextJSON added in v0.5.3

func (u Time) MarshalContextJSON(ctx context.Context) ([]byte, error)

func (Time) MarshalJSON added in v0.3.0

func (u Time) MarshalJSON() ([]byte, error)

func (*Time) UnmarshalContextJSON added in v0.5.3

func (u *Time) UnmarshalContextJSON(ctx context.Context, data []byte) error

func (*Time) UnmarshalJSON added in v0.3.0

func (u *Time) UnmarshalJSON(data []byte) error

type Token added in v0.2.0

type Token struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	Type         string `json:"token_type"`
	ClientID     string
	Expires      int `json:"expires_in"`
}

Token represents an OAuth2 token with refresh capabilities. It contains both access and refresh tokens and methods to use them in requests.

func (*Token) Use added in v0.2.0

func (t *Token) Use(ctx context.Context) context.Context

Use returns a new context that includes this token for authentication. The token will be used for all REST API calls that use this context.

type TokenSender added in v0.5.25

type TokenSender struct {
	Token string
}

func (*TokenSender) Send added in v0.5.25

func (s *TokenSender) Send(from string, to []string, msg io.WriterTo) error

type UploadInfo added in v0.4.0

type UploadInfo struct {
	MaxPartSize     int64 // maximum size of a single part in MB, defaults to 1024 (1GB)
	ParallelUploads int   // number of parallel uploads to perform (defaults to 3)
	// contains filtered or unexported fields
}

UploadInfo represents configuration and state for file uploads. It supports different upload methods: direct PUT, multi-part uploads, and AWS S3 uploads for large files.

func PrepareUpload added in v0.4.0

func PrepareUpload(req map[string]any) (*UploadInfo, error)

PrepareUpload creates an UploadInfo from the server response to an upload request. It parses server-provided upload parameters and prepares for the actual upload.

Parameters:

- req: Map containing upload configuration from the server

Returns: an UploadInfo object or an error if preparation fails.

func (*UploadInfo) Do added in v0.4.0

func (u *UploadInfo) Do(ctx context.Context, f io.Reader, mimeType string, ln int64) (*Response, error)

Do performs the actual file upload using the appropriate method. It automatically chooses between direct PUT, multi-part, or AWS S3 uploads based on file size and server capabilities.

Parameters:

- ctx: Context for the upload request - f: Reader for the file content to upload - mimeType: MIME type of the file content - ln: Length of the file in bytes, or -1 if unknown

Returns: the API response after upload completion or an error.

func (*UploadInfo) String added in v0.4.0

func (u *UploadInfo) String() string

String returns the upload URL as a string representation of the UploadInfo.

type UploadProgressFunc added in v0.6.8

type UploadProgressFunc func(bytesUploaded int64)

UploadProgressFunc is a callback function for upload progress updates. It receives the number of bytes that have been successfully uploaded. The function is called after each part completes uploading.

type UploadWriteHandler added in v0.6.9

type UploadWriteHandler struct {
	// contains filtered or unexported fields
}

func SpotUploadWriter added in v0.6.9

func SpotUploadWriter(ctx context.Context, client SpotClient, req, method string, param Param, mimeType string) (*UploadWriteHandler, error)

func UploadWriter added in v0.6.9

func UploadWriter(ctx context.Context, req, method string, param Param, mimeType string) (*UploadWriteHandler, error)

func (*UploadWriteHandler) Close added in v0.6.9

func (up *UploadWriteHandler) Close() error

func (*UploadWriteHandler) CloseWithResponse added in v0.6.9

func (up *UploadWriteHandler) CloseWithResponse() (*Response, error)

func (*UploadWriteHandler) Write added in v0.6.9

func (up *UploadWriteHandler) Write(b []byte) (n int, err error)

Directories

Path Synopsis
cli
restupload command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL