Skip to content

Getting StartedInstallation

Installation

Add @noverax/courier to your project — works with any JavaScript framework.

6 min readUpdated

In one command

npx @noverax/create-courier installs what your framework needs and sets it up: the route, the config, Studio and a mock. See Create Courier. This page installs each package by hand.

Install the core package

npm install @noverax/courier

The core package has zero runtime dependencies and works in any JavaScript runtime with fetch.

Framework adapters

Install the adapter for your server framework. Each adapter is a thin wrapper around createHandler — see the Server Handler docs for details.

FrameworkPackageExport
Next.js@noverax/courier-nextapiRoute, sseRoute, createAction
Nuxt / Nitro / h3@noverax/courier-nuxtcourierHandler
SvelteKit@noverax/courier-sveltekitcourierHandler
Astro@noverax/courier-astrocourierHandler
Remix / React Router@noverax/courier-remixcourierHandler
SolidStart@noverax/courier-solidcourierHandler
Express@noverax/courier-expresscourierHandler
Hono@noverax/courier-honocourierHandler
Fastify@noverax/courier-fastifycourierPlugin
NestJS@noverax/courier-nestjscourierMiddleware
npm install @noverax/courier-next

React Query integration

For React apps, add the query package for type-safe hooks (useQuery, useMutation, useSuspenseQuery, useInfiniteQuery):

bash
npm install @noverax/courier-react @tanstack/react-query

Studio

Studio inspects requests in development. Its runtime registers your sources from courier.config.ts, and every framework's instrumentation snippet imports it directly — so it is a dependency of your app, not only of the dev server:

bash
npm install @noverax/courier-studio-core
npm install --save-dev @noverax/courier-studio

A production install leaves dev dependencies out, so a server that imports the runtime would not start if it were one — even a server that calls it only in development. Studio itself is a development tool and stays a dev dependency.

@noverax/courier-studio-core/runtime brings its own socket client and loads it only outside production, so the connection to the Studio dev server costs a production build nothing.

Studio and its runtime read courier.config.ts at the project root. A project that has no API sources yet starts with this one; Studio's APIs page offers it to copy when the project has none, and keeps Import API off until it loads:

courier.config.ts
import { defineConfig } from "@noverax/courier/config";
export default defineConfig({ sources: {} });

Start Studio with npx @noverax/courier-studio, and Import API on its APIs page writes the project's first API — under src/api/<source name>/ by default, where source.ts exports the source; npx @noverax/courier-studio import <spec> does the same from the command line. Add it to sources under its source name, and Studio lists it:

courier.config.ts
import { defineConfig } from "@noverax/courier/config";
import { petstore } from "./src/api/petstore/source";

export default defineConfig({ sources: { petstore } });

In a "type": "module" package whose tsconfig resolves modules as Node does (node16 or nodenext), the generated files name each relative import with its .js extension, as TypeScript requires there, and Studio asks the config to import ./src/api/petstore/source.js.

Mock servers

Studio can also run mock servers of your sources. Add the mock package and the server it runs on, for development only — leave out any your app already lists under dependencies:

bash
npm install --save-dev @noverax/courier-mock hono @hono/node-server

Declare the mocks in courier.mocks.ts, beside courier.config.ts, or let Studio's mock wizard write the file for you:

courier.mocks.ts
import { defineMocks, defineMockServer } from "@noverax/courier-mock"
import courierConfig from "./courier.config"

export default defineMocks({
  apiMock: defineMockServer({ source: courierConfig.sources.api, autoStart: true }),
})

In a "type": "module" package whose tsconfig resolves modules as Node does (node16 or nodenext), import ./courier.config.js instead: TypeScript asks for the extension there, and both TypeScript and Studio map it to courier.config.ts. The wizard writes it that way in such a project.

Reading the source from the config this way, as Studio's wizard writes it, needs a config whose default export is defineConfig({ sources }). With a config that exports sources by name only, import the source from where the config does instead: a default import of that config does not type-check.

Only Studio reads courier.mocks.ts. Your app loads courier.config.ts in production too, where development packages are not installed, so mocks are never declared there: courier.config.ts has no mocks key.

Install only what you use

All framework packages and integrations are optional. The core @noverax/courier package works standalone for direct API calls from any runtime.

Routes CLI

A page that calls your API through your server's proxy takes its routes from courier.routes.ts, which Courier writes from the endpoints you mark .expose() — Studio as it runs, and courier-cli routes without it. Add the CLI for development, so your CI checks the module with the version your lockfile pins:

bash
npm install --save-dev @noverax/courier-cli

npx @noverax/courier-cli routes writes the module; with --check, it writes nothing and fails when the module is out of date. See Exposing Endpoints.

Requirements

  • Node.js 22 or later for every package; Studio needs 22.13, for the SQLite built into Node.
  • Safari 15.4, Chrome 97 or Firefox 104 or later for the core in a browser — every version released since August 2022. It needs nothing those lack, and no crypto.subtle, which a page served over plain HTTP does not get. The published code is ES2022; a bundler that targets older browsers lowers it.
  • A bundler that reads a package's exports and imports fields, as Vite, webpack 5, Rollup, esbuild, Turbopack and Metro do. Through imports a page takes a stand-in of each module only a server runs — its terminal logger, the addresses a request came through, the check of forward — and Node, an edge runtime and React Native take the modules themselves.
  • ES modules. The packages are published as ES modules only. A CommonJS project loads them with require() on Node.js 22.12 or later, which loads an ES module synchronously; with TypeScript, set "module": "nodenext", under which it allows that require.
  • TypeScript 6.0 or later, with strict on. The published declarations compile with skipLibCheck off in a project typed for Node.js alone, without the DOM library; in one resolving modules as a bundler does; in a browser project; and in a CommonJS one. They compile with exactOptionalPropertyTypes on too, and there an option you may leave out also takes undefined, so a setting that may be missing is passed as it is.

TypeScript configuration

Courier ships type declarations. Enable strict mode for the best type inference:

tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true
  }
}

A project Node.js runs without a bundler resolves modules as Node does instead: "module": "nodenext", which also sets moduleResolution.

An existing project

A project whose tsconfig still says "moduleResolution": "node" — node10, which reads no exports map — finds every entry Courier publishes too, @noverax/courier/define and the rest, through each package's typesVersions; it needs no change to start. TypeScript 6 deprecates that resolution, though, and with it baseUrl, downlevelIteration, an ES5 target and a few more; it refuses them unless "ignoreDeprecations": "6.0" says to wait, and TypeScript 7 drops them. Moving to it, a project that a bundler builds — Next.js, Vite, Nuxt — sets "moduleResolution": "bundler", and one Node.js runs "module": "node16"; paths are written relative to the tsconfig once baseUrl goes. TypeScript 6 also checks side-effect imports, so import "swiper/css" needs a declaration — declare module "swiper/css"; — or "noUncheckedSideEffectImports": false. create-courier lists what your tsconfig sets of these in its plan.

Environment variables

Add your backend API URL as a server-side environment variable:

.env.local
# Your backend API URL (server-side only)
API_URL=http://localhost:4000

This is used in your defineApi call. In proxy mode, the browser never sees this URL — requests go through your server's proxy route.

Next steps