@unirate/express
v0.1.0
Published
Official Express middleware and router for the UniRate currency-exchange API. Attaches a typed `unirate` client to `req.unirate`, or mounts read-only proxy routes that keep your API key server-side. Zero runtime deps; express is a peer (v4 + v5).
Maintainers
Readme
@unirate/express
Express middleware and router for the UniRate API — free currency exchange rates, conversion, and VAT rates.
Attach a typed UniRate client to every request, or mount a ready-made set of JSON endpoints, and keep your API key server-side. Works with Express 4 and 5.
Features
unirateClient()middleware — attaches a typedUniRateClienttoreq.unirateunirate()— a mountable ExpressRouterwith/rate,/convert,/currencies,/vatroutes- Full error mapping — UniRate statuses (400 / 401 / 403 / 404 / 429 / 503) mirrored to proper HTTP responses; transport failures become 502; a missing key becomes 500
- Currency-code validation + uppercasing
- API key read from options or the
UNIRATE_API_KEYenvironment variable - Zero runtime dependencies (uses native
fetch);expressis a peer dependency - TypeScript throughout —
req.unirateis typed via Express module augmentation - ESM + CJS builds
Install
npm install @unirate/express expressexpress is a peer dependency (>=4.0.0, works with v4 and v5). Requires Node 18+ for native fetch. Get a free UniRate API key at unirateapi.com.
Quick start
Middleware
import express from "express";
import { unirateClient } from "@unirate/express";
const app = express();
app.use(unirateClient()); // reads process.env.UNIRATE_API_KEY
app.get("/eur", async (req, res) => {
const rate = await req.unirate.getRate("USD", "EUR");
res.json({ pair: "USD/EUR", rate });
});
app.listen(3000);req.unirate is fully typed via Express's Request interface — no casting needed.
Router
import express from "express";
import { unirate } from "@unirate/express";
const app = express();
app.use("/api/unirate", unirate());That mounts:
GET /api/unirate/rate?from=USD&to=EUR
GET /api/unirate/convert?from=USD&to=EUR&amount=100
GET /api/unirate/currencies
GET /api/unirate/vat?country=DEOptions
Both unirateClient() and unirate() accept the same options:
| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | — | API key. If omitted, resolved from the env variable |
| envKey | string | "UNIRATE_API_KEY" | Name of the env variable to read the key from |
| baseUrl | string | https://api.unirateapi.com | Override the API base URL |
| fetch | typeof fetch | globalThis.fetch | Inject a custom fetch |
| timeoutMs | number | 30000 | Request timeout |
| userAgent | string | @unirate/express/<version> | Custom User-Agent header |
app.use(unirateClient({ apiKey: process.env.UNIRATE_API_KEY }));
// or a custom env variable name
app.use("/fx", unirate({ envKey: "MY_UNIRATE_KEY" }));Routes
GET /rate
| Param | Required | Default | Description |
|---|---|---|---|
| from | no | USD | Source currency (3-letter ISO code) |
| to | no | — | Target currency. Omit to return all rates for from |
// /rate?from=USD&to=EUR →
{ "rate": 0.92 }
// /rate?from=USD →
{ "rates": { "EUR": 0.92, "GBP": 0.79 } }GET /convert
| Param | Required | Default | Description |
|---|---|---|---|
| from | no | USD | Source currency |
| to | yes | — | Target currency |
| amount | no | 1 | Amount to convert (positive number) |
// /convert?from=USD&to=EUR&amount=100 →
{ "result": 92.5 }GET /currencies
{ "currencies": ["USD", "EUR", "GBP", "..."] }GET /vat
| Param | Required | Default | Description |
|---|---|---|---|
| country | no | — | ISO-3166 alpha-2 code. Omit for all countries |
// /vat?country=DE →
{ "country": "DE", "vat_data": { "country_code": "DE", "country_name": "Germany", "vat_rate": 19 } }Using the client directly
The internal client is exported for direct use in your own handlers:
import { UniRateClient, RateLimitError } from "@unirate/express";
const client = new UniRateClient({ apiKey: "..." });
try {
const rate = await client.getRate("USD", "EUR"); // number
const all = await client.getRate("USD"); // Record<string, number>
const eur = await client.convert("EUR", 100, "USD"); // number
const codes = await client.getSupportedCurrencies(); // string[]
const vat = await client.getVatRate("DE"); // { country, vat_data }
} catch (err) {
if (err instanceof RateLimitError) {
// back off and retry
}
}It is also available on the @unirate/express/client subpath if you want the client without pulling in the Express glue.
Error handling
The router maps thrown errors to HTTP responses with a { "error": string } body:
| Status | Meaning |
|---|---|
| 400 | Invalid parameter (bad currency/country code, missing/invalid amount) |
| 401 | Missing or invalid API key |
| 403 | Endpoint requires a Pro subscription |
| 404 | Currency not found or no data available |
| 429 | Rate limit exceeded |
| 500 | API key not configured on the server |
| 502 | UniRate upstream / transport error |
| 503 | Service unavailable |
When using the client directly, these map to typed error classes — all extending UniRateError:
InvalidRequestError, AuthenticationError, ProRequiredError, InvalidCurrencyError, RateLimitError, ServiceUnavailableError.
Free vs Pro tier
The free tier covers /rate, /convert, /currencies, and /vat. Historical and time-series endpoints require a Pro subscription and are not exposed by this package.
Example
A runnable Express app lives in examples/server.mjs:
npm run build
UNIRATE_API_KEY=your-key node examples/server.mjsRelated packages
UniRate API client libraries: Python · Node.js · Go · Rust · Ruby · PHP · Java · Swift · .NET
Framework integrations: Fastify · Hono · Next.js · Nuxt · SvelteKit · Astro · NestJS · Remix · Angular · Vue · React
Platform: Cloudflare Workers · MCP server · CLI
License
MIT © Unirate Team
