Connecting a SOAP/XML backend

Use an SDL-first approach to create a GraphQL API for backends with SOAP.

The custom directive @rest extends to SOAP services through postbody configuration. In this way, API Connect for GraphQL supports both REST structures and SOAP protocols. SOAP services vary a lot. Use this tutorial to learn how to use API Connect for GraphQL to create a temperature converter so you can apply your knowledge to other SOAP services.

Create your schema

Create a file called index.graphql and add the following code to it:

	type Query {
    conversion (celsius: Float): JSON
        @rest (
            endpoint: "https://www.w3schools.com/xml/tempconvert.asmx", 
            method: POST,
            postbody: """
                <?xml version="1.0" encoding="utf-8"?>
                <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  	            <soap12:Body>
                <CelsiusToFahrenheit xmlns="https://www.w3schools.com/xml/">
                <Celsius>{{ .Get "celsius" }}</Celsius>
                </CelsiusToFahrenheit>
  	            </soap12:Body>
                </soap12:Envelope>
                                  """
            headers: [{name:"Content-Type", value: "text/xml"},
                      {name: "Content-Type", value: "charset=utf-8"}]
            transforms: [{pathpattern: "[]", editor:"xml2json"}]
    )
}

The code tells API Connect for GraphQL to make the SOAP call using the @rest directive. The @rest directive supports the following arguments:

  • endpoint - connects the GraphQL API to the ASP.NET Web Service file
  • method - defines the HTTP method used, here, it's POST
  • postbody - defines the POST body, you can see how to pass a GraphQL argument into the body of the SOAP message with {{ .Get "celsius" }}
  • headers - adds appropriate headers; check with your own SOAP Backend on what headers it needs
  • transforms - transforms the response from XML to JSON using an editor xml2json (the pathpattern is always [] for xml2json)

Deploy your API

Run stepzen start inside your workspace, and pick an API name.

stepzen start
? What would you like your endpoint to be called? (api/prodding-seastar) 

You'll get a message in your terminal confirming successful deployment.

To test, run a command similar to the following.

 "https://<your-account-name>.stepzen.net/api/prodding-seastar/__graphql" --header "Authorization: Apikey $(stepzen whoami --apikey)" --header "Content-Type: application/json" --data '{"query": "{conversion (celsius: 20.0)}"}’ 

You will get a JSON response from your SOAP service with the CelsiusToFahrenheitResult result.

{
	"data": {
		"conversion": {
			"Envelope": {
				"Body": {
					"CelsiusToFahrenheitResponse": {
						"CelsiusToFahrenheitResult": "68",
						"xmlns": "https://www.w3schools.com/xml/"
					}
				},
				"soap": "http://www.w3.org/2003/05/soap-envelope",
				"xsd": "http://www.w3.org/2001/XMLSchema",
				"xsi": "http://www.w3.org/2001/XMLSchema-instance"
			}
		}
	}
}

Create GraphQL types with JSON-to-GraphQL

At this point, the entire JSON body is returned. To allow the API user to specify which pieces of data are returned from the query, you will need to add GraphQL types that reflect the structure of the SOAP service. You can manually create GraphQL types, or you can use the JSON-to-GraphQL tool to transform the response into the correct SDL (Schema Definition Language).

To do that, take the JSON response from the preceding section and paste it on the left side of JSON-to-GraphQL. Hit run, and copy the generated SDL into the bottom of your index.graphql file.

Modify your type Query in your index.graphql file to return Envelope as opposed to JSON for the query.

    ~~conversion (celsius: Float): JSON~~
    conversion (celsius: Float): Envelope

Next, add a resultroot: "Envelope" argument following your transforms argument. The resultroot directive specifies the path that API Connect for GraphQL will use as the root. Your index.graphql should look like the following example:

type Query {
    conversion (celsius: Float): Envelope
    @rest (endpoint: "https://www.w3schools.com/xml/tempconvert.asmx",
    method: POST,
    postbody: """
    <?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <CelsiusToFahrenheit xmlns="https://www.w3schools.com/xml/">
      <Celsius>{{ .Get "celsius" }}</Celsius>
    </CelsiusToFahrenheit>
  </soap12:Body>
</soap12:Envelope>
    """
    headers: [{name:"Content-Type", value: "text/xml"},
    {name: "Content-Type", value: "charset=utf-8"}],
    transforms: [{pathpattern: "[]", editor:"xml2json"}]
    resultroot: Envelope
    )
}
type CelsiusToFahrenheitResponse {
  CelsiusToFahrenheitResult: String
  xmlns: String
}
type Body {
  CelsiusToFahrenheitResponse: CelsiusToFahrenheitResponse
}
type Envelope {
  Body: Body
  soap: String
  xsd: String
  xsi: String
}
type Conversion {
  Envelope: Envelope
}
type Data {
  conversion: Conversion
}
type Root {
  data: Data
}

Run stepzen start once more, then run a command to verify your updated response.

 "https://public3cad390d353e929b.stepzen.net/api/prodding-seastar/__graphql" --header "Authorization: Apikey $(stepzen whoami --apikey)" --header "Content-Type: application/json" --data '{"query": "{conversion (celsius: 20.0){Body {CelsiusToFahrenheitResponse {CelsiusToFahrenheitResult}}}}"}'

You now have a GraphQL endpoint where you can select any field from the XML response via your GraphQL request.

{
	"data": {
		"conversion": {
			"Body": {
				"CelsiusToFahrenheitResponse": {
					"CelsiusToFahrenheitResult": "68"
				}
			}
		}
	}
}