npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@unirate/qwik

v0.1.0

Published

Qwik & Qwik City integration for the UniRate currency-exchange API. Server-safe client factory, routeLoader$-ready loader factories, a server$-friendly data API, a useUniRate helper, and a key-hiding proxy handler. Zero runtime deps.

Readme

@unirate/qwik

npm ci License: MIT

Qwik & Qwik City integration for the UniRate currency-exchange API.

  • Server-safe client factory — createUniRate() resolves the key from UNIRATE_API_KEY at call time. Use it inside routeLoader$, routeAction$, or server$, so the key never enters the browser bundle.
  • routeLoader$-ready loader factories — createRateLoader, createConvertLoader, createCurrenciesLoader. Drop them straight into routeLoader$(...).
  • server$-friendly data functions — getRate, convert, listCurrencies, getVatRates, getHistoricalRate, getTimeSeries, getHistoricalLimits.
  • useUniRate(signal) — adapts a loader signal into a tidy { data, error } view.
  • Key-hiding proxy — createUniRateProxyHandler() for a /api/unirate endpoint that forwards allow-listed paths with the key injected server-side.
  • Zero runtime deps. Native fetch. Full typed error mapping.

Install

npm install @unirate/qwik
# or
pnpm add @unirate/qwik
# or
yarn add @unirate/qwik

Peer deps: @builder.io/qwik ^1.5 and @builder.io/qwik-city ^1.5 (both optional — you can use the raw client and the proxy handler without Qwik City). Requires Node 18.17+.

Get a free API key at unirateapi.com — the free tier covers latest rates, conversions, currency lists, and VAT rates for ~600 currencies including crypto. Historical rates, time-series, and commodity feeds require Pro.

Why no Qwik build step? This package ships as plain, tsc-compiled TypeScript utilities. It never calls $ itself, so it does not need the Qwik optimizer to build or publish. You wrap the factories in your own routeLoader$ / server$, where your app's optimizer takes over. That keeps the dependency surface tiny and the package trivially testable.

Quickstart

Set UNIRATE_API_KEY in your Qwik City .env, then in src/routes/currency/index.tsx:

import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import { createRateLoader, useUniRate } from "@unirate/qwik";

// Runs only on the server; reads UNIRATE_API_KEY from the platform env.
export const useRate = routeLoader$(createRateLoader({ defaultFrom: "USD" }));

export default component$(() => {
  // Visit /currency?from=USD&to=EUR
  const { data, error } = useUniRate(useRate());
  if (error) return <p>{error.message}</p>;
  return (
    <p>
      {data?.from} → {data?.to}: {JSON.stringify(data?.rate)}
    </p>
  );
});

A full route (all three loaders + server$) is in examples/routes-currency-index.tsx, and the proxy endpoint in examples/routes-api-unirate-index.ts.

Loader factories

Each factory returns an async (ev) => data function shaped for routeLoader$. It resolves the API key in this order: explicit apiKey option → ev.env.get("UNIRATE_API_KEY") → process.env.UNIRATE_API_KEY.

import {
  createRateLoader,
  createConvertLoader,
  createCurrenciesLoader,
} from "@unirate/qwik";

export const useRate = routeLoader$(createRateLoader({ defaultFrom: "USD" }));
export const useConvert = routeLoader$(createConvertLoader());
export const useCurrencies = routeLoader$(createCurrenciesLoader());

| Factory | Reads query params | Returns | |---|---|---| | createRateLoader | from (default USD), to | { rate, from, to } | | createConvertLoader | from, to (required), amount (default 1) | { result, from, to, amount } | | createCurrenciesLoader | — | { currencies } |

createConvertLoader throws a Response with status 400 when to is missing, which Qwik City surfaces as an error boundary.

Options for every factory:

{
  apiKey?: string;      // overrides env resolution
  baseUrl?: string;     // point at a proxy
  timeoutMs?: number;   // default 30_000
  defaultFrom?: string; // rate/convert base when `from` is absent (default USD)
}

useUniRate(signal)

A tiny, optimizer-free adapter. Hand it any Qwik Signal-like value (e.g. the result of a routeLoader$) and it returns { data, error }. If the payload is an { error: string } envelope, it comes back as a normalized Error; otherwise it passes through on data.

const rate = useUniRate(useRate());
if (rate.error) return <p>{rate.error.message}</p>;
return <p>{JSON.stringify(rate.data?.rate)}</p>;

server$ data functions & the client factory

createUniRate() gives you the full UniRate surface with Qwik-friendly (from, to, amount) argument order plus the raw client:

import { server$ } from "@builder.io/qwik-city";
import { createUniRate } from "@unirate/qwik";

export const convertMoney = server$(async function (from: string, to: string, amount: number) {
  const unirate = createUniRate();          // key from server env
  return unirate.convert(from, to, amount);  // → number
});

Available methods on the object returned by createUniRate():

  • getRate(from, to?) → number (with to) or Record<string, number>
  • convert(from, to, amount) → number
  • listCurrencies() → string[]
  • getVatRates(country?) → all countries, or one when country is passed
  • getHistoricalRate(date, from, to?, amount?) → number or map (Pro)
  • getTimeSeries(startDate, endDate, base?, currencies?, amount?) → nested map (Pro)
  • getHistoricalLimits() → limits struct (Pro)
  • client — the underlying UniRateClient

Module-level getRate / convert / listCurrencies / … are also exported; they lazily build a default instance from process.env.UNIRATE_API_KEY, handy for wrapping directly in server$.

Key-hiding proxy endpoint

For client-side fetches that must not leak the key, mount a proxy route (src/routes/api/unirate/index.ts):

import type { RequestHandler } from "@builder.io/qwik-city";
import { createUniRateProxyHandler } from "@unirate/qwik";

const proxy = createUniRateProxyHandler();

export const onGet: RequestHandler = async (ev) => {
  const res = await proxy(new URL(ev.url), ev.env.get("UNIRATE_API_KEY"));
  const ct = res.headers.get("Content-Type");
  if (ct) ev.headers.set("Content-Type", ct);
  ev.send(res);
};

The browser then calls /api/unirate?path=/api/rates&from=USD&to=EUR. Only these paths are allowed by default (override with allowedPaths): /api/rates, /api/convert, /api/currencies, /api/vat/rates, /api/historical/rates, /api/historical/timeseries, /api/historical/limits. Responses carry a Cache-Control: public, s-maxage=60, stale-while-revalidate=300 header.

Using the raw client

UniRateClient is exported from @unirate/qwik and @unirate/qwik/client. It takes a fetch option for injection (that's how the test suite avoids the network):

import { UniRateClient } from "@unirate/qwik/client";

const client = new UniRateClient({ apiKey: process.env.UNIRATE_API_KEY! });
const rate = await client.getRate("USD", "EUR"); // 0.92

Errors

Every failure is a typed subclass of UniRateError, exported for instanceof checks:

  • AuthenticationError — 401, bad / missing key
  • RateLimitError — 429
  • InvalidCurrencyError — 404, unknown currency code
  • InvalidRequestError — 400, bad params
  • ProRequiredError — 403, endpoint requires a Pro subscription
  • UniRateError — base class for any other failure (carries status and body)
import { InvalidCurrencyError } from "@unirate/qwik";

try {
  await unirate.getRate("USD", "XYZ");
} catch (err) {
  if (err instanceof InvalidCurrencyError) {
    // unknown currency
  }
}

Rate limits

The free tier is rate-limited per key; on 429 you'll get a RateLimitError. Keep the key server-side (loaders, server$, or the proxy handler) so it is never exposed to end users.

Other UniRate clients

UniRate ships official client libraries and framework integrations across the ecosystem. The repos below are all maintained under the UniRate-API org.

Get a free API key at unirateapi.com.

License

MIT © Unirate Team