migrationsoap-to-restapi-designtutorial

Migrate from SOAP to REST: A Practical Step-by-Step Guide for 2026

SOAPless Team7 min read

Migrating from SOAP to REST is one of the most impactful modernization efforts an engineering team can undertake. SOAP services are verbose, hard to integrate with modern frontend frameworks, and increasingly difficult to find developers willing to maintain. But a migration done poorly can be worse than no migration at all — broken integrations, data inconsistencies, and months of parallel maintenance.

This guide covers a practical, step-by-step approach that minimizes risk and keeps your system running throughout the transition.

Before You Start: Assess What You Have

Inventory Your SOAP Services

Before writing any code, document every SOAP service your system depends on:

  1. List all WSDL endpoints your applications consume
  2. Map operations to business functions — which SOAP calls support which features?
  3. Identify consumers — which applications, teams, or external partners call each service?
  4. Check operation frequency — high-traffic operations need the most careful migration
  5. Document authentication methods — WS-Security, Basic Auth, client certificates?
# Quick way to extract operations from a WSDL
curl -s "https://api.example.com/Service?wsdl" | \
  grep -oP 'operation name="\K[^"]+' | sort -u

Evaluate Whether You Should Migrate at All

Not every SOAP service needs to become a REST API. Ask these questions:

  • Is the SOAP service under your control? If it's a third-party service you consume, you can't change it — but you can put a translation layer in front of it.
  • Is the service stable? If it hasn't changed in years and works reliably, the cost of migration may not be justified.
  • Are there active consumers who expect SOAP? External partners using your SOAP API need advance notice and migration support.

Strategy 1: The Strangler Fig Pattern

The safest migration approach is the Strangler Fig pattern — you build new REST endpoints alongside the existing SOAP service and gradually redirect traffic until the SOAP service can be decommissioned.

Phase 1: Build a REST Facade

Create REST endpoints that internally call the existing SOAP service:

// New REST endpoint that wraps the existing SOAP call
app.get("/api/v1/users/:id", async (req, res) => {
  try {
    // Call the existing SOAP service internally
    const soapResponse = await soapClient.GetUserAsync({
      userId: parseInt(req.params.id),
    });

    // Transform to JSON response
    res.json({
      id: soapResponse.userId,
      name: soapResponse.userName,
      email: soapResponse.userEmail,
    });
  } catch (error) {
    // Translate SOAP faults to HTTP status codes
    if (error.message.includes("UserNotFound")) {
      return res.status(404).json({ error: "User not found" });
    }
    res.status(500).json({ error: "Internal server error" });
  }
});

This gives consumers a REST interface immediately without changing the backend.

Phase 2: Migrate Business Logic

Once the REST facade is stable, move the actual business logic from the SOAP service into the REST layer:

// Before: REST -> SOAP -> Database
// After: REST -> Database directly
app.get("/api/v1/users/:id", async (req, res) => {
  const user = await db.users.findById(parseInt(req.params.id));
  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }
  res.json(user);
});

Phase 3: Redirect and Decommission

Once all consumers have switched to the REST endpoints and the REST layer no longer depends on the SOAP service:

  1. Monitor SOAP traffic logs for any remaining callers
  2. Set a decommission date and communicate it
  3. Return 410 Gone from the SOAP endpoint for a grace period
  4. Shut down the SOAP service

Strategy 2: The Proxy Approach

If you consume SOAP services you don't control (third-party APIs, partner services, legacy systems maintained by other teams), you can't migrate the service itself. Instead, put a SOAP-to-REST proxy in front of it.

The proxy translates between REST/JSON on the consumer side and SOAP/XML on the service side:

Your App  →  JSON/REST  →  Proxy  →  SOAP/XML  →  Legacy Service
                                  ←  JSON  ←         ←  XML  ←

You can build this proxy yourself or use a purpose-built tool. SOAPless is designed exactly for this scenario — point it at a WSDL URL, and it generates REST endpoints that handle the SOAP translation automatically. This is particularly effective when you need to migrate quickly or when the SOAP service has a large number of operations.

Designing Your REST API

When creating REST endpoints to replace SOAP operations, follow these design principles:

Map Operations to Resources

SOAP is operation-centric (GetUser, CreateUser, DeleteUser). REST is resource-centric. Group related operations around resources:

SOAP OperationREST Endpoint
GetUserGET /api/users/{id}
CreateUserPOST /api/users
UpdateUserPUT /api/users/{id}
DeleteUserDELETE /api/users/{id}
SearchUsersGET /api/users?name=alice
GetUserOrdersGET /api/users/{id}/orders

Handle SOAP Faults as HTTP Status Codes

SOAP faults should map to appropriate HTTP status codes:

function soapFaultToHttpStatus(fault) {
  const mapping = {
    "soap:Client": 400,       // Bad request
    "soap:Server": 502,       // Bad gateway (upstream error)
    "AuthenticationFailed": 401,
    "AuthorizationFailed": 403,
    "ResourceNotFound": 404,
    "RateLimitExceeded": 429,
  };

  return mapping[fault.faultcode] || 500;
}

Version Your API from Day One

/api/v1/users/{id}

Versioning gives you room to evolve the REST API without breaking consumers. This is especially important during migration, when you may discover that the initial REST interface doesn't perfectly represent the underlying data model.

Testing Strategy

Run SOAP and REST in Parallel

During migration, run both interfaces simultaneously and compare responses:

async function compareResponses(userId) {
  const [soapResult, restResult] = await Promise.all([
    soapClient.GetUserAsync({ userId }),
    fetch(`/api/v1/users/${userId}`).then((r) => r.json()),
  ]);

  // Deep comparison
  assert.deepStrictEqual(
    normalizeUser(soapResult),
    normalizeUser(restResult),
    `Mismatch for user ${userId}`
  );
}

Contract Testing

Use tools like Pact or Dredd to verify that your REST API conforms to its OpenAPI spec. This catches regressions when you refactor the translation layer.

Load Testing

SOAP and REST have different performance characteristics. SOAP envelopes are larger (XML vs. JSON), but SOAP services sometimes batch operations differently. Load test your REST endpoints to ensure they handle the same traffic levels as the SOAP service.

Common Pitfalls

1. Losing SOAP Fault Detail

SOAP faults can carry structured detail elements with business-specific error information. Don't reduce everything to a generic 500 error:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Email address is invalid",
    "details": [
      { "field": "email", "reason": "Missing @ symbol" }
    ]
  }
}

2. Ignoring Stateful Operations

Some SOAP services maintain session state across calls (via SessionId headers or WS-ReliableMessaging). REST is stateless by design. You'll need to decide how to handle this — either maintaining state server-side in the REST layer or redesigning the flow.

3. Underestimating Type Differences

SOAP's xs:decimal has arbitrary precision. JSON's number is an IEEE 754 double. Financial calculations that depend on decimal precision can silently lose accuracy when converted to JSON numbers. Use string representations for precise decimal values:

{ "amount": "1234.56", "currency": "USD" }

4. Breaking External Consumers

If external partners consume your SOAP API, they need time and support to migrate. Provide:

  • A clear timeline (minimum 6 months notice)
  • Migration documentation
  • A sandbox environment with the new REST API
  • Direct engineering support during their transition

Timeline Expectations

A realistic timeline for a SOAP-to-REST migration:

PhaseDurationActivities
Assessment1-2 weeksInventory, dependency mapping
Design1-2 weeksREST API design, OpenAPI spec
Facade/Proxy2-4 weeksREST endpoints wrapping SOAP
Parallel testing2-4 weeksSide-by-side validation
Consumer migration4-12 weeksExternal partner transitions
Decommission2-4 weeksTraffic monitoring, shutdown

For a faster path, a SOAP-to-REST proxy like SOAPless can compress the Facade phase from weeks to hours. You still need the assessment, testing, and consumer migration phases, but the translation layer is handled for you.

Final Advice

Start with the lowest-risk, highest-value service first. Get the migration process right on a single service before scaling to the rest. Document everything — the next team to do this migration will thank you.