SOAP in GO: Beyond the Happy Path
Because integrating SOAP isn’t always just generate and go.
SOAP in GO: Beyond the Happy Path
Because integrating SOAP isn’t always just generate and go.

Introduction
I recently worked on a project that required integrating a SOAP service using Go. I’d done this before, successfully and without drama. Generate stubs from a WSDL, wire up a client, call the methods, done. Clean. Predictable. Almost relaxing.
This time, though, SOAP decided to remind me why it still has a reputation.
The challenge wasn’t SOAP itself. It wasn’t Go either. It was a WSDL that looked innocent but quietly referenced other WSDLs and XSDs. That’s where things stopped being straightforward.
This article walks through:
- How SOAP integration usually works in Go
- Why WSDLs that reference other WSDLs are tricky
- How this compares to Java’s SOAP ecosystem
- What I ended up doing to make it work
- Lessons learned if you ever find yourself here
A Quick SOAP Refresher
SOAP is XML-based, contract-first, and heavily schema-driven. The contract lives in a WSDL (Web Services Description Language), which defines:
- Operations
- Request/response structures
- Bindings endpoints
In practice, integration usually looks like this:
WSDL → generate stubs → call methods → parse response
When that flow holds, life is good.
A SOAP Integration That Went Smoothly
Before this project, I had integrated another SOAP service in Go that was refreshingly simple.
- Single WSDL
- No imports
- No external schemas
- Everything self-contained
The workflow was exactly what you’d expect.
Step 1: Generate stubs
go install github.com/hooklift/gowsdl/cmd/gowsdl@latest
gowsdl simple_service.wsdl
Step 2: Use the generated client
client := NewSimpleServicePortType(
"https://soap.example.com/simple",
http.DefaultClient,
)
resp, err := client.GetAccount(ctx, &GetAccountRequest{
AccountID: "12345",
})
That was it. No surprises. No archaeology.
This Time: The WSDL Had Opinions
This project’s WSDL looked fine at first glance:
<definitions ...>
<types>
<xsd:schema>
<xsd:import namespace="http://example.com/common"
schemaLocation="common.xsd"/>
</xsd:schema>
</types>
</definitions>
Then common.xsd referenced another schema.
Which referenced another.
And somewhere down the chain, another WSDL.
The Real Structure Looked More Like This
main.wsdl
├── common.xsd
│ ├── identifiers.xsd
│ └── addresses.xsd
└── other.wsdl
└── other-types.xsd
This is perfectly valid SOAP. It’s also where Go tooling starts to struggle.
The Core Problem: Linked WSDLs and Schemas
Go can generate SOAP stubs from WSDLs. That part works.
The issue appears when:
- WSDLs reference other WSDLs
- Schemas are split across multiple files
- Imports assume specific resolution paths
- Relative URLs don’t resolve cleanly
Many Go SOAP generators:
- Expect a mostly self-contained WSDL
- Don’t recursively resolve imports well
- Fail silently or generate incomplete structs
I couldn’t find a tool that handled this cleanly out of the box.
Why This Is Easier in Java
This is where Java quietly flexes.
With tools like **wsimport** (JAX-WS), Java:
- Resolves linked WSDLs automatically
- Follows schema imports recursively
- Generates a complete, type-safe client
- Handles SOAP headers and bindings gracefully
wsimport -keep -p com.example.soap main.wsdl
Five seconds later, you’re calling methods.
In Go, you earn it.
What I Ended Up Doing
Instead of fighting the tooling, I changed strategy.
Step 1: Treat SOAP as XML, Not Magic
In this project, I did not rely on a SOAP framework or a fully generated client to manage SOAP behavior. Instead, I leaned into Go’s strengths:
- Explicit structs
- Manual control
- Clear data flow
I started by defining the SOAP envelope myself.
type Envelope struct {
XMLName xml.Name `xml:"soapenv:Envelope"`
SoapEnv string `xml:"xmlns:soapenv,attr"`
Body Body
}
type Body struct {
XMLName xml.Name `xml:"soapenv:Body"`
Content any
}
Simple. Predictable. No surprises.
Step 2: Define the Contract Manually
From the WSDL, I extracted only what I actually needed and defined request structs explicitly:
type SampleRequest struct {
XMLName xml.Name `xml:"getSampleData"`
Username string `xml:"username"`
Password string `xml:"password"`
Code string `xml:"code"`
}
Step 3: Build a Thin SOAP Client
Instead of a massive generated client, I wrote a focused SOAP caller:
func callSOAP(endpoint string, payload any, resp any) error {
envelope := Envelope{
SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
Body: Body{Content: payload},
}
data, _ := xml.Marshal(envelope)
req, _ := http.NewRequest("POST", endpoint, bytes.NewReader(data))
req.Header.Set("Content-Type", "text/xml; charset=utf-8")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return xml.NewDecoder(res.Body).Decode(resp)
}
No frameworks. No surprises. Just XML in, XML out.
Handling Responses (and SOAP Faults)
SOAP loves wrapping everything in envelopes — including errors.
Instead of fighting that, I embraced it:
- Read the raw XML on non-200 responses
- Log it verbatim
- Store it for debugging
This saved me hours later when authentication issues or schema mismatches cropped up.
Final Thoughts
SOAP is still around and it rewards precision over creativity.
When a service exposes a single, self-contained WSDL, integrating it in Go is fairly straightforward. You generate the stubs, call the methods, and move on. I’ve done this before, and it worked exactly as expected.
Things change when that WSDL starts referencing other WSDLs. At that point, Go tooling still gets the job done, but it makes you slow down and get more hands-on. Not because it can’t handle it, but because most tools assume simpler, standalone contracts.
So if you’re planning to integrate a SOAP service in Go, take a look at the WSDL first. If it pulls in other files, be prepared to spend some time understanding and shaping the integration yourself.
Happy coding!
메타데이터
- post_id
- dbc8b5cabe88
- slug
- soap-in-go-beyond-the-happy-path-dbc8b5cabe88
- url
- https://medium.com/@alekske/soap-in-go-beyond-the-happy-path-dbc8b5cabe88
- canonical_url
- https://medium.com/@alekske/soap-in-go-beyond-the-happy-path-dbc8b5cabe88
- author_url
- https://medium.com/@alekske
- status
- ok
- fetched_at
- 2026-07-13 06:23:13