Portway: Built for the Systems You Can't Replace
How a recurring ERP integration problem became an open-source API gateway, built and validated on a production Globe+ installation.
Exact Globe+ has an internal REST API. It runs on a local port, speaks NTLM, and was never meant to be reached from outside the server. The client had also built a set of custom SQL databases alongside it: reporting tables, staging data, and business logic that Globe+ didn't cover. Two sources of truth, neither of them safely reachable from a modern web app.
Portway was built on that installation specifically: custom databases from a developer who had since left, Windows authentication throughout, a team that needed to extend it without breaking anything.
The standard approach
The standard approach is to write a small service that exposes what the current project needs: a .NET or Node wrapper around the database or ERP, authentication bolted on, deployed alongside the app. It ships and it works. The problem is what accumulates: every project gets its own service, each one slightly different, none of them with consistent auth, filtering, or error handling. Six months later nobody knows who owns them or whether they're still running. Portway came out of writing enough of those services to know exactly what they all needed in common.
What Portway is
It's a .NET 10 API gateway that runs on Windows Server under IIS or as a Docker container. Configuration is a folder of JSON files: one per environment, one per endpoint. Portway routes authenticated requests to SQL databases or internal HTTP services, watches those files for changes, and brings a new endpoint live the moment you save it. There is no dashboard and no proprietary deployment format.
Every request URL includes an environment segment: /api/{environment}/{endpoint}. That segment maps to a folder on disk. The folder has a connection string. That's the whole routing model.
Client
Bearer token
Portway
Auth · Route · OData
SQL
Tables, views, stored procedures, and TVFs exposed as REST with OData filtering, pagination, and column-level access control.
Proxy & Composite
Forward requests to internal HTTP services with URL rewriting and NTLM passthrough. Composite endpoints chain multiple steps into a single atomic call.
File, Webhook & Static
Upload and retrieve files via API, receive and persist webhook payloads to SQL, or serve pre-defined JSON, XML, or CSV with OData support.
Setting it up for the Globe+ deployment
Globe+'s internal API runs on a local port and uses NTLM. The IIS Application Pool identity runs as a domain user with Globe+ access, so credentials never appear in config files. Portway inherits that access through Windows authentication and forwards requests to the ERP on behalf of the caller. Each environment gets its own folder with the server name and the headers Globe+ expects on every request.
// environments/500/settings.json
{
"ServerName": "ERP-SERVER-01",
"ConnectionString": "Server=ERP-SERVER-01;Database=500;Trusted_Connection=True;",
"Headers": {
"DatabaseName": "500",
"ServerName": "ERP-SERVER-01"
}
}
Two company databases, two environment folders: /api/500/ and /api/700/. The custom SQL databases the previous developer had built sat alongside Globe+ and used the same connection string setup. One gateway, both sources, tokens scoped so each consumer only reaches what it's supposed to.
Exposing the item catalogue
Globe+'s internal field names follow the ERP's own conventions. Column aliases map them to a cleaner API surface without touching the schema: developers query ProductName and Price without ever seeing Assortment or the underlying field names.
// endpoints/SQL/Items/entity.json
{
"DatabaseObjectName": "Items",
"PrimaryKey": "ItemCode",
"AllowedColumns": [
"ItemCode",
"Description;ProductName",
"Assortment;Category",
"SalesPrice;Price"
],
"AllowedMethods": ["GET"],
"AllowedEnvironments": ["500", "700"]
}
From that point, the customer portal could query live ERP data with standard OData filtering. Adding a new column or endpoint meant editing a config file, not deploying code.
GET /api/500/Items?$filter=Category eq 'Electronics' and Price gt 50&$orderby=ProductName
Authorization: Bearer <token>
Multi-step write operations
Globe+ requires multi-step transactions for things like sales orders: first the lines with a shared transaction key, then the header. Getting that right across two separate API calls, reliably, under failure conditions, is the kind of work that produces bugs you only find in production. Portway's composite endpoints handle the orchestration server-side. The client sends one request, gets one response, and if any step fails, the whole operation rolls back.
POST /api/500/composite/SalesOrder
{
"Header": { "OrderDebtor": "100123", "YourReference": "WEB-0042" },
"Lines": [
{ "Itemcode": "ITEM-001", "Quantity": 2 },
{ "Itemcode": "ITEM-002", "Quantity": 4 }
]
}
Production
The first deployment went cleanly: the customer portal had live ERP prices, the custom SQL databases were reachable through the same gateway, and the ERP itself was untouched throughout. The same pattern went into a second project, then a third: environments per database, endpoints per table or view, tokens scoped per consumer. Setting up a new data source went from weeks of coordination to an afternoon of config files.
Docker images, IIS guides, endpoint references, and ERP integration examples on GitHub.