Golang Pro
antoniopaya22/go-rest-template
Implements concurrent Go patterns using goroutines and channels, designs and builds microservices with gRPC or REST, optimizes Go application performance with pprof, and enforces idiomatic Go with…
Shows how to build composable, context-aware HTTP pipelines in Go using fp-go's ReaderIOResult monad instead of raw net/http calls.
$ npx skills add IBM/fp-go --skill fp-go-http -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install IBM/fp-go fp-go-http --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/fp-go-http .claude/skills/fp-go-http && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .claude/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/IBM/fp-go/tree/main/skills/fp-go-httpType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add IBM/fp-go --skill fp-go-http -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install IBM/fp-go fp-go-http --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/fp-go-http .agents/skills/fp-go-http && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .agents/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add IBM/fp-go --skill fp-go-http -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install IBM/fp-go fp-go-http --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/fp-go-http .cursor/skills/fp-go-http && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .cursor/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/IBM/fp-go.git --path skills/fp-go-http--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add IBM/fp-go --skill fp-go-http -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install IBM/fp-go fp-go-http --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/fp-go-http .gemini/skills/fp-go-http && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .gemini/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install IBM/fp-go fp-go-httpInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add IBM/fp-go --skill fp-go-http -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/fp-go-http .github/skills/fp-go-http && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .github/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add IBM/fp-go --skill fp-go-http -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install IBM/fp-go fp-go-http --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/fp-go-http .opencode/skills/fp-go-http && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "fp-go-http" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-http into .opencode/skills/fp-go-http/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-http", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
fp-go-httpShows how to build composable, context-aware HTTP pipelines in Go using fp-go's ReaderIOResult monad instead of raw net/http calls.
Wraps `net/http` so every HTTP operation is described lazily as a `ReaderIOResult`, a function from a context down to a function that returns a single `Result` value, not a Go-style value-and-error pair. Running such a pipeline takes two calls in sequence and must be unwrapped with the result package's own helper, since writing it as a plain two-variable assignment is a compile error.
Covers building a client and a request with helpers like `MakeGetRequest` and the request builder options (`WithURL`, `WithJSON`, `WithBearer`, `WithHeader`, `WithQueryArg`), reading a response with `ReadJSON`, `ReadText`, `ReadAll` or `ReadFullResponse`, and running several requests in parallel with `TraverseArray` or `TraverseTuple2`. Header and content-type name constants are provided so common values like `Authorization` or `application/json` don't need to be typed as raw strings.
Because fp-go is uncommon in most models' training data, the guidance points to the project's own MCP server for looking up any combinator not already shown rather than guessing its signature, and insists on running `go build` and `go vet` afterward to catch type-parameter or argument-order mistakes before presenting the code.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 4c9c328. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
goFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
jsonplaceholder.typicode.comcatfact.ninjaFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
fp-go HTTP Client Patterns loads about 4.4k tokens when it runs. Until then it costs about 184 tokens; SKILL.md has 798 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from IBM/fp-go at commit 4c9c328, republished under its Apache-2.0 licence (© IBM). 798 words, ~4,443 tokens.
.claude/skills/fp-go-http/SKILL.md (or your agent's skills folder).fp-go wraps net/http in the ReaderIOResult monad, giving you composable, context-aware HTTP operations with automatic error propagation. The core package is:
github.com/IBM/fp-go/v2/context/readerioresult/httpAll HTTP operations are lazy — they describe what to do but do not execute until you call the resulting function with a context.Context.
Executing a pipeline. ReaderIOResult[A] is func(context.Context) func() Result[A], so running it takes two calls and yields one value:
res := pipeline(ctx)() // Result[A] — a single value, NOT (A, error)
value, err := R.Unwrap(res) // R = github.com/IBM/fp-go/v2/resultExamples below use the shorthand value, err := R.Unwrap(pipeline(ctx)()). Writing
value, err := pipeline(ctx)() is a compile error ("2 variables but … returns 1 value").
fp-go is low-frequency in training data, so signatures are easy to misremember.
For any combinator not shown below, look it up via the fp-go MCP server's
search_examples / get_example tools (see the fp-go-mcp skill) instead of
guessing. After writing code, run go build ./... and go vet ./... and fix any
type-parameter or argument-order errors before presenting it.
// Requester builds an *http.Request given a context.
type Requester = ReaderIOResult[*http.Request] // func(context.Context) func() result.Result[*http.Request]
// Client executes a Requester and returns the response wrapped in ReaderIOResult.
type Client interface {
Do(Requester) ReaderIOResult[*http.Response]
}import (
"net/http"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
)
client := H.MakeClient(http.DefaultClient)
// Or with a custom client:
custom := &http.Client{Timeout: 10 * time.Second}
client := H.MakeClient(custom)// GET request (most common)
req := H.MakeGetRequest("https://api.example.com/users/1")
// Arbitrary method + body
req := H.MakeRequest("POST", "https://api.example.com/users", bodyReader)import (
"context"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
client := H.MakeClient(http.DefaultClient)
// ReadJSON validates status, Content-Type, then unmarshals JSON
result := H.ReadJSON[User](client)(H.MakeGetRequest("https://api.example.com/users/1"))
// Execute — provide context once. The inner () yields a Result[User] (one value).
user, err := R.Unwrap(result(context.Background())())http.Client.Timeout is a global cap. For a per-request or per-pipeline bound, scope the context instead: requests honour context cancellation, and the cancel func is released automatically.
bounded := F.Pipe2(
H.ReadJSON[User](client)(H.MakeGetRequest("https://api.example.com/users/1")),
RIO.WithTimeout[User](2*time.Second),
RIO.WithValue[User](traceIDKey, traceID), // read downstream with RIO.AskValue[string](traceIDKey)
)Never write ctx, cancel := context.WithTimeout(...) around the call or ctx.Value(key).(V) inside it. See the fp-go-context skill for the full context-handling guide.
All accept a Client and return a function Requester → ReaderIOResult[A]:
| Function | Returns | Notes |
|---|---|---|
ReadJSON[A](client) | ReaderIOResult[A] | Validates status + Content-Type, unmarshals JSON |
ReadText(client) | ReaderIOResult[string] | Validates status, reads body as UTF-8 string |
ReadAll(client) | ReaderIOResult[[]byte] | Validates status, returns raw body bytes |
ReadFullResponse(client) | ReaderIOResult[FullResponse] | Returns Pair[*http.Response, []byte] |
FullResponse = Pair[*http.Response, []byte] — use pair.First / pair.Second to access components.
import (
F "github.com/IBM/fp-go/v2/function"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
IO "github.com/IBM/fp-go/v2/io"
)
client := H.MakeClient(http.DefaultClient)
readPost := H.ReadJSON[Post](client)
pipeline := F.Pipe2(
H.MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1"),
readPost,
RIO.TapIOK(IO.Logf[Post]("Got post: %v")),
)
post, err := R.Unwrap(pipeline(context.Background())())Use RIO.TraverseArray when all requests return the same type:
import (
A "github.com/IBM/fp-go/v2/array"
F "github.com/IBM/fp-go/v2/function"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
IO "github.com/IBM/fp-go/v2/io"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/string"
)
type PostItem struct {
UserID uint `json:"userId"`
ID uint `json:"id"`
Title string `json:"title"`
}
client := H.MakeClient(http.DefaultClient)
readPost := H.ReadJSON[PostItem](client)
// index -> URL, point-free
postURL := F.Flow2(N.Add(1), S.Format[int]("https://jsonplaceholder.typicode.com/posts/%d"))
// Fetch 10 posts in parallel
data := F.Pipe3(
A.MakeBy(10, postURL),
RIO.TraverseArray(F.Flow3(
H.MakeGetRequest,
readPost,
RIO.TapIOK(IO.Logf[PostItem]("Post: %v")),
)),
RIO.TapIOK(IO.Logf[[]PostItem]("All posts: %v")),
RIO.Map(A.Size[PostItem]),
)
count, err := R.Unwrap(data(context.Background())())Use RIO.TraverseTuple2 (or Tuple3, etc.) when requests return different types:
import (
T "github.com/IBM/fp-go/v2/tuple"
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
F "github.com/IBM/fp-go/v2/function"
)
type CatFact struct {
Fact string `json:"fact"`
}
client := H.MakeClient(http.DefaultClient)
readPost := H.ReadJSON[PostItem](client)
readCatFact := H.ReadJSON[CatFact](client)
// Execute both requests in parallel with different response types
data := F.Pipe3(
T.MakeTuple2(
"https://jsonplaceholder.typicode.com/posts/1",
"https://catfact.ninja/fact",
),
T.Map2(H.MakeGetRequest, H.MakeGetRequest), // build both requesters
RIO.TraverseTuple2(readPost, readCatFact), // run in parallel, typed
RIO.TapIOK(IO.Logf[T.Tuple2[PostItem, CatFact]]("Result: %v")),
)
both, err := R.Unwrap(data(context.Background())())
// both.F1 is PostItem, both.F2 is CatFactFor complex requests (custom headers, query params, JSON body), use the builder:
import (
B "github.com/IBM/fp-go/v2/http/builder"
RB "github.com/IBM/fp-go/v2/context/readerioresult/http/builder"
F "github.com/IBM/fp-go/v2/function"
HD "github.com/IBM/fp-go/v2/http/headers"
C "github.com/IBM/fp-go/v2/http/content"
)
// GET with query parameters
req := F.Pipe2(
B.Default,
B.WithURL("https://api.example.com/items?page=1"),
B.WithQueryArg("limit")("50"),
)
requester := RB.Requester(req)
// POST with JSON body
req := F.Pipe3(
B.Default,
B.WithURL("https://api.example.com/users"),
B.WithPost,
B.WithJSON(map[string]string{"name": "Alice"}),
// sets Content-Type: application/json (C.JSON) automatically
)
requester := RB.Requester(req)
// With authentication and custom headers — use the constants, not string literals
req := F.Pipe4(
B.Default,
B.WithURL("https://api.example.com/protected"),
B.WithBearer("my-token"), // sets Authorization: Bearer my-token
B.WithHeader(HD.Accept)(C.JSON),
B.WithHeader(HD.XRequestID)("123"),
)
requester := RB.Requester(req)
// Execute
result := H.ReadJSON[Response](client)(requester)
data, err := R.Unwrap(result(ctx)())| Function | Effect |
|---|---|
B.WithURL(url) | Set the target URL |
B.WithMethod(method) | Set HTTP method (GET, POST, PUT, DELETE, …) |
B.WithJSON(v) | Marshal v as JSON body, set Content-Type: application/json |
B.WithBytes(data) | Set raw bytes body, set Content-Length automatically |
B.WithHeader(key)(value) | Add a request header |
B.WithBearer(token) | Set Authorization: Bearer <token> |
B.WithQueryArg(key)(value) | Append a query parameter |
B.WithGet / B.WithPost / B.WithPut / B.WithDelete | Pre-bound WithMethod for the common verbs |
B.WithContentType(ct) | Set the Content-Type header |
B.WithFormData(values) | url.Values body + Content-Type: application/x-www-form-urlencoded |
B.WithoutBody | Remove any body previously set |
B.WithoutHeader(key) / B.WithoutQueryArg(key) | Remove a header / query parameter |
Every With* is an Endomorphism[*Builder], so they chain freely inside F.PipeN(B.Default, …).
Never spell header names or media types as string literals — fp-go ships constants for both.
http/headers (Alias HD)github.com/IBM/fp-go/v2/http/headers defines the commonly used header names as lower-case constants (the form mandated by HTTP/2 and HTTP/3):
| Group | Constants |
|---|---|
| Representation | ContentType, ContentLength, ContentEncoding, ContentLanguage, ContentDisposition, ContentRange, ContentLocation, TransferEncoding, Date, Link |
| Request | Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, Authorization, ProxyAuthorization, Cookie, Host, UserAgent, Referer, Origin, Range, Expect, Forwarded |
| Response | Location, Server, SetCookie, WWWAuthenticate, ProxyAuthenticate, RetryAfter, Allow, AcceptRanges |
| Caching / conditional | CacheControl, ETag, LastModified, Expires, Age, Vary, Pragma, IfMatch, IfNoneMatch, IfModifiedSince, IfUnmodifiedSince, IfRange |
| CORS | AccessControlAllowOrigin, AccessControlAllowMethods, AccessControlAllowHeaders, AccessControlAllowCredentials, AccessControlExposeHeaders, AccessControlMaxAge, AccessControlRequestMethod, AccessControlRequestHeaders |
| Security | StrictTransportSecurity, ContentSecurityPolicy, XContentTypeOptions, XFrameOptions, ReferrerPolicy |
| Proxy / tracing | XForwardedFor, XForwardedHost, XForwardedProto, XRequestID, XCorrelationID, Traceparent, Tracestate |
Lower case is safe with HTTP/1.1 too: http.Header.Get/Set/Add/Values/Del and the builder canonicalize keys. Only raw map indexing does not — h[HD.ContentType] misses "Content-Type". Use h.Get(HD.ContentType) or the lenses below.
The package also provides functional access to http.Header:
HD.AtValue(HD.Authorization).Get(h) // Option[string] — first value
HD.AtValues(HD.Accept).Get(h) // Option[[]string] — all values, None if absent
h2 := HD.AtValue(HD.ContentType).Set(O.Some(C.JSON))(h) // new header map; O.None removes the header
merged := HD.Monoid.Concat(defaults, overrides) // union; values of shared keys are concatenatedBoth lenses canonicalize the header name, and Set returns a new http.Header, leaving h untouched.
http/content (Alias C)github.com/IBM/fp-go/v2/http/content defines media type constants (bare type, no parameters):
| Group | Constants |
|---|---|
| JSON family | JSON, ProblemJSON, JSONPatch, MergePatch, NDJSON, JSONLD, HALJSON, JSONAPI |
| XML family | XML, TextXML, ProblemXML, SOAP |
| Other structured | YAML, CBOR, Protobuf, GRPC |
| Text | TextPlain, TextHTML, TextCSS, TextCSV, TextJavaScript, TextMarkdown, TextEventStream |
| Forms / multipart | FormEncoded, MultipartFormData, MultipartMixed, MultipartByteRanges |
| Binary | OctetStream, PDF, ZIP, Gzip, WASM |
| Media | ImagePNG, ImageJPEG, ImageGIF, ImageWebP, ImageAVIF, ImageSVG, AudioMPEG, AudioOGG, VideoMP4, VideoWebM, FontWOFF2 |
C.Json is deprecated — use C.JSON.
The builder already uses these: B.WithJSON sets C.JSON, B.WithFormData sets C.FormEncoded; B.WithContentType(ct) and B.WithAuthorization(v) are B.WithHeader(HD.ContentType) / B.WithHeader(HD.Authorization).
A received Content-Type usually carries parameters (application/json; charset=utf-8), so parse before comparing against a constant:
import (
FH "github.com/IBM/fp-go/v2/http"
PA "github.com/IBM/fp-go/v2/pair"
R "github.com/IBM/fp-go/v2/result"
S "github.com/IBM/fp-go/v2/string"
)
isJSON := F.Flow3(
FH.ParseMediaType, // string -> Result[Pair[mediaType, params]]
R.Map(PA.Head[string, map[string]string]),
R.Fold(F.Constant1[error](false), S.Equals(C.JSON)),
)
isJSON(resp.Header.Get(HD.ContentType))H.ReadJSON already performs this validation (JSON or any +json type) — no manual check is needed on that path.
Errors from request creation, HTTP status codes, Content-Type validation, and JSON parsing all propagate automatically through the Result monad. You only handle errors at the call site:
// Pattern 1: run it, then unwrap. pipeline(ctx)() yields a single Result[A];
// result.Unwrap turns that into idiomatic (A, error).
value, err := R.Unwrap(pipeline(ctx)())
if err != nil { /* handle */ }
// Pattern 2: run the pipeline, then eliminate the Result with result.Fold,
// passing named handlers (defined once, see the full example below)
F.Pipe1(
pipeline(ctx)(), // Result[MyType]
R.Fold(writeError(w, http.StatusInternalServerError), writeJSON[MyType](w)),
)Do not use RIO.Fold for this. In context/readerioresult, Fold[A, B](onLeft Kleisli[error, B], onRight Kleisli[A, B]) Operator[A, B] stays inside the monad — both branches must return a ReaderIOResult[B], so it cannot take plain func(error) / func(A) side-effecting handlers. To leave the monad, execute the pipeline and fold the resulting Result (as above), or use readerioresult.Fold from the non-context package, which lands in ReaderIO.
package main
import (
"encoding/json"
"net/http"
F "github.com/IBM/fp-go/v2/function"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
R "github.com/IBM/fp-go/v2/result"
IO "github.com/IBM/fp-go/v2/io"
S "github.com/IBM/fp-go/v2/string"
HD "github.com/IBM/fp-go/v2/http/headers"
C "github.com/IBM/fp-go/v2/http/content"
)
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
}
var client = H.MakeClient(http.DefaultClient)
// fetchPost: int -> ReaderIOResult[Post], point-free
func fetchPost() RIO.Kleisli[int, Post] {
return F.Flow4(
S.Format[int]("https://jsonplaceholder.typicode.com/posts/%d"),
H.MakeGetRequest,
H.ReadJSON[Post](client),
RIO.TapIOK(IO.Logf[Post]("fetched: %v")),
)
}
// Leaf handlers — the only place that touches the ResponseWriter
func writeError(w http.ResponseWriter, status int) func(error) F.Void {
return func(err error) F.Void {
http.Error(w, err.Error(), status)
return F.VOID
}
}
func writeJSON[A any](w http.ResponseWriter) func(A) F.Void {
return func(a A) F.Void {
w.Header().Set(HD.ContentType, C.JSON)
json.NewEncoder(w).Encode(a)
return F.VOID
}
}
func handler(w http.ResponseWriter, r *http.Request) {
F.Pipe1(
fetchPost()(1)(r.Context())(), // run it: Result[Post]
R.Fold(writeError(w, http.StatusBadGateway), writeJSON[Post](w)),
)
}import (
"net/http"
H "github.com/IBM/fp-go/v2/context/readerioresult/http"
RB "github.com/IBM/fp-go/v2/context/readerioresult/http/builder"
B "github.com/IBM/fp-go/v2/http/builder"
HD "github.com/IBM/fp-go/v2/http/headers" // header name constants, AtValue/AtValues lenses, Monoid
C "github.com/IBM/fp-go/v2/http/content" // content type constants
FH "github.com/IBM/fp-go/v2/http" // ParseMediaType, StatusCodeError, …
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
F "github.com/IBM/fp-go/v2/function"
R "github.com/IBM/fp-go/v2/result" // Unwrap, Fold — leaving the monad
A "github.com/IBM/fp-go/v2/array"
T "github.com/IBM/fp-go/v2/tuple"
IO "github.com/IBM/fp-go/v2/io"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/string"
PA "github.com/IBM/fp-go/v2/pair"
)© IBM, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/fp-go-http of IBM/fp-go.
Open the folder on GitHubat commit 4c9c328
fp-go HTTP Client Patterns next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| fp-go HTTP Client Patterns this skillIBM/fp-go | 2k | — | ~4.4k | Automated safety check: Pass | Apache-2.0 | |
| Golang Proantoniopaya22/go-rest-template | 172 | 3 repos | ~1.2k | Automated safety check: Pass | MIT | |
| Tiger Brokers OpenAPI Go SDKqusong0627/QuantMind | 1.7k | — | ~1.1k | Automated safety check: Pass | Apache-2.0 | |
| Tiger Brokers Rust SDKqusong0627/QuantMind | 1.7k | — | ~1.1k | Automated safety check: Pass | Apache-2.0 | |
| Golang Prodavila7/claude-code-templates | 32k | 7 repos | ~1.9k | Automated safety check: Pass | MIT | |
| Golang Best Practiceskoolamusic/claudefiles | 130 | — | ~935 | Automated safety check: Pass | MIT |
antoniopaya22/go-rest-template
Implements concurrent Go patterns using goroutines and channels, designs and builds microservices with gRPC or REST, optimizes Go application performance with pprof, and enforces idiomatic Go with…
qusong0627/QuantMind
Reference guides for building Go trading apps on the Tiger Brokers OpenAPI: SDK setup, market data, stock, futures and options orders, accounts and push streams.
qusong0627/QuantMind
Reference guides for building Rust apps on the Tiger Brokers OpenAPI SDK: setup, async market data, orders, accounts, options and real-time push.
davila7/claude-code-templates
Master Go 1.21+ with modern patterns, advanced concurrency, performance optimization, and production-ready microservices.
koolamusic/claudefiles
A skill your agent uses when building Go applications requiring concurrent programming, microservices architecture, or high-performance systems.
strukto-ai/mirage
Builds or extends a custom Mirage virtual filesystem adapter for an API, database, object store or app data, with a working mount configuration and filesystem tests.
IBM/fp-go
Covers handling Go's context.Context idiomatically in fp-go code: reading and scoping context through operators, timeouts, cancellation and converting ctx-first functions.
IBM/fp-go
Teaches an agent to write fp-go v2 services with the Effect type, carrying dependencies in its type parameter instead of in context.Context or parameters.
IBM/fp-go
Generates or hand-writes composable lenses for the fp-go library so nested Go structs can be read and updated immutably.
IBM/fp-go
Adds logging to fp-go functional pipelines with Tap operators, entry and exit logs and error context, so that logging never changes the value or error flowing through.
IBM/fp-go
Configures and queries the fp-go MCP server so Claude Code, Claude Desktop or another MCP client can search fp-go's examples and skills directly.
IBM/fp-go
Guides writing or reviewing point-free, first-match-wins case lists in fp-go v2 Go code instead of switch statements and if-else chains.
Works with
Categories
Shows how to build composable, context-aware HTTP pipelines in Go using fp-go's ReaderIOResult monad instead of raw net/http calls. Wraps `net/http` so every HTTP operation is described lazily as a `ReaderIOResult`, a function from a context down to a function that returns a single `Result` value, not a Go-style value-and-error pair. Running such a pipeline takes two calls in sequence and must be unwrapped with the result package's own helper, since writing it as a plain two-variable assignment is a compile error.
fp-go HTTP Client Patterns fits situations like: writing a new HTTP request or client using fp-go's ReaderIOResult; running several HTTP requests in parallel with TraverseArray or TraverseTuple2; adding a per-request timeout to an fp-go HTTP pipeline; looking up an fp-go HTTP combinator's exact signature before using it.
Run `npx skills add IBM/fp-go --skill fp-go-http -a claude-code`. Or copy the skill folder (skills/fp-go-http in IBM/fp-go) into .claude/skills/fp-go-http in your project. Claude Code loads it when a task matches its description.
Run `npx skills add IBM/fp-go --skill fp-go-http -a codex`. Or copy the skill folder (skills/fp-go-http in IBM/fp-go) into .agents/skills/fp-go-http in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add IBM/fp-go --skill fp-go-http -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/fp-go-http, .gemini/skills/fp-go-http, .github/skills/fp-go-http and .opencode/skills/fp-go-http in your project.
Going by SKILL.md and its folder, fp-go HTTP Client Patterns needs the command-line tools its instructions call (go). Our summary lists: Go 1.24 or newer; The github.com/IBM/fp-go/v2 module.
SKILL.md names 2 domains. In commands or code: jsonplaceholder.typicode.com and catfact.ninja; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
fp-go HTTP Client Patterns is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.4k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with fp-go HTTP Client Patterns: Golang Pro (antoniopaya22/go-rest-template, 172 stars), Tiger Brokers OpenAPI Go SDK (qusong0627/QuantMind, 1.7k stars), Tiger Brokers Rust SDK (qusong0627/QuantMind, 1.7k stars) and Golang Pro (davila7/claude-code-templates, 32k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
IBM (a GitHub organization, an official publisher) maintains it in IBM/fp-go, which has 2,029 GitHub stars. The repository holds 10 skills in this directory. The repository was last updated on October 7, 2026.
Source: IBM/fp-go on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.