petstore
The petstore.example microservice is the canonical worked output of the OpenAPI-import skill. It is generated, byte-for-byte, by pointing the skill at the public Swagger Petstore v3 OpenAPI document:
https://petstore3.swagger.io/api/v3/openapi.jsonIf you’re considering using the skill on your own remote API, this example is the concrete answer to “what does the skill actually produce?” — three endpoints chosen to cover both shapes the generator emits (typed function endpoints + raw web-relay endpoints), with every layer of the generated code visible alongside the prompts that produced it.
The full upstream API has 19 operations; only three are imported here. The durable record of the remote API lives in openapispecs.json so additional endpoints can be added incrementally later without re-fetching the document. That incremental shape is itself part of what the skill is designed to demonstrate.
The example is intentionally not wired into
main/main.goin the fabric repo so unrelated example builds don’t depend on a live remote API. Addpetstore.NewService()to your app explicitly if you want to exercise it against the public Petstore endpoint.
Imported Endpoints
| Endpoint | Route | Shape |
|---|---|---|
AddPet | POST /pet | Function: JSON Pet body, JSON Pet response |
GetPetById | GET /pet/{petId} | Function: int64 path argument, JSON response |
UploadFile | POST /pet/{petId}/uploadImage | Web: binary octet-stream relay |
Depends On
httpegress.core— every outbound HTTPS call topetstore3.swagger.iogoes through the egress proxy.
Configuration
Two configs are generated from the OpenAPI document’s server list + security scheme:
petstore.example:
RemoteBaseURL: https://petstore3.swagger.io/api/v3 # default; override per environment
BearerToken: <your-oauth2-token> # secret; goes in config.local.yamlBearerToken is the OAuth2 credential presented to the remote API on every call. It is a secret and belongs in config.local.yaml. RemoteBaseURL defaults to the public Petstore endpoint but is overridable so the same generated code can target a staging deployment of the same API.
Code Walkthrough
Source: exampleservices/petstore/service.go.
The Service struct holds no state — every call is independent:
type Service struct {
*Intermediate // IMPORTANT: Do not remove
}OnStartup and OnShutdown are empty.
The generated code has three layers:
- A typed delegating handler per imported endpoint. Body is one line: build the URL, call the appropriate helper, return the result.
- Two shared request helpers (
makeFunctionRequest,makeWebRequest) that own the actual outbound call: JSON encode/decode for typed endpoints, byte-transparent body relay for web endpoints. - Two small auth/URL helpers (
remoteURL,authenticate) called by both request helpers.
AddPet
func (svc *Service) AddPet(ctx context.Context, httpRequestBody *petstoreapi.Pet) (httpResponseBody *petstoreapi.Pet, httpStatusCode int, err error) {
httpStatusCode, err = svc.makeFunctionRequest(ctx, "POST", svc.remoteURL("/pet"), httpRequestBody, &httpResponseBody)
return httpResponseBody, httpStatusCode, errors.Trace(err)
}A typed function endpoint. The Go signature mirrors the OpenAPI operation exactly: a *Pet request body in, a *Pet response body and HTTP status code out. The skill recognized application/json on both sides of the operation and emitted typed JSON marshalling automatically. The remote status code is returned to the caller as-is rather than translated — there is no error reshaping, no retry, no caching baked into the generated code. Those are cross-cutting concerns that belong in the caller, not in the API’s representation.
GetPetById
func (svc *Service) GetPetById(ctx context.Context, petId int64) (httpResponseBody *petstoreapi.Pet, httpStatusCode int, err error) {
u, err := url.Parse(svc.remoteURL("/pet/" + url.PathEscape(fmt.Sprint(petId))))
if err != nil {
return nil, 0, errors.Trace(err)
}
httpStatusCode, err = svc.makeFunctionRequest(ctx, "GET", u.String(), nil, &httpResponseBody)
return httpResponseBody, httpStatusCode, errors.Trace(err)
}Demonstrates a path argument ({petId} in the OpenAPI route). The skill translates path placeholders into typed Go arguments — petId int64 here, derived from the OpenAPI schema’s format: int64. The handler URL-escapes the value before splicing it into the remote URL so a hostile input cannot inject path segments.
UploadFile
func (svc *Service) UploadFile(w http.ResponseWriter, r *http.Request) (err error) {
u := svc.remoteURL("/pet/" + url.PathEscape(r.PathValue("petId")) + "/uploadImage")
return svc.makeWebRequest(w, r, "POST", u)
}Demonstrates a web endpoint (non-JSON body, raw relay). The operation accepts an application/octet-stream body — an image upload — so the skill emitted a web handler that takes the raw http.ResponseWriter and *http.Request and delegates to makeWebRequest, which copies the request body and headers to the remote and streams the response back byte-for-byte. There is no parsing of the request body in the middle; the microservice is a transparent pipe.
Shared Helpers
func (svc *Service) makeFunctionRequest(ctx context.Context, method, rawURL string, in, out any) (status int, err error) {
var body io.Reader
if in != nil {
b, err := json.Marshal(in)
if err != nil {
return 0, errors.Trace(err)
}
body = bytes.NewReader(b)
}
req, err := http.NewRequest(method, rawURL, body)
if err != nil {
return 0, errors.Trace(err)
}
if in != nil {
req.Header.Set("Content-Type", "application/json")
}
svc.authenticate(req)
resp, err := httpegressapi.NewClient(svc).Do(ctx, req)
if err != nil {
return 0, errors.Trace(err)
}
defer resp.Body.Close()
if out != nil {
err = json.NewDecoder(resp.Body).Decode(out)
if err != nil {
return resp.StatusCode, errors.Trace(err)
}
}
return resp.StatusCode, nil
}Three things to notice:
- Outbound HTTP goes through
httpegressapi, never throughnet/httpdirectly. The skill wires this in automatically (addinghttpegress.NewService()to the app if it isn’t already there). That’s what gives the delegating microservice tracing, time-budget honoring, and a test mock for free. svc.authenticate(req)injects the bearer token on every outbound request. The generated code reads the credential from config via the typedBearerToken()accessor and never exposes it to upstream callers.- The remote status code is returned, not translated. A
404 Not Foundfrom the upstream Petstore comes back to the caller ashttpStatusCode = 404, not converted into a Go error. This preserves the byte-transparent delegation contract.
makeWebRequest is the same shape for raw web bodies, using httpx.Copy(w, resp) to transfer the response body without copying its bytes.
See Also
- OpenAPI-Delegating Microservices — the skill that generated this microservice. Walks through the prompt, the
openapispecs.jsondurable record, incremental endpoint import, and the closed-by-defaultrequiredClaimsposture. - HTTP Egress core service — the proxy that owns the actual outbound call.
- Browser example — a hand-written egress consumer, useful contrast against the generated style here.