REST vs SOAP vs GraphQL for Enterprise Integration: Contracts, Security, Performance and Where Each Fits

Madhu Nair
Practice Director
16 min read

SOAP is a contract-first XML messaging protocol suited to regulated, operation-centric ERP and HCM interfaces, while REST is a resource-oriented style over HTTP and the default for system APIs. GraphQL is a typed query language best used as an aggregation layer for consumers that need flexible data shapes over data that already lives behind REST and SOAP.

This article is written for integration architects, heads of enterprise applications and CIOs running more than 100 interfaces across ERP, HCM, CRM and data platforms, with notes for SME IT managers and startup CTOs. By the end, you will be able to decide which style belongs at each boundary, what to keep, wrap or add, and how to run all three without a rebuild.

Why does the API style decision matter in an enterprise integration estate?

The API style decision matters because it fixes the failure modes your operations team inherits. Chosen by habit instead of by boundary, each style fails predictably in production, and each failure arrives as business cost: a late payroll run, a stuck order batch, days of reconciliation or a security review finding.

Take an illustrative worked example: an order-to-cash step that makes 12 sequential REST calls at 150 ms each spends 1.8 seconds in round trips per order, so a 20,000-order batch needs 10 hours of serial time before a single retry. That arithmetic is why the choice between synchronous and asynchronous integration belongs in the same design review as the API style.

SOAP fails differently. When a consumer regenerates client stubs from a new vendor WSDL without regression testing, a renamed element or a newly required field breaks the interface on the first run after the release weekend. In payroll, that means a missed bank file cutoff and manual corrections.

GraphQL fails at the edge. An endpoint exposed without depth or cost limits lets one nested query fan out into thousands of backend calls, and an endpoint with introspection left on hands any caller a full map of your data model. Both surface as findings in the next penetration test.

Rule of thumb: choose the style per boundary, never per team preference.

How do REST, SOAP and GraphQL actually work?

SOAP wraps every message in an XML envelope governed by a WSDL contract, REST exposes resources whose behavior comes from standard HTTP method semantics, and GraphQL publishes one typed schema that clients query for exactly the fields they need. What differs is where the contract lives, how errors surface and how change is managed.

SOAP: contract-first XML messaging

Under the W3C SOAP 1.2 messaging framework, every message is an Envelope, the outermost XML element, containing an optional Header for metadata such as security tokens and a mandatory Body for the payload. The contract lives in a WSDL (Web Services Description Language) document, which declares operations, messages and endpoints, and in XSD (XML Schema Definition) files, which define every type.

Errors arrive as a SOAP Fault, a structured error element inside the Body. In SOAP 1.2 it carries a mandatory Code and Reason plus an optional Detail, and the Sender and Receiver codes tell you whether resending the same message could ever succeed. SOAP is transport independent through its protocol binding framework, so the same envelope can travel over HTTP or a message queue.

WS-Security, an OASIS standard, adds signing and encryption at the message level, so integrity survives intermediaries that terminate TLS. SOAP 1.2 changed the envelope namespace, the media type and the Fault structure, yet SOAP 1.1 remains common in vendor APIs.

REST: resources over HTTP

In REST, a resource is any entity addressable by a URI, and the verbs come from HTTP. Under RFC 9110 HTTP Semantics, GET is safe, and GET, PUT and DELETE are idempotent, meaning repeating the request has the same effect as sending it once. POST is not, so retried POSTs need an idempotency key. Status codes carry the outcome, so gateways read results without parsing bodies.

An ETag is an opaque validator for one version of a representation. Sending it back in an If-Match header makes a write conditional, and a 412 Precondition Failed response prevents a lost update. The contract is optional in REST but expected in the enterprise: the OpenAPI Specification, now at version 3.2.1, is the machine-readable description gateways import and code generators consume. For errors, problem details give every API one predictable JSON shape.

GraphQL: a typed query layer

GraphQL is a query language over a typed schema written in SDL (Schema Definition Language), a compact syntax for declaring types and fields. Queries read, mutations write and subscriptions push changes, all through a single endpoint, usually /graphql. Each field is produced by a resolver, a function that fetches its value from a database, a REST API or a SOAP service.

Introspection lets a client query the schema itself, which powers tooling and also exposes your data model. The GraphQL specification, whose latest edition is September 2025, defines the @deprecated directive, which marks a field or enum value as retired with a reason while existing queries keep working.

What does each one look like on the wire?

On the wire, SOAP posts an XML envelope to an operation endpoint, REST combines a method, a resource and headers with a status code that carries meaning, and GraphQL posts a query document and returns JSON in which data and errors can coexist. Each example below is followed by what an integration engineer should notice.

POST /ccx/service/{tenant}/Human_Resources/{WWS_VERSION} HTTP/1.1
Host: {workday-services-host}
Content-Type: text/xml; charset=UTF-8

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:bsvc="urn:com.workday/bsvc">
  <soapenv:Header>
    <wsse:Security soapenv:mustUnderstand="1"
        xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken>
        <wsse:Username>ISU_INTEGRATION@{tenant}</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{secret-from-vault}</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <bsvc:Get_Workers_Request bsvc:version="{WWS_VERSION}">
      <bsvc:Request_Criteria>
        <bsvc:Exclude_Inactive_Workers>true</bsvc:Exclude_Inactive_Workers>
      </bsvc:Request_Criteria>
      <bsvc:Response_Filter>
        <bsvc:Page>1</bsvc:Page>
        <bsvc:Count>100</bsvc:Count>
      </bsvc:Response_Filter>
      <bsvc:Response_Group>
        <bsvc:Include_Personal_Information>true</bsvc:Include_Personal_Information>
      </bsvc:Response_Group>
    </bsvc:Get_Workers_Request>
  </soapenv:Body>
</soapenv:Envelope>

The UsernameToken authenticates an integration system user, and because PasswordText carries the secret inside the envelope, it depends entirely on TLS and a vaulted credential. Workday versions its web services per release, and the version appears in both the endpoint path and the version attribute; when both are present, the message version wins, so pin it and confirm it against the Workday API reference before each release. Failures return a SOAP Fault, typically on HTTP 500, so route on the fault code. In the Workday integration programs we support, pinning the version per interface is the cheapest control against release-weekend breakage.

GET /api/v2/customers/C-10482 HTTP/1.1
Host: api.example.com
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
ETag: "c-10482-v17"

{ "id": "C-10482", "name": "Example Supply Co", "creditLimit": 250000, "currency": "USD" }

PATCH /api/v2/customers/C-10482 HTTP/1.1
Host: api.example.com
Content-Type: application/merge-patch+json
If-Match: "c-10482-v17"

{ "creditLimit": 300000 }

HTTP/1.1 412 Precondition Failed
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/stale-representation",
  "title": "Resource was modified by another client",
  "status": 412,
  "detail": "Customer C-10482 is now at version v18. Re-read and reapply the change.",
  "instance": "/api/v2/customers/C-10482"
}

The ETag turns an unsafe PATCH into a safe concurrent write: if another process changed the customer since the read, the server refuses with 412 instead of silently overwriting. The body follows RFC 9457 problem details, served as application/problem+json with type, title, status, detail and instance members, so every consumer parses one error shape. A 412 is not retryable as sent; the flow must re-read, reapply and resend.

POST /graphql HTTP/1.1
Content-Type: application/json
Accept: application/graphql-response+json, application/json;q=0.9

{
  "query": "query WorkerSummary($id: ID!) { worker(id: $id) { name costCenter { code } openRequisitions { id title } } }",
  "variables": { "id": "W-2231" }
}

HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "worker": {
      "name": "Dana Ortiz",
      "costCenter": { "code": "CC-410" },
      "openRequisitions": null
    }
  },
  "errors": [
    {
      "message": "Recruiting service timed out",
      "path": ["worker", "openRequisitions"],
      "extensions": { "code": "UPSTREAM_TIMEOUT" }
    }
  ]
}

The payload shape is the lesson. The response carries usable data and an errors array at the same time, with path pointing at the field that failed, and a legacy server returns it on HTTP 200, so an integration that checks only the status code records a success; treat any non-empty errors array as a failure for alerting. For system consumers, use persisted queries: the server stores approved operations and the client sends only an identifier or hash, which lets the gateway allowlist operations and serve cacheable reads over GET.

How do REST, SOAP and GraphQL compare for enterprise integration?

For enterprise integration, SOAP leads on formal contracts and message-level security, REST leads on simplicity, tooling and HTTP caching, and GraphQL leads on consumer-shaped reads across many sources. Read the table as a placement guide, not a scorecard.

Two rows decide most outcomes. Versioning determines how often consumers break, and a deliberate approach to API versioning strategies for enterprise integrations matters more than the style itself.

Platform support determines operating cost. Azure API Management supports GraphQL APIs in all service tiers, although synthetic GraphQL APIs are not currently supported in workspaces. Weigh limits like these when you compare enterprise integration platforms and gateways.

Dimension SOAP REST GraphQL
Contract and schema WSDL and XSD, mandatory OpenAPI, expected SDL schema, mandatory
Transport Binding independent HTTP HTTP, WebSocket for subscriptions
Payload format XML envelope Usually JSON JSON
Error handling SOAP Fault Status code plus problem details errors array beside partial data
Security model TLS plus WS-Security TLS, OAuth 2.0, mutual TLS TLS, OAuth 2.0, depth and cost limits
Caching Rare, POST only Native HTTP caching, ETags Hard; persisted queries help
Versioning and evolution New WSDL version per release URI or header versions Additive, with @deprecated
Reliability and transactions WS-ReliableMessaging where supported Idempotent methods and keys No cross-call transactions
iPaaS and gateway support Broad, with mediation Universal Growing, uneven
Best-fit boundary Vendor system of record System and process APIs Aggregation layer

Choose SOAP when the vendor publishes it as the authoritative interface, when a counterparty requires message-level signing, or when the operation is a business transaction such as a hire rather than a data read. Do not build new SOAP services for internal consumers.

Choose REST for system and process APIs, for anything an iPaaS or partner will consume, and wherever HTTP caching and idempotent retries reduce operating risk. It is the default unless a boundary gives a specific reason otherwise.

Choose GraphQL when three or more consumers need different shapes of the same data and the alternative is a growing set of backend-for-frontend endpoints. It does not fit bulk extraction or file loads; use batch APIs or events there.

Where does each style fit in a real integration estate?

SOAP stays at system-of-record boundaries where the vendor publishes it, REST is the default for system and process APIs, and GraphQL is an optional aggregation layer on top for portals, mobile apps and internal tools. An integration layer or API gateway mediates between the layers, so no consumer has to speak all three.

Workday Web Services, older ERP interfaces and many banking connections publish SOAP, and the vendor owns those contracts, so you keep them. Above those systems of record, system APIs expose each system’s data through a stable REST contract, and process APIs, which orchestrate several system APIs into one business step such as hire-to-provision, are REST as well.

GraphQL, where it earns its place, sits on top, serving consumers such as an employee portal that combines HCM, ITSM and finance data. It reads through the REST and SOAP layers, never around them.

Protocol mediation makes this work without a rebuild. A sound integration layer architecture keeps the vendor’s protocol at the vendor’s boundary and translates once, never in every consumer. Azure API Management can import a WSDL and expose its operations as REST, and in MuleSoft Anypoint Platform the Web Service Consumer connector calls the SOAP operation while DataWeave maps XML to JSON. Infor ION API Gateway plays a similar role for Infor CloudSuite, publishing REST endpoints behind OAuth 2.0.

Federating several REST APIs behind one GraphQL schema is mediation one layer up. Synthetic GraphQL in Azure API Management maps schema fields to HTTP resolvers backed by REST or SOAP APIs. The hard part is semantic: if Worker means three different things in three backends, the schema exposes the conflict, which is why a canonical data model for integration teams should exist before the federated schema does.

Where gateway policy cannot express a mapping, such as stateful aggregation or compensation logic, a thin mediation service becomes custom integration development work. If your estate has fewer than 20 interfaces and one frontend, skip the GraphQL layer entirely.

Diagram: a layered stack with systems of record (Workday, ERP, banking) at the bottom, REST system APIs above them, REST process APIs above those, and an optional GraphQL aggregation layer on top for portals, apps and internal tools, with an API gateway on the right side spanning every layer, labeled authentication, mediation and monitoring.

What breaks in production with each style?

Each style has a signature production failure: SOAP breaks on contract drift and payload size, REST breaks on call volume and inconsistent conventions, and GraphQL breaks on unbounded queries and errors hidden inside successful responses. That signature tells you what to monitor first.

In the Workday estates we support, the most common post-release breakage is WSDL drift: a consumer regenerated against the newest WSDL, or a strict XSD validator rejected an element the vendor added. Large XML responses also create memory pressure in iPaaS runtimes that build a full document tree, so stream and page instead. For attachments, MTOM (Message Transmission Optimization Mechanism) sends binary data as MIME parts, avoiding the roughly 33 percent size increase of inline base64.

REST breaks on chatty call chains, inconsistent pagination across APIs and error envelopes that differ by team. Under load, providers return 429 Too Many Requests with a Retry-After header; clients that ignore it and retry immediately extend the throttle for every interface sharing the quota.

GraphQL breaks on N+1 resolver calls, where a list query triggers one backend call per item, so batch them with a DataLoader-style loader. The OWASP GraphQL Cheat Sheet recommends depth limits, amount limits, query cost analysis and disabling introspection in production. HTTP caching is also harder, because most queries arrive as POST to one URL.

The quietest failure is HTTP 200 with errors, which hides partial failures from status-based monitoring. The GraphQL over HTTP working draft changes this when the application/graphql-response+json media type is used: validation failures return 4xx codes such as 422, and responses carrying both data and errors should use a custom 294 status. Until your gateway and clients support it, alert on the errors array.

How does the choice change for enterprises, SMEs and startups?

The layering stays the same at every size; what changes is how much governance you can afford. Enterprises standardize the boundaries and police them, SMEs standardize on one style and wrap the exceptions, and startups defer complexity until a customer or a second frontend forces it.

Enterprises need governance more than new protocols. Register every WSDL, OpenAPI description and GraphQL schema in a contract registry, enforce authentication, quotas and schema validation as gateway policy, and keep vendor SOAP where the vendor publishes it, wrapping it rather than replacing it. Record the chosen style and reason for every new boundary.

SMEs should standardize on REST with OpenAPI and wrap the one or two SOAP systems, often payroll or an older ERP, behind a REST facade in the gateway. GraphQL can wait until a real consumer need appears, since one gateway and one contract format keep support predictable.

Startups should ship REST and webhooks first and add GraphQL only when several frontends need different shapes of the same data. Prepare early for the first enterprise customer, who may require a SOAP interface, signed messages or a security questionnaire covering API authentication and rate limiting.

Which metrics show your API style choices are working?

Four KPIs show whether your API style choices are working, and each can be baselined from gateway logs, iPaaS run history and your contract registry. Measure for 30 days before any change, then track monthly.

  • Calls per business transaction: count the API calls one business event triggers, such as a hire or an order, using correlated trace IDs. A rising number signals chatty design.
  • p95 latency per process: measure 95th percentile end-to-end time for each process API, not each endpoint, because SLAs apply to the process.
  • Consumer breakages per vendor release: count incidents caused by vendor contract changes in the two weeks after each Workday or ERP release.
  • Contract coverage: divide interfaces with a published WSDL, OpenAPI description or GraphQL schema by total interfaces, and target full coverage.

Frequently asked questions

Is SOAP still used in enterprise integration?

Yes. Major HCM, ERP and banking platforms still publish SOAP as their most complete interface, and Workday Web Services is the best-known example. Retiring a vendor’s SOAP API is not your decision. Keep it at the system-of-record boundary and expose REST above it.

Can GraphQL replace REST for system-to-system integration?

Rarely. System-to-system flows need idempotent writes, predictable status codes, HTTP caching and bulk transfer, which REST, events and file patterns handle better. Use GraphQL as an aggregation layer over REST, not as the transport between your ERP and your data platform.

Is SOAP more secure than REST?

Not inherently. SOAP with WS-Security offers message-level signing and encryption that survives intermediaries, which some regulated exchanges require. REST relies on TLS, OAuth 2.0 and mutual TLS, which are equally strong for point-to-point calls. Configuration and credential handling matter far more than the protocol.

How do you expose a SOAP service as REST or GraphQL without rewriting it?

Put a mediation layer in front of it. Azure API Management can import the WSDL and expose operations as REST, or an iPaaS can map XML to JSON. For GraphQL, configure resolvers that call the REST facade or the SOAP endpoint directly. The backend stays unchanged.

How do you version GraphQL compared with REST?

REST APIs usually version explicitly, with a new URI segment or header value for breaking changes. GraphQL evolves one schema additively: add fields freely, mark old ones with the @deprecated directive, monitor field usage, then remove them once traffic reaches zero. Both need a deprecation window.

The practical next step is an audit, not a platform decision. List every interface and record which boundary it sits on, whether system of record, system API, process API or experience layer, along with its current style and whether it has a published contract. That inventory shows which SOAP interfaces to keep, which to wrap, where REST conventions need standardizing and whether any consumer group justifies a GraphQL layer.

;