gapp

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2026 License: MIT Imports: 19 Imported by: 0

README

gapp

Full-stack Go + TypeScript framework with protobuf RPC, code generation, and preloading.

Features

  • Type-safe RPCs — Define services in protobuf, get generated Go handlers and TypeScript clients
  • Code generation — Single gapp codegen command generates Go and TypeScript from .proto files
  • Preloading — Server-side data preloading with route-aware RPC batching
  • React hooksuseStore bindings that auto-update on RPC responses
  • Client-side routing — Type-safe router with parameter extraction
  • Vite plugin — Dev-mode preload injection via @gapp/client/vite

Quick Start

# Install the CLI
go install github.com/germtb/gapp/cli@latest

# Create a new project
gapp init myapp --framework react -y

# Start development
cd myapp
gapp run

Architecture

myapp/
├── proto/service.proto     # Service definitions
├── server/
│   ├── main.go             # Go server with RPC handlers
│   └── generated/          # Generated protobuf code
└── client/
    ├── src/
    │   ├── generated/      # Generated TypeScript types
    │   ├── routes/         # Route definitions with RPC declarations
    │   └── stores/         # Reactive stores
    └── vite.config.ts
  1. Define your service in proto/service.proto
  2. Run gapp codegen to generate Go structs and TypeScript types
  3. Implement RPC handlers in server/main.go
  4. Use generated types in your client code

Packages

Package Description
github.com/germtb/gapp Go server framework — dispatcher, preload engine, auth middleware
@gapp/client Client runtime — stores, RPC transport, router, preloading
@gapp/react React bindings — useStore hook

CLI Commands

Command Description
gapp init <name> Create a new project (react or vanilla)
gapp codegen Generate Go + TypeScript from protobuf
gapp run [path] Start server and client dev server
gapp build [path] Build for production

Examples

See the examples/ directory:

  • with-auth — Authentication with siauth, protected RPC handlers

License

MIT

Documentation

Index

Constants

View Source
const (
	CodeValidationError  = "VALIDATION_ERROR"
	CodeNotFound         = "NOT_FOUND"
	CodeAlreadyExists    = "ALREADY_EXISTS"
	CodeUnauthenticated  = "UNAUTHENTICATED"
	CodePermissionDenied = "PERMISSION_DENIED"
	CodeRateLimited      = "RATE_LIMITED"
	CodeInternal         = "INTERNAL"
)

Error codes for structured RPC error responses.

Variables

This section is empty.

Functions

func GetAuthToken

func GetAuthToken(r *http.Request) any

GetAuthToken retrieves the auth token from the request context. Returns nil if no token has been set.

func HasUnsubstitutedParam

func HasUnsubstitutedParam(params map[string]string) bool

HasUnsubstitutedParam checks if any parameter values still contain unresolved :param placeholders.

func ListenAndServe

func ListenAndServe(addr string, handler http.Handler) error

ListenAndServe starts an HTTP server and blocks until a SIGINT or SIGTERM signal is received, at which point it initiates a graceful shutdown with a 30-second timeout. Returns http.ErrServerClosed on clean shutdown.

func MatchPattern

func MatchPattern(pattern, path string) (map[string]string, bool)

MatchPattern matches a URL pattern against a path, extracting route parameters.

func SetAuthToken

func SetAuthToken(r *http.Request, token any) *http.Request

SetAuthToken returns a new request with the given token stored in its context.

func SplitPath

func SplitPath(path string) []string

SplitPath splits a URL path into segments, trimming leading/trailing slashes.

func SubstituteParams

func SubstituteParams(rpcParams map[string]string, routeParams map[string]string) map[string]string

SubstituteParams replaces :param placeholders in RPC params with actual route parameter values.

func ToProtoBytes

func ToProtoBytes(v any) string

ToProtoBytes marshals a proto message, gzip-compresses it, and base64-encodes the result.

Types

type Assets

type Assets struct {
	JS  string
	CSS string
}

Assets holds the resolved asset paths from Vite manifest

func LoadAssetsFromManifest

func LoadAssetsFromManifest(manifestPath string) Assets

LoadAssetsFromManifest reads the Vite manifest to get hashed asset filenames.

type CORSConfig

type CORSConfig struct {
	AllowedOrigins []string                 // specific origins, or ["*"] for all
	AllowOrigin    func(origin string) bool // dynamic check, takes precedence over AllowedOrigins
	AllowedHeaders []string                 // defaults to standard RPC headers if nil
}

CORSConfig controls Cross-Origin Resource Sharing behavior.

type Dispatcher

type Dispatcher struct {
	Unary     map[string]UnaryHandler
	Streaming map[string]StreamHandler
	// contains filtered or unexported fields
}

Dispatcher routes RPC calls to registered handlers.

func NewDispatcher

func NewDispatcher(opts ...DispatcherOption) *Dispatcher

NewDispatcher creates a new Dispatcher with the given options.

func (*Dispatcher) ServeHTTP

func (d *Dispatcher) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler for the RPC dispatcher.

func (*Dispatcher) Use

func (d *Dispatcher) Use(m Middleware)

Use adds a middleware to the dispatcher. Middlewares are applied in order: first added = outermost (runs first), last added = innermost (runs last, closest to handler).

type DispatcherOption

type DispatcherOption func(*Dispatcher)

DispatcherOption configures a Dispatcher.

func WithCORS

func WithCORS(config CORSConfig) DispatcherOption

WithCORS sets the CORS configuration for the dispatcher.

type MessageReader

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

MessageReader reads length-prefixed messages from a byte buffer. Each message is expected to be preceded by a 4-byte big-endian length prefix, matching the format used by StreamAdapter.Send and the client streaming transport.

func NewMessageReader

func NewMessageReader(data []byte) *MessageReader

NewMessageReader creates a MessageReader over the given data.

func (*MessageReader) Next

func (r *MessageReader) Next() ([]byte, error)

Next returns the next message from the buffer. Returns io.EOF when no more messages are available.

type Middleware

type Middleware func(next RpcHandler) RpcHandler

Middleware wraps an RpcHandler, allowing pre/post processing of RPC calls.

func AuthMiddleware

func AuthMiddleware(validate func(r *http.Request) any) Middleware

AuthMiddleware creates a middleware that validates requests using the provided function. If validate returns a non-nil token, it's stored in the request context via SetAuthToken. If validate returns nil, the request proceeds without a token (unauthenticated). This allows handlers to decide individually whether auth is required.

type PreloadEngine

type PreloadEngine struct {
	Routes      []RouteSpec
	PreloadFunc PreloadFunc
	// contains filtered or unexported fields
}

PreloadEngine handles route-based RPC preloading and HTML rendering.

func NewPreloadEngine

func NewPreloadEngine(config PreloadEngineConfig) *PreloadEngine

func (*PreloadEngine) HandlePreloadEndpoint

func (p *PreloadEngine) HandlePreloadEndpoint(w http.ResponseWriter, r *http.Request)

HandlePreloadEndpoint handles the /__preload?path=... endpoint used by the Vite plugin in dev mode.

func (*PreloadEngine) ServeHTML

func (p *PreloadEngine) ServeHTML(w http.ResponseWriter, r *http.Request)

ServeHTML serves the HTML page with preloaded data for the matched route.

type PreloadEngineConfig

type PreloadEngineConfig struct {
	Routes       []RouteSpec
	PreloadFunc  PreloadFunc
	ManifestPath string // path to .vite/manifest.json, defaults to "public/.vite/manifest.json"
	AppName      string // defaults to "App"
}

type PreloadFunc

type PreloadFunc func(ctx context.Context, r *http.Request, method string, params map[string]string) (request, response proto.Message, err error)

PreloadFunc is the callback that executes an RPC for preloading. It receives the context, method name, and substituted route params. It returns the request and response proto messages.

type PreloadedRpc

type PreloadedRpc struct {
	RequestBytes  string `json:"requestBytes"`
	ResponseBytes string `json:"responseBytes"`
}

PreloadedRpc contains base64-encoded gzip-compressed protobuf bytes for request and response

type RouteSpec

type RouteSpec struct {
	Pattern string
	Rpcs    []RpcSpec
}

RouteSpec defines preload configuration for a route pattern.

func MatchRoute

func MatchRoute(routes []RouteSpec, path string) (*RouteSpec, map[string]string)

MatchRoute finds the first matching route for a given path.

type RpcError

type RpcError struct {
	Code    string            `json:"code"`
	Message string            `json:"message"`
	Details map[string]string `json:"details,omitempty"`
}

RpcError is a structured error that serializes to JSON for RPC responses.

func ErrAlreadyExists

func ErrAlreadyExists(msg string) *RpcError

func ErrInternal

func ErrInternal(msg string) *RpcError

func ErrNotFound

func ErrNotFound(msg string) *RpcError

func ErrPermissionDenied

func ErrPermissionDenied(msg string) *RpcError

func ErrRateLimited

func ErrRateLimited(msg string) *RpcError

func ErrUnauthenticated

func ErrUnauthenticated(msg string) *RpcError

func ErrValidation

func ErrValidation(msg string) *RpcError

func (*RpcError) Error

func (e *RpcError) Error() string

func (*RpcError) WithDetails

func (e *RpcError) WithDetails(details map[string]string) *RpcError

WithDetails returns a copy of the error with the given details added.

type RpcHandler

type RpcHandler func(w http.ResponseWriter, r *http.Request, method string, body []byte) ([]byte, error)

RpcHandler is the callback signature used by middleware and the dispatcher.

type RpcSpec

type RpcSpec struct {
	Method string
	Params map[string]string
}

RpcSpec defines an RPC to preload with optional parameter mappings.

type StreamAdapter

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

StreamAdapter provides length-prefixed streaming over HTTP responses. Each message is sent with a 4-byte big-endian length prefix followed by the protobuf-encoded message bytes.

func NewStreamAdapter

func NewStreamAdapter(w http.ResponseWriter) *StreamAdapter

func (*StreamAdapter) Send

func (sa *StreamAdapter) Send(data []byte) error

Send writes a length-prefixed message to the stream.

func (*StreamAdapter) SendHeaders

func (sa *StreamAdapter) SendHeaders() error

SendHeaders writes streaming response headers and flushes them to the client.

type StreamHandler

type StreamHandler func(w http.ResponseWriter, r *http.Request, method string, body []byte) error

StreamHandler handles a streaming RPC call. It receives the method name, request body, and a StreamAdapter for sending responses. It should return nil after writing to the stream.

type UnaryHandler

type UnaryHandler func(w http.ResponseWriter, r *http.Request, method string, body []byte) ([]byte, error)

UnaryHandler handles a unary RPC call. It receives the method name and request body, and returns the serialized response bytes or an error.

func RequireAuth

func RequireAuth(handler UnaryHandler) UnaryHandler

RequireAuth wraps a UnaryHandler to reject unauthenticated requests with 401. Use this on individual handlers that require authentication.

type ViteManifest

type ViteManifest map[string]ViteManifestEntry

ViteManifest represents the Vite build manifest

type ViteManifestEntry

type ViteManifestEntry struct {
	File string   `json:"file"`
	Src  string   `json:"src"`
	CSS  []string `json:"css"`
}

Directories

Path Synopsis
cli module
cmd
gapp module

Jump to

Keyboard shortcuts

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