For REST, OpenAPI, and GraphQL with first-class Legacy Support for XML, SOAP, and WSDL
Built on the Java platform, Membrane bridges legacy and modern APIs. It supports XML-to-JSON transformation, WSDL-to-OpenAPI conversion, SOAP-to-REST integration, and validation against OpenAPI and WSDL.
For modern APIs, Membrane supports technologies such as OAuth 2, JWT, and AI, along with a broad range of routing, transformation, and observability features. It is easy to set up and deploy, either as a container or as a Java application on a virtual machine.
Run Membrane as a container or as a Java application:
docker run --rm -it -p 2000:2000 predic8/membraneOpen these URLs in your browser to access sample APIs:
- http://localhost:2000 (Actual time)
- http://localhost:2000/api-docs (API deployed from OpenAPI)
Or call an API from the command line:
curl http://localhost:2000/shop/v2/productsCreate a file apis.yaml with the following content:
api:
port: 2000
target:
url: https://apibin.ioStart Membrane with your configuration:
Linux/macOS:
docker run --rm -p 2000:2000 -v "$(pwd)/apis.yaml:/opt/membrane/conf/apis.yaml" predic8/membraneWindows PowerShell:
docker run --rm -p 2000:2000 -v "${PWD}/apis.yaml:/opt/membrane/conf/apis.yaml" predic8/membraneRequests to http://localhost:2000 are now forwarded to https://apibin.io.
- Download the Membrane distribution
- Unzip
- Open tutorials/getting-started/10-First-API.yaml in your text editor and follow the instructions.
From OpenAPI and OAuth to SOAP, XML, LLMs, and MCP, Membrane bridges modern APIs and enterprise integration.
Deploy APIs directly from OpenAPI documents, validate messages against them, and even generate OpenAPI specifications from legacy WSDL. In addition to OpenAPI 3.0, and 3.1, Membrane also supports OpenAPI 3.2.
wsdl2openapi transforms a Web Service's WSDL into an OpenAPI and uses the underlying XSD schema for the conversion between XML and JSON. Deploy a WSDL, and Membrane exposes the service as an API with an OpenAPI description.
XML and JSON are deeply integrated into Membrane. XPath and JSONPath expressions provide direct access to message data for routing, filtering, and transformation.
Templates and XSLT allow for flexible message transformation and SOAP-to-REST conversion.
Secure legacy services with WSDL and XSD message validation, XML message protection, and XML signatures.
Validate messages against API and service specifications. Don't let invalid messages slip into your organization.
Orchestrate calls to external APIs and process collections with loops and conditional flows.
Take a look at the samples below and the tutorials to see what just a few lines of configuration can do.
When you need more flexibility, extend Membrane with expressions and scripting using JSONPath, XPath, Groovy, or SpEL, or write your own plugin in Java. In most cases, custom Java code is not necessary.
Although Membrane is written in Java, it delivers high performance with a low memory footprint. HTTP streaming, Keep-Alive, and non-blocking processing enable efficient resource utilization and high throughput. The Membrane distribution is only about 55 MB, making it smaller than many other API gateways.
On a single server Membrane can process 39,000 requests per second. However, raw throughput benchmarks often measure only simple proxying without message protection or transformation.
Membrane is implemented entirely in Java, from the HTTP engine to OpenAPI processing. This avoids the overhead of crossing between a native proxy core and a separate scripting runtime for plugins.
As a result, Membrane can maintain high performance even when multiple plugins for validation, security, and transformation are active. What matters is not performance in reduced benchmark setups, but performance under realistic gateway configurations.
- Expose and protect APIs for partners over the public Internet.
- Secure APIs with OAuth 2.0, JWT, API keys, TLS, and message validation.
- Modernize legacy services by integrating SOAP, XML, and WSDL with REST, JSON, and OpenAPI.
- Transform messages between JSON, XML, SOAP, HTTP headers, query parameters, and other formats.
- Route and control traffic with flexible rules, rate limiting, load balancing, and conditional processing.
- Use Membrane as an outgoing gateway to control access to partner and public APIs.
- Observe API traffic with logging, metrics, Prometheus, and OpenTelemetry tracing.
- Use Membrane as an AI Gateway for LLM providers and MCP servers.
- Replace maintenance-intensive Backend for Frontend (BFF) services with declarative gateway configuration where appropriate.
- Embed Membrane into your own Java applications and products.
- Deploy Membrane in containers, virtual machines, private clouds, or public clouds.
For a quick overview of what you can do with Membrane, the sections below provide a selection of short examples and configuration snippets.
- OpenAPI Deployment, Validation and Swagger UI
- Legacy Web Services with SOAP and WSDL
- AI and LLM Gateway
- Routing
- Message Transformation
- Orchestration and Call Outs
- Scripting
- Security
- Traffic Control
- Operation
OpenAPI is a native feature in Membrane. The gateway supports OpenAPI 3.0, 3.1, and 3.2, including the QUERY HTTP method.
Membrane can deploy an API directly from an OpenAPI description. It uses the defined paths, schemas, operations, and backend URLs to configure routing and, optionally, validate incoming requests and outgoing responses.
api:
port: 2000
openapi:
- location: openapi/fruitshop-v2-2-0.oas.yml
validateRequests: trueMembrane lets you explore APIs deployed from OpenAPI APIs in a single overview page.
For documentation and testing the gateway hosts also a Swagger UI for the deployed APIs.
See: OpenAPI tutorial
Integrate and modernize legacy SOAP web services.
Membrane can expose a SOAP web service described by WSDL as a REST API with an OpenAPI specification.
This configuration:
api:
port: 2000
flow:
- wsdl2openapi:
wsdl: mocks/partner.wsdl
operations:
getPartners:
method: GET
path: /partners
tag: Partner
getPartner:
method: GET
path: /partners/{id}
tag: Partner
createPartner:
method: POST
path: /partners
tag: Partner
updatePartner:
method: PUT
path: /partners/{id}
tag: Partner
deletePartner:
method: DELETE
path: /partners/{id}
tag: Partner
target:
url: http://localhost:3000/partner-serviceturns this WSDL into an OpenAPI specification including JSON schemas derived from the WSDL's XSD schemas:
After deploying this configuration, clients can send JSON requests to the REST API. Membrane transforms them into SOAP requests for the backend Web Service and converts the XML responses back to JSON.
The conversion uses the XML Schema definitions from the WSDL to map data precisely between JSON and XML.
The easiest way to expose a SOAP Web Service as a REST API is to use the wsdl2openapi plugin described above.
For more control over the conversion, you can define the REST API manually and map individual REST endpoints to SOAP operations.
The following configuration accepts a request such as GET /cities/Tokio, creates a SOAP request for the backend service, and transforms the SOAP response into JSON:
api:
port: 2000
method: GET
path:
uri: /cities/{city}
flow:
- request:
- soapBody:
src: |
<getCity xmlns="https://predic8.de/cities">
<name>${pathParam.city}</name>
</getCity>
- setHeader:
name: SOAPAction
value: https://predic8.de/cities/get
- response:
- template:
contentType: application/json
src: |
{
"country": ${xpath('//country')},
"population": ${xpath('//population')}
}
target:
# Change method to POST
method: POST
url: https://www.predic8.de/city-serviceNote: Membrane automatically escapes expression values such as ${xpath(...)} for the specified content type.
See the SOAP to REST tutorial for more details.
Membrane can create a SOAP proxy directly from a WSDL:
soapProxy:
port: 2000
wsdl: https://www.predic8.de/city-service?wsdlAfter startup, Membrane exposes:
- A SOAP endpoint at http://localhost:2000/city-service
- A rewritten WSDL at http://localhost:2000/city-service?wsdl
The validator checks SOAP messages against a WSDL document including referenced XSD schemas.
soapProxy:
port: 2000
wsdl: https://www.predic8.de/city-service?wsdl
flow:
# Validates SOAP messages against the WSDL and XSDs
- validator: {}Legacy SOAP services protected with Web Services Security (WS-Security) may require credentials and an XML Signature.
api:
port: 2000
flow:
- wsSecurity:
secure:
- usernameToken:
username: alice
password: secret
- signature:
references:
- by: BODY
- by: USERNAME_TOKEN
target:
url: http://localhost:2001Membrane can create and validate WS-Security UsernameTokens and XML Signatures.
Membrane can act as a gateway for Large Language Models (LLMs) and Model Context Protocol (MCP) servers, providing centralized access control, usage policies, and API key management.
The mcpProtectionplugin sits in front of an MCP server and controls which tools clients can discover and call.
api:
port: 2000
flow:
- mcpProtection:
tools:
- allow: getCustomers
- allow: getOrders
- deny: '.*'
target:
url: http://my-mcp-serverSee the MCP protection tutorial.
The llmGateway plugin routes requests to LLM providers such as Anthropic Claude, OpenAI, and Google Gemini. It can centralize provider API keys and enforce policies for token usage and allowed models.
api:
port: 2000
flow:
- llmGateway:
claude: {}
apiKey: <<Replace with your API_KEY>>
policies:
maxOutputTokens: 100000
models:
- claude-opus-4-8
- claude-sonnet-5
simpleStore:
users:
- name: alice
apiKey: abc123
tokens: 2000000
- name: bob
apiKey: qwertz
tokens: 10000000
limitResetPeriod: 86400
target:
url: https://api.anthropic.comInstead of handing the API key to every developer, keep it in the gateway and issue per-user keys. Membrane authenticates the user, enforces a per-user token budget, restricts the allowed models, and forwards the request using the shared provider key.
See: LLM key sharing tutorial.
Membrane provides flexible routing based on HTTP properties and custom expressions.
# Only GET to /products on port 2000
api:
port: 2000
method: GET
path: /products
flow:
- response:
- static:
src: Oui!
- return:
status: 200There are many routing options:
| Option | Description |
|---|---|
port |
Listening port |
method |
HTTP method, e.g. GET, POST, QUERY |
path |
Request path |
host |
Hostname, e.g. api.predic8.de |
test |
Custom expression, e.g. header['content-type'].startsWith('text/plain') |
See the API reference documentation.
Transform a POST request such as:
POST /products
Content-Type: application/json
{"limit": 100, "sort": "name"}
into a GET request: GET /products?limit=100&sort=name using a URI template with JSONPath expressions:
api:
port: 2000
target:
method: GET
url: https://api.predic8.de/shop/v2/products?sort=${$.sort}&limit=${$.limit}
language: jsonpathNote: Membrane automatically escapes expression values such as
See the tutorial to transform from GET to POST.
Templates can transform request and response bodies using data from the current message or the environment.
Membrane uses a Groovy-based template engine with dynamic constructs such as loops and conditionals. Its syntax is similar to template engines commonly used in web applications.
The example creates a JSON document containing the names and values of the request's HTTP headers.
- template:
contentType: application/json
pretty: true
src: |
{
<% header.eachWithIndex { e, i -> %>
<% if (i > 0) { %>,<% } %>
<%= e.key %>: <%= e.value %>
<% } %>
}Templates can access message data using JSONPath, XPath, and Groovy.
Membrane can orchestrate calls to external APIs and process collections with loops and conditional flows.
The example iterates over a list of fruits and sends a POST request for each item:
api:
port: 2000
flow:
- for:
# Loops over a list of objects [{ "name": "Mango", "price": 1.23 }, ..]
in: $.fruits
language: jsonpath
flow:
- setBody:
# Serialize the current item to a JSON string
value: ${toJSON(it)}
# callout to an external API
- call:
method: POST
url: https://api.predic8.de/shop/v2/products
- log:
message: "Created product: ${it['name']}"
- return:
status: 200Scripts can inspect and modify requests, responses, and extend gateway behavior. This makes it possible to implement custom API logic for use cases such as:
- Routing: Load balance requests across multiple backends
- Error Handling: Create custom error responses
- Orchestration: Chain multiple APIs together to form a complex workflow
- Creating Responses: Tailor responses dynamically based on client requests or internal logic.
- Mocking APIs: Simulate API behavior during testing or development phases.
- Debugging and Tracing: Inspect incoming requests during development.
api:
port: 2000
flow:
- groovy:
src: |
println "I'm executed in the ${flow} flow"
println "HTTP Headers:\n${header}"
target:
url: https://api.predic8.deYou can write scripts in Groovy and JavaScript.
A Membrane flow does not have to follow a fixed sequence. The if and choose plugins let you execute parts of a flow only when specific conditions are met. A common use case is error handling.
api:
port: 2000
flow:
- response:
- if:
test: statusCode >= 500
flow:
- static:
src: Failure!
target:
url: https://httpbin.org/status/500Membrane provides security features for protecting APIs, services, and backend systems.
Incoming requests can be authenticated by validating an API key against keys stored in a file or database.
global:
- apiKey:
stores:
- simple:
- secret:
value: aed8bcc4-7c83-44d5-8789-21e4024ac873
- secret:
value: 08f121fa-3cda-49c6-90db-1f189ff80756
extractors:
- header: X-Api-KeyMembrane also supports:
- Defining permissions as scopes in OpenAPI and enforcing them with API keys.
- Extracting API keys from headers, query parameters, or custom locations using expressions.
- Role-based access control (RBAC) with fine-grained permissions.
See the API Key Tutorials
The API below only allows requests that present a valid JSON Web Token issued by Microsoft Azure Entra ID.
api:
port: 2000
flow:
- jwtAuth:
expectedAud: api://2axxxx16-xxxx-xxxx-xxxx-faxxxxxxxxf0
jwks:
jwksUris: https://login.microsoftonline.com/common/discovery/keys
target:
url: https://your-backendUse OAuth2/OpenID to secure endpoints against Google, Azure Entra ID, GitHub, Keycloak or Membrane Authentication Servers.
api:
port: 2000
flow:
- oauth2Resource2:
membrane:
src: http://localhost:8000
clientId: abc
clientSecret: def
scope: openid profile
claims: username
claimsIdt: sub
- request:
# Forward the authenticated user’s email to the backend in an HTTP header.
- setHeader:
name: X-EMAIL
value: ${property['membrane.oauth2'].userinfo['email']}
target:
url: http://backendTry the OAuth tutorial
The following example shows a minimal configuration for running Membrane as an OAuth 2.0 authorization server.
api:
port: 8000
flow:
- oauth2authserver:
issuer: http://localhost:8000
location: logindialog
consentFile: consentFile.json
staticUserDataProvider:
users:
- username: john
password: secret
email: john@predic8.de
staticClientList:
clients:
- clientId: abc
clientSecret: def
callbackUrl: http://localhost:2000/oauth2callback
bearerToken: {}
claims:
value: aud email iss sub username
scopes:
- id: username
claims: username
- id: profile
claims: username emailUser accounts can be stored in a file, in an LDAP server or backed by a database.
This example enables TLS for connections from clients to the API Gateway:
api:
port: 443
ssl:
keystore:
location: keystore.p12
password: changeit
truststore:
location: keystore.p12
password: changeit
target:
url: http://backendMembrane supports advanced TLS scenarios, including:
- TLS termination at the API Gateway with optional TLS forwarding to the backend.
- SNI-based routing.
- Routing TLS connections without decrypting them.
See the TLS/SSL tutorial
Membrane protects APIs from risks associated with XML, JSON, JSON-RPC and GraphQL payloads.
api:
port: 2000
flow:
- xmlProtection:
maxAttributeCount: 3
maxElementNameLength: 100
removeDTD: true
- return:
status: 200See the XML protection, JSON protection , JSON-RPC protection, and GraphQl protection references.
Limit the number of incoming requests within a defined time period:
global:
- rateLimiter:
requestLimit: 1000
requestLimitDuration: PT1HDistribute the workload across multiple API backend nodes.
api:
port: 8080
flow:
- balancer:
clusters:
- name: Default
nodes:
- host: my.backend-1
port: 4000
- host: my.backend-2
port: 4000
- host: my.backend-3
port: 4000See the API loadbalancing examples
Expose Membrane metrics for Prometheus:
api:
port: 2000
path:
uri: /metrics
flow:
- prometheus: {}The collected metrics can be visualized in a Grafana dashboard:
Membrane supports integration with OpenTelemetry. This enables detailed tracing of requests across Membrane and backend services.
For working examples of Prometheus, Grafana and OpenTelemetry see the operation tutorial.
To get support from our community, post your questions to our discussions page @GitHub.
If you find a bug, report it using GitHub Issues. Please provide a minimal example that reproduces the issue and the version of Membrane you are using.
See commercial support options and pricing.
Learn how API Gateways work through practical scenarios and real-world examples.
Download instantly. No registration required.
Meet other Membrane users online to discuss API gateway operation and architecture. Membrane developers answer questions and welcome your feedback and feature requests.
- September 30, 2026 Legacy Integration with XML, SOAP, and WSDL
- October 28, 2026 Authentication with JSON Web Tokens (JWT)
- November 25, 2026 MCP and AI Tool Integration
- December 30, 2026 Message Transformation







