Learn about Parka's specialized handlers for serving static content, templates, and commands
Parka provides two specialized handlers for serving static content: StaticDirHandler and StaticFileHandler. These handlers are designed to serve static files from either the filesystem or embedded files, with different strategies for path handling and file organization.
The StaticDirHandler is designed to serve an entire directory of static files, maintaining the directory structure when serving the files over HTTP.
type StaticDirHandler struct {
fs fs.FS
localPath string
}
fs: The filesystem interface that provides access to the static fileslocalPath: The base path within the filesystem where the static files are locatedThe handler can be configured using functional options:
WithDefaultFS(fs fs.FS, localPath string): Sets a default filesystem and local path
handler := NewStaticDirHandler(
WithDefaultFS(embeddedFS, "static"),
)
WithLocalPath(localPath string): Sets up the handler to serve files from a local directory
handler := NewStaticDirHandler(
WithLocalPath("/path/to/static/files"),
)
Basic creation with options:
handler := NewStaticDirHandler(options...)
Creation from configuration:
handler := NewStaticDirHandlerFromConfig(staticConfig, options...)
The handler is registered with a Parka server using the Serve method:
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create and configure the handler
handler := NewStaticDirHandler(
WithLocalPath("./static"),
)
// Register the handler with a base path
err = handler.Serve(server, "/static")
if err != nil {
return err
}
This will serve all files from the configured directory under the /static URL path.
The StaticFileHandler is designed to serve individual files or specific subdirectories, with more precise control over the served paths.
type StaticFileHandler struct {
fs fs.FS
localPath string
}
fs: The filesystem interface that provides access to the static fileslocalPath: The specific path to the file or subdirectory to serveWithDefaultFS(fs fs.FS, localPath string): Sets a default filesystem and local path
handler := NewStaticFileHandler(
WithDefaultFS(embeddedFS, "assets/file.css"),
)
WithLocalPath(localPath string): Sets up the handler to serve from a local path
handler := NewStaticFileHandler(
WithLocalPath("/path/to/specific/file.js"),
)
Basic creation with options:
handler := NewStaticFileHandler(options...)
Creation from configuration:
handler := NewStaticFileHandlerFromConfig(staticFileConfig, options...)
The handler is registered with a Parka server using the Serve method:
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create and configure the handler
handler := NewStaticFileHandler(
WithLocalPath("/path/to/specific/file.js"),
)
// Register the handler with a specific URL path
err = handler.Serve(server, "/assets/js/script.js")
if err != nil {
return err
}
MustSubFS for safe subpath handlingScope:
StaticDirHandler: Serves entire directories with their structureStaticFileHandler: Serves specific files or subdirectories with precise path controlPath Handling:
StaticDirHandler: Automatically handles directory structure and trailing slashesStaticFileHandler: Provides exact path mapping and uses Echo's subfilesystem functionalityUse Cases:
StaticDirHandler: Best for serving static assets like images, CSS, and JavaScript files in their directory structureStaticFileHandler: Best for serving individual files or when precise control over URL paths is neededDirectory Structure:
StaticDirHandler for serving multiple related filesStaticFileHandler for specific files that need custom URL pathsSecurity:
Performance:
//go:embed static/*
var staticFS embed.FS
handler := NewStaticDirHandler(
WithDefaultFS(staticFS, "static"),
)
err = handler.Serve(server, "/assets")
if err != nil {
return err
}
handler := NewStaticDirHandler(
WithLocalPath("./static"),
)
err = handler.Serve(server, "/static")
if err != nil {
return err
}
handler := NewStaticFileHandler(
WithLocalPath("./assets/main.js"),
)
err = handler.Serve(server, "/js/main.js")
if err != nil {
return err
}
config := &config.Static{
LocalPath: "./static",
}
handler := NewStaticDirHandlerFromConfig(config)
err = handler.Serve(server, "/assets")
if err != nil {
return err
}
Both handlers handle errors gracefully:
Both handlers integrate seamlessly with Echo's static file serving capabilities:
StaticFS method internallyParka provides two specialized handlers for serving templated content: TemplateHandler and TemplateDirHandler. These handlers enable dynamic content rendering using Go templates, with support for both HTML and Markdown files.
The TemplateHandler is designed to serve a single template file, rendering it with optional data and supporting both HTML and Markdown content.
type TemplateHandler struct {
fs fs.FS
TemplateFile string
rendererOptions []render.RendererOption
renderer *render.Renderer
alwaysReload bool
}
fs: The filesystem interface that provides access to the template filesTemplateFile: The path to the template file to be renderedrendererOptions: Additional options for configuring the template rendererrenderer: The renderer instance used to process templatesalwaysReload: Whether to reload templates on every request (useful for development)The handler can be configured using functional options:
WithDefaultFS(fs fs.FS): Sets a default filesystem for template loading
handler := NewTemplateHandler("index.tmpl.html",
WithDefaultFS(embeddedFS),
)
WithAlwaysReload(alwaysReload bool): Enables template reloading for development
handler := NewTemplateHandler("index.tmpl.html",
WithAlwaysReload(true),
)
The handler is registered with a Parka server using the Serve method:
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create and configure the handler
handler := NewTemplateHandler("index.tmpl.html",
WithDefaultFS(embeddedFS),
WithAlwaysReload(true),
)
// Register the handler with a URL path
err = handler.Serve(server, "/")
if err != nil {
return err
}
The TemplateDirHandler is designed to serve an entire directory of templates, supporting both HTML and Markdown files with automatic routing based on file paths.
type TemplateDirHandler struct {
fs fs.FS
LocalDirectory string
IndexTemplateName string
MarkdownBaseTemplateName string
rendererOptions []render.RendererOption
renderer *render.Renderer
alwaysReload bool
}
fs: The filesystem interface that provides access to the template filesLocalDirectory: The base directory containing templatesIndexTemplateName: The template to use for directory index pagesMarkdownBaseTemplateName: The base template for rendering Markdown filesrendererOptions: Additional options for configuring the template rendererrenderer: The renderer instance used to process templatesalwaysReload: Whether to reload templates on every requestWithDefaultFS(fs fs.FS, localPath string): Sets a default filesystem and local path
handler, err := NewTemplateDirHandler(
WithDefaultFS(embeddedFS, "templates"),
)
WithLocalDirectory(localPath string): Sets up the handler to serve from a local directory
handler, err := NewTemplateDirHandler(
WithLocalDirectory("./templates"),
)
The handler is registered with a Parka server using the Serve method:
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create and configure the handler
handler, err := NewTemplateDirHandler(
WithLocalDirectory("./templates"),
WithAlwaysReload(true),
)
if err != nil {
return err
}
// Register the handler with a base path
err = handler.Serve(server, "/")
if err != nil {
return err
}
The TemplateDirHandler automatically discovers and serves:
*.tmpl.md - Markdown templates*.md - Plain Markdown files*.tmpl.html - HTML templates*.html - Plain HTML filesScope:
TemplateHandler: Serves a single template fileTemplateDirHandler: Serves an entire directory of templates with automatic routingFile Support:
TemplateHandler: Focused on single template renderingTemplateDirHandler: Supports multiple template types and automatic discoveryUse Cases:
TemplateHandler: Best for single pages or specific templatesTemplateDirHandler: Best for documentation sites, multi-page applications, or content-heavy sitesTemplate Organization:
Development Workflow:
WithAlwaysReload(true) during developmentPerformance:
handler := NewTemplateHandler("index.tmpl.html",
WithDefaultFS(embeddedFS),
WithAlwaysReload(true),
)
server.AddHandler(handler, "/")
handler, err := NewTemplateDirHandler(
WithLocalDirectory("./docs"),
WithAlwaysReload(true),
)
server.AddHandler(handler, "/docs")
config := &config.TemplateDir{
LocalDirectory: "./templates",
IndexTemplateName: "index.tmpl.html",
}
handler, err := NewTemplateDirHandlerFromConfig(config)
server.AddHandler(handler, "/content")
All handlers handle errors gracefully and return appropriate error types that should be checked:
The handlers integrate with Echo's routing system:
Parka provides three specialized handlers for serving commands: CommandHandler, CommandDirHandler, and GenericCommandHandler. These handlers enable exposing commands as HTTP endpoints with various output formats and interactive UIs.
The GenericCommandHandler is the base handler that provides core functionality for serving commands over HTTP. It's used internally by both CommandHandler and CommandDirHandler.
type GenericCommandHandler struct {
Stream bool
AdditionalData map[string]interface{}
ParameterFilter *config.ParameterFilter
TemplateName string
IndexTemplateName string
TemplateLookup render.TemplateLookup
BasePath string
preMiddlewares []middlewares.Middleware
postMiddlewares []middlewares.Middleware
middlewares []middlewares.Middleware
}
Stream: Whether to use row-based streaming output (true by default)AdditionalData: Extra data passed to templatesParameterFilter: Configuration for parameter filtering, defaults, and overridesTemplateName: Template for rendering command outputIndexTemplateName: Template for rendering command indexesTemplateLookup: Interface for finding templatesBasePath: Base URL path for the handlerpreMiddlewares: Middleware chain to run before parameter filter middlewarespostMiddlewares: Middleware chain to run after parameter filter middlewaresThe handler provides several endpoints for different output formats:
/data/*: Returns command output in JSON format/text/*: Returns command output as plain text/streaming/*: Streams command output using Server-Sent Events (SSE)/datatables/*: Displays command output in an interactive DataTables UI/download/*: Allows downloading command output in various formatsWithTemplateName(name string): Sets the template for command outputWithParameterFilter(filter *config.ParameterFilter): Configures parameter handlingWithMergeAdditionalData(data map[string]interface{}, override bool): Adds template dataWithPreMiddlewares(middlewares ...middlewares.Middleware): Add middlewares to run before parameter filter middlewaresWithPostMiddlewares(middlewares ...middlewares.Middleware): Add middlewares to run after parameter filter middlewaresExample:
handler := NewGenericCommandHandler(
WithTemplateName("command.tmpl.html"),
WithParameterFilter(filter),
WithPreMiddlewares(myPreMiddleware1, myPreMiddleware2),
WithPostMiddlewares(myPostMiddleware1, myPostMiddleware2),
)
The middlewares will be executed in this order:
The CommandHandler is designed to serve a single command with multiple output formats.
type CommandHandler struct {
GenericCommandHandler
DevMode bool
Command cmds.Command
}
GenericCommandHandlerDevMode: Enables development features like template reloadingCommand: The command to be servedWithDevMode(devMode bool): Enables development mode
handler := NewCommandHandler(cmd,
WithDevMode(true),
)
WithGenericCommandHandlerOptions(options ...GenericCommandHandlerOption): Adds generic options
handler := NewCommandHandler(cmd,
WithGenericCommandHandlerOptions(
WithTemplateName("command.tmpl.html"),
WithParameterFilter(filter),
),
)
Basic creation with options:
handler := NewCommandHandler(myCommand, options...)
Creation from configuration:
handler, err := NewCommandHandlerFromConfig(config, loader, options...)
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create your command
cmd := &MyCommand{}
// Create and configure the handler
handler := NewCommandHandler(cmd,
WithDevMode(true),
WithGenericCommandHandlerOptions(
WithTemplateName("command.tmpl.html"),
WithParameterFilter(filter),
),
)
// Register the handler with a URL path
err = handler.Serve(server, "/my-command")
if err != nil {
return err
}
The CommandDirHandler serves multiple commands from a repository, providing automatic routing and discovery.
type CommandDirHandler struct {
GenericCommandHandler
DevMode bool
Repository *repositories.Repository
}
GenericCommandHandlerDevMode: Enables development featuresRepository: The command repository to serveWithDevMode(devMode bool): Enables development mode
handler := NewCommandDirHandler(
WithDevMode(true),
)
WithRepository(r *repositories.Repository): Sets the command repository
handler := NewCommandDirHandler(
WithRepository(myRepo),
)
routes:
- path: /commands
commandDirectory:
includeDefaultRepositories: true
repositories:
- ~/code/my-commands
templateLookup:
directories:
- ~/templates
indexTemplateName: index.tmpl.html
defaults:
flags:
limit: 100
overrides:
layers:
glazed:
filter:
- id
- name
additionalData:
title: "My Commands"
// Create a new Parka server
server, err := server.NewServer(
server.WithPort(8080),
server.WithAddress("localhost"),
)
if err != nil {
return err
}
// Create and configure your repository
repo := repositories.NewRepository()
repo.AddCommand(commands.NewHelloCommand())
// Add more commands...
// Create and configure the handler
handler, err := NewCommandDirHandler(
WithRepository(repo),
WithDevMode(true),
WithGenericCommandHandlerOptions(
WithIndexTemplateName("commands/index.tmpl.html"),
WithTemplateName("commands/view.tmpl.html"),
),
)
if err != nil {
return err
}
// Register the handler with a base path
err = handler.Serve(server, "/commands")
if err != nil {
return err
}
Command Organization:
Security:
Performance:
cmd := &MyCommand{}
handler := NewCommandHandler(cmd,
WithDevMode(true),
WithGenericCommandHandlerOptions(
WithTemplateName("command.tmpl.html"),
WithParameterFilter(filter),
),
)
server.AddHandler(handler, "/my-command")
repo := repositories.NewRepository()
repo.AddCommandFromFile("./commands/my-command.yaml")
handler, err := NewCommandDirHandler(
WithRepository(repo),
WithDevMode(true),
WithGenericCommandHandlerOptions(
WithIndexTemplateName("index.tmpl.html"),
WithTemplateName("command.tmpl.html"),
),
)
server.AddHandler(handler, "/commands")
config := &config.CommandDir{
IncludeDefaultRepositories: true,
Repositories: []string{"./commands"},
IndexTemplateName: "index.tmpl.html",
}
handler, err := NewCommandDirHandlerFromConfig(config)
server.AddHandler(handler, "/api")
All handlers handle errors gracefully and return appropriate error types that should be checked:
The handlers integrate with Echo's routing system: