tsoap-cli
v0.0.4
Published
End-to-end type-safe SOAP client for TypeScript. Generate typed RPC wrappers directly from WSDLs.
Maintainers
Readme
tsoap-cli
Code generator that reads a WSDL file and emits a fully typed TypeScript client. Designed to replace the manual client.describe() + any-cast workflow you're used to with the soap package.
Why
If you've used the soap package, you know the drill:
import soap from 'soap';
const client = await soap.createClientAsync('http://example.com?wsdl');
// No types, no autocomplete — everything is `any`
const [result] = await client.MyOperationAsync({ foo: 'bar' });tsoap-cli generates TypeScript interfaces and a typed factory function from your WSDL so you get full autocomplete, compile-time type checking, and a cleaner async API.
Install
# npm
npm install -D tsoap-cli
# pnpm
pnpm add -D tsoap-cli
# yarn
yarn add -D tsoap-cliYou also need the runtime package as a production dependency:
npm install typed-soapUsage
Generate types from a WSDL
npx tsoap generate --input <path-or-url> --output <dir>| Flag | Alias | Description | Required |
|------|-------|-------------|----------|
| --input <path> | -i | Path to a local .wsdl file or a URL | Yes |
| --output <dir> | -o | Directory where the generated .ts file is written | Yes |
Examples
# Local WSDL file
npx tsoap generate -i ./wsdl/weather.wsdl -o ./src/generated
# Remote WSDL URL
npx tsoap generate -i https://api.example.com/weather?wsdl -o ./src/generatedThe output file is named after the input file — weather.wsdl produces weather.ts.
Use the generated client
import { createWeatherServiceClient } from './src/generated/weather.js';
const client = await createWeatherServiceClient('http://example.com/weather?wsdl');
// Fully typed: service -> port -> operation
const result = await client.WeatherService.WeatherPort.GetWeather({ city: 'NYC' });
console.log(result.temperature); // number — autocompleted and type-checkedWhat gets generated
Given a WSDL that defines a WeatherService with a GetWeather operation, the CLI produces a file like this:
// Auto-generated by tsoap-cli — do not edit
import { createSoapClient } from "typed-soap";
import type { ServiceDefinition, InferClient, SoapClientOptions } from "typed-soap";
// --- Enums (if the WSDL has XSD enumerations) ---
export type WeatherCondition = "SUNNY" | "CLOUDY" | "RAINY";
// --- Interfaces for every input/output type ---
export interface GetWeatherRequest {
city: string;
}
export interface GetWeatherResponse {
temperature: number;
condition: WeatherCondition;
description?: string;
}
// --- Service definition (maps the WSDL structure) ---
export interface WeatherServiceDefinition extends ServiceDefinition {
WeatherService: {
WeatherPort: {
GetWeather: {
input: GetWeatherRequest;
output: GetWeatherResponse;
};
};
};
}
// --- Typed factory function ---
export type WeatherServiceClient = InferClient<WeatherServiceDefinition>;
export async function createWeatherServiceClient(
wsdlUrl: string,
options?: SoapClientOptions,
): Promise<WeatherServiceClient> {
return createSoapClient<WeatherServiceDefinition>(wsdlUrl, options);
}Naming conventions
| WSDL concept | Generated name |
|---|---|
| Service definition type | {ServiceName}Definition |
| Client type | {ServiceName}Client |
| Factory function | create{ServiceName}Client |
| Input/output types | Named after the WSDL element names (e.g. GetWeatherRequest) |
| Enums | Named after the XSD simpleType (PascalCase) |
If the WSDL contains multiple services, the names fall back to GeneratedServiceDefinition, GeneratedClient, and createGeneratedClient.
Coming from the soap package
| Before (soap) | After (tsoap-cli + typed-soap) |
|---|---|
| client.MyOpAsync(args) returns [result, rawResponse, soapHeader, rawRequest] | client.Service.Port.MyOp(args) returns the result directly |
| No types — everything is any | Full interfaces generated from WSDL |
| You guess at field names | Autocomplete on every field |
| Typos in operation names fail silently at runtime | Caught at compile time |
| Numeric XSD types like unsignedInt arrive as strings | Custom deserializers convert them to number |
Supported XSD types
| XSD Type | TypeScript | Notes |
|---|---|---|
| int, short, byte, float, double | number | |
| unsignedInt, unsignedShort, unsignedByte | number | Custom deserializer |
| long, unsignedLong, integer, decimal | number | Precision warning emitted |
| boolean | boolean | |
| dateTime, date | Date | |
| string, normalizedString, token, anyURI, base64Binary | string | |
| anyType | unknown | |
| Enumerations | Union literal type | e.g. "A" \| "B" |
| maxOccurs="unbounded" | T[] | |
| minOccurs="0" | Optional property (?) | |
Adding to your build
A common pattern is to add a generate script to your package.json:
{
"scripts": {
"generate:types": "tsoap generate -i ./wsdl/my-service.wsdl -o ./src/generated"
}
}Then run npm run generate:types whenever the WSDL changes. The generated file should be checked into source control so your CI doesn't need access to the WSDL at build time.
Requirements
- Node.js 18+
- TypeScript 5.x (for consuming the generated types)
License
MIT
