agentleFS
jkanindajkaninda/okapi-skills/web_spa/SKILL.md

web_spa

Skill3 starsChanged 3 months agoMITView on GitHub

Okapi serves a front-end application (React, Vue, Svelte, Angular, …) alongside the API. Real files are served directly; any unmatched path under the prefix falls back to the index document so the client-side router takes over. Two entry points: SPA / SPAFS / SPAConfig are deprecated aliases of Web / WebFS / WebConfig. They still work unchanged. Register Web/WebFS after your API routes so the API keeps precedence. Use the all: prefix in the //go:embed directive so files beginning with…

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use the all: prefix in the //go:embed directive so files beginning with _ or .

    line 49
  2. Distinctive:

    If the path maps to a real file, that file is served.

    line 68
  3. Distinctive:

    When you want plain file serving without the SPA fallback:

    line 88

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Okapi Web / SPA Serving

Okapi serves a front-end application (React, Vue, Svelte, Angular, …) alongside the API. Real files are served directly; any unmatched path under the prefix falls back to the index document so the client-side router takes over.

Two entry points:

o.Web(prefix, dir string, cfg ...WebConfig)        // from a directory on disk
o.WebFS(prefix string, fsys fs.FS, cfg ...WebConfig) // from any fs.FS (embed.FS, os.DirFS, …)

SPA / SPAFS / SPAConfig are deprecated aliases of Web / WebFS / WebConfig. They still work unchanged.

Register Web/WebFS after your API routes so the API keeps precedence.

Serve from Disk (development)

o := okapi.Default()

o.Get("/api/v1/users", listUsers)

// Serves ./web/index.html for "/", "/login", "/users/42", ...
o.Web("/", "./web")

o.Start()

Serve from an Embedded FS (production, single binary)

//go:embed all:web/dist
var dist embed.FS

func main() {
    o := okapi.Default()

    o.Get("/api/v1/users", listUsers)

    o.WebFS("/", dist, okapi.WebConfig{
        Root:   "web/dist", // sub-directory inside the embed.FS
        MaxAge: time.Hour,  // Cache-Control for assets
    })

    o.Start()
}

Use the all: prefix in the //go:embed directive so files beginning with _ or . (common in build output) are included.

WebConfig

FieldTypeDescription
IndexstringFile served for client-side routes. Defaults to index.html.
RootstringSub-directory inside the fs.FS holding the built app. WebFS only — ignored by Web.
Exclude[]stringExtra path prefixes that must never fall back to the index.
DisableAutoExcludeboolTurn off auto-excluding the top-level segment of registered routes; only Exclude is consulted.
MaxAgetime.DurationCache-Control max-age for asset files. The index is always no-cache.

The zero value is valid: serve index.html, auto-exclude registered API routes, no asset caching header.

Resolution Order

For every unmatched GET/HEAD request under the prefix:

  1. A registered route always wins (register Web last).
  2. If the path maps to a real file, that file is served.
  3. Otherwise the index document is returned.

The top-level segment of every registered route is auto-excluded from the fallback, so an unknown path under an API namespace 404s instead of silently serving HTML. With /api/v1/users registered, /api/v1/missing returns 404 — not index.html.

Excluding Extra Paths

o.Web("/", "./web", okapi.WebConfig{
    Exclude: []string{"/metrics", "/healthz"},
})

Caching Behaviour

  • Index document — always Cache-Control: no-cache, so a new deploy is picked up immediately.
  • Assets — honour WebConfig.MaxAge. MaxAge: time.Hour emits Cache-Control: public, max-age=3600. Zero adds no header.

Static Files (no index fallback)

When you want plain file serving without the SPA fallback:

o.Static("/assets", "public/assets")        // serve a directory, no directory listing
o.StaticFile("/favicon.ico", "favicon.ico") // serve a single file
o.StaticFS("/assets", http.FS(embedFS))     // serve from any http.FileSystem

Directory listing is disabled by default.

Mounting a Standard File Server

A catch-all segment ({any...}) hands a whole subtree to an http.Handler:

o.HandleHTTP("GET", "/assets/{any...}",
    http.StripPrefix("/assets/", http.FileServer(http.Dir("./public"))))

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.