Adding Swagger.json to GraphQL Middleware for Migrating SOAP to REST with NSwagStudio
Migrating from SOAP to REST can be a complex process, but GraphQL as a middleware provides a structured way to phase out SOAP services while keeping the system functional. To make this migration smoother, we can generate a Swagger.json file for the GraphQL middleware, allowing us to use NSwagStudio to generate type-safe proxy classes in C#, Python, Go, or TypeScript.
This approach helps bury the SOAP complexity while allowing UI and client code to interact with the RESTful GraphQL middleware using a generated proxy class.
Why Add Swagger.json to GraphQL Middleware?
- Enables automatic client SDK generation: UI and client apps can interact with the API using strongly-typed proxies.
- Abstracts SOAP complexity: Developers work with a clean REST-like API instead of legacy SOAP XML.
- Ensures consistency: Swagger.json provides a standardized API description.
- Allows for gradual migration: Legacy SOAP calls get replaced with REST incrementally while maintaining the generated API contract.
Step 1: Adding OpenAPI/Swagger Support to GraphQL Middleware
To generate a Swagger.json file for our GraphQL middleware, we need to expose the GraphQL queries and mutations via a RESTful OpenAPI specification.
1. Install Dependencies
For a Node.js/Express GraphQL middleware, install swagger-jsdoc and swagger-ui-express:
1
| npm install swagger-jsdoc swagger-ui-express
|
For a Python FastAPI-based GraphQL middleware:
1
| pip install fastapi-graphql pydantic
|
2. Define OpenAPI Specification for GraphQL
Create a swagger.js
file to generate OpenAPI documentation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| const swaggerJsDoc = require("swagger-jsdoc");
const swaggerUi = require("swagger-ui-express");
const swaggerOptions = {
definition: {
openapi: "3.0.0",
info: {
title: "GraphQL Middleware API",
version: "1.0.0",
description: "API gateway for transitioning SOAP to REST via GraphQL",
},
servers: [
{
url: "http://localhost:4000",
},
],
},
apis: ["./routes/*.js"],
};
const swaggerDocs = swaggerJsDoc(swaggerOptions);
module.exports = { swaggerDocs, swaggerUi };
|
Then integrate it into an Express.js GraphQL middleware:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
const { swaggerDocs, swaggerUi } = require("./swagger");
const app = express();
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocs));
const schema = buildSchema(`
type Query {
getUser(id: ID!): User
}
type User {
id: ID!
name: String
email: String
}
`);
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
graphiql: true,
})
);
app.listen(4000, () => console.log("Server running on port 4000"));
|
Now, hitting http://localhost:4000/api-docs
displays the OpenAPI documentation.
Step 2: Using NSwagStudio to Generate Proxy Classes
Once we have swagger.json
, we can use NSwagStudio to generate typed client proxies for C#, Python, Go, or TypeScript.
1. Generate Swagger.json
Export the Swagger definition using:
1
| curl -o swagger.json http://localhost:4000/api-docs
|
2. Open NSwagStudio
- Download and install NSwagStudio from NSwag GitHub.
- Open NSwagStudio and load
swagger.json
. - Select the desired output format (C#, Python, Go, TypeScript).
- Click Generate Client Code.
Step 3: Using the Generated Proxy Classes
C# Proxy Class (Generated)
1
2
3
4
5
6
7
8
9
10
| public class UserClient
{
private readonly HttpClient _httpClient;
public UserClient(HttpClient httpClient) { _httpClient = httpClient; }
public async Task<User> GetUserAsync(int id)
{
return await _httpClient.GetFromJsonAsync<User>($"/users/{id}");
}
}
|
Python Proxy Class (Generated)
1
2
3
4
5
6
7
8
9
| import requests
class UserClient:
def __init__(self, base_url):
self.base_url = base_url
def get_user(self, user_id):
response = requests.get(f"{self.base_url}/users/{user_id}")
return response.json()
|
TypeScript Proxy Class (Generated)
1
2
3
4
5
6
7
8
| export class UserClient {
constructor(private baseUrl: string) {}
async getUser(id: number): Promise<User> {
const response = await fetch(`${this.baseUrl}/users/${id}`);
return await response.json();
}
}
|
Conclusion
By adding Swagger.json to GraphQL middleware, we enable NSwagStudio to generate type-safe client proxies for multiple programming languages. This approach allows:
- Abstracting SOAP complexity behind GraphQL middleware.
- Preserving API contracts while transitioning to REST.
- Using strongly-typed client code instead of manual API requests.
- Enabling incremental SOAP-to-REST migration without breaking existing clients.
Example 1: Fetching a Person by ID
SOAP Request:
1
2
3
4
5
6
7
| <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<GetPersonRequest>
<personId>12345</personId>
</GetPersonRequest>
</soapenv:Body>
</soapenv:Envelope>
|
SOAP Response:
1
2
3
4
5
6
7
8
9
10
11
| <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<GetPersonResponse>
<person>
<id>12345</id>
<name>John Doe</name>
<age>30</age>
</person>
</GetPersonResponse>
</soapenv:Body>
</soapenv:Envelope>
|
Swagger-Generated REST Proxy (C#):
1
2
3
| var personClient = new PersonClient(httpClient);
Person person = await personClient.GetPersonAsync(12345);
Console.WriteLine(person.Name);
|
REST API Response (JSON):
1
2
3
4
5
| {
"id": "12345",
"name": "John Doe",
"age": 30
}
|
Example 2: Fetching a Person with Address
SOAP Request:
1
2
3
| <GetPersonWithAddressRequest>
<personId>12345</personId>
</GetPersonWithAddressRequest>
|
SOAP Response:
1
2
3
4
5
6
7
8
9
10
11
| <GetPersonWithAddressResponse>
<person>
<id>12345</id>
<name>John Doe</name>
<address>
<street>123 Main St</street>
<city>Springfield</city>
<zip>12345</zip>
</address>
</person>
</GetPersonWithAddressResponse>
|
Swagger-Generated REST Proxy (C#):
1
2
3
| var personClient = new PersonClient(httpClient);
Person person = await personClient.GetPersonWithAddressAsync(12345);
Console.WriteLine(person.Address.Street);
|
REST API Response (JSON):
1
2
3
4
5
6
7
8
9
| {
"id": "12345",
"name": "John Doe",
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}
|
Example 3: Fetching an Order with Order Items
SOAP Request:
1
2
3
| <GetOrderRequest>
<orderId>98765</orderId>
</GetOrderRequest>
|
SOAP Response:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <GetOrderResponse>
<order>
<id>98765</id>
<customerName>John Doe</customerName>
<items>
<item>
<name>Laptop</name>
<price>1000</price>
</item>
<item>
<name>Mouse</name>
<price>50</price>
</item>
</items>
</order>
</GetOrderResponse>
|
Swagger-Generated REST Proxy (C#):
1
2
3
| var orderClient = new OrderClient(httpClient);
Order order = await orderClient.GetOrderAsync(98765);
Console.WriteLine(order.CustomerName);
|
REST API Response (JSON):
1
2
3
4
5
6
7
8
| {
"id": "98765",
"customerName": "John Doe",
"items": [
{ "name": "Laptop", "price": 1000 },
{ "name": "Mouse", "price": 50 }
]
}
|
Pros and Cons
SOAP Pros:
- Strongly defined contracts via WSDL.
- Robust security mechanisms (WS-Security, WS-ReliableMessaging).
- Widely used in enterprise applications.
SOAP Cons:
- Verbose XML format compared to JSON.
- Slower performance due to larger payloads.
- Requires WSDL parsing and SOAP-specific libraries.
REST with Swagger-Generated Clients Pros:
- Lightweight JSON responses improve performance.
- Easier to integrate with modern applications.
- Can be used with GraphQL as a middleware.
- NSwagStudio generates type-safe client code.
REST with Swagger-Generated Clients Cons:
- Less security standardization (compared to WS-Security).
- REST can have less strict contracts than SOAP.
- Requires Swagger/OpenAPI documentation maintenance.
Alternative Approaches
Approach | Pros | Cons |
---|
GraphQL as Middleware | Flexible queries, incremental migration | Overhead of GraphQL layer |
Direct SOAP to REST Rewrite | Clean REST endpoints | High development effort |
SOAP-to-REST Adapters | Allows old SOAP clients to call REST | Requires adapter maintenance |
References