Skip to content

Repository files navigation

@unirate/solid

npm ci License: MIT

SolidJS primitives and a provider for the UniRate currency-exchange API. Every primitive returns a Solid Resource, so loading / error / refetch come for free and the request re-runs reactively when its inputs change.

  • createRate(from, to) — single rate → Resource<number>
  • createRates(from) — full rate table for a base → Resource<Record<string, number>>
  • createConversion(from, to, amount) — converted amount → Resource<number>
  • createCurrencies() — supported codes (~600) → Resource<string[]>
  • createVatRates() / createVatRates(country) — VAT rates → Resource<VATRatesAll | VATRateOne>
  • createHistoricalRate(date, from, to) — historical rate (Pro) → Resource<number>
  • <UniRateProvider apiKey="..."> — one client for the whole app
  • useUniRate() — grab the client imperatively
  • Zero runtime deps. Native fetch. Requests aborted on cleanup.

Every argument may be a plain value or a Solid accessor (a signal getter) — pass an accessor and the resource re-fetches reactively when it changes.

Install

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

Peer dep: solid-js >=1.8. Requires Node 18.17+ for build/server use.

Quickstart

Wrap your app once in <UniRateProvider>:

import { render } from "solid-js/web";
import { UniRateProvider } from "@unirate/solid";
import App from "./App";

render(
  () => (
    <UniRateProvider apiKey={import.meta.env.VITE_UNIRATE_API_KEY}>
      <App />
    </UniRateProvider>
  ),
  document.getElementById("root")!,
);

Then reach for a primitive anywhere in the tree:

import { Show } from "solid-js";
import { createRate } from "@unirate/solid";

function Ticker() {
  const [rate] = createRate("USD", "EUR");
  return (
    <Show when={!rate.loading} fallback={<p>Loading…</p>}>
      <Show when={!rate.error} fallback={<p>Couldn't load the rate.</p>}>
        <p>1 USD = {rate()} EUR</p>
      </Show>
    </Show>
  );
}

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

Where does the key live? @unirate/solid runs in the browser, so the API key ships to the client. UniRate keys are scoped to the free/Pro tier you choose; if you need the key kept server-side, put a proxy in front (a SolidStart server function, or pass a custom baseUrl pointing at your own proxy).

Primitives

Each primitive returns Solid's standard [Resource, { refetch, mutate }] tuple:

const [rate, { refetch }] = createRate("USD", "EUR");
rate();          // the settled value, or undefined until first success
rate.loading;    // true while a request is in flight
rate.error;      // the last error, or undefined while healthy
rate.state;      // "unresolved" | "pending" | "ready" | "refreshing" | "errored"
refetch();       // imperatively re-run

createRate(from, to, options?)

const [rate] = createRate("EUR", "USD");
// rate() === 1.0823

createRates(from, options?)

const [rates] = createRates("USD");
// rates() === { EUR: 0.92, GBP: 0.80, JPY: 149.3, ... }

createConversion(from, to, amount, options?)

const [amount, setAmount] = createSignal(100);
const [converted] = createConversion("USD", "EUR", amount);
// converted() === 92.5 — recomputes when amount() changes

createCurrencies(options?)

const [codes] = createCurrencies();
// codes() === ["USD", "EUR", "GBP", ...] (~600 incl. crypto)

createVatRates(options?) and createVatRates(country, options?)

const [all] = createVatRates();          // Resource<VATRatesAll>
const [de]  = createVatRates("DE");      // Resource<VATRateOne>
// de()?.vat_data.vat_rate === 19

createHistoricalRate(date, from, to, amount?, options?) — Pro

const [hist] = createHistoricalRate("2024-01-01", "USD", "EUR");
// On the free tier hist.error is a ProRequiredError.

Reactive args + enabled

Any argument accepts a plain value or an accessor, and options.enabled (also value-or-accessor) defers the request until dependent state is ready:

const [base, setBase] = createSignal("USD");
const [ready, setReady] = createSignal(false);
const [rates] = createRates(base, { enabled: ready });
// fires once ready() flips true, and refires whenever base() changes

options.client overrides the context client for a single call (tests, or a second UniRate account/proxy).

Imperative client

Need a rate outside a component (a route data function, a server function)? Import the zero-dep client directly:

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

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

Inside a component you can also grab the context client with useUniRate().

Errors

The client maps HTTP status codes to typed errors, all extending UniRateError. They surface through each resource's .error:

Status Error Meaning
400 InvalidRequestError Bad parameters
401 AuthenticationError Missing/invalid API key
403 ProRequiredError Endpoint requires Pro
404 InvalidCurrencyError Unknown currency / no data
429 RateLimitError Rate limit exceeded

Security

  • Zero runtime dependencies; solid-js is a peer provided by your app.
  • Native fetch only — no transitive HTTP-client supply-chain surface.
  • In-flight requests are aborted on cleanup via AbortController.
  • Published to npm with provenance attestation.

Part of the UniRate ecosystem

Official UniRate clients & framework integrations: Python · Node · React · Vue · Angular · Nuxt · Next.js · SvelteKit · Astro · MCP server

License

MIT © Unirate Team

About

SolidJS integration for the UniRate currency-exchange API — reactive resources + provider, zero runtime dependencies

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages