Skip to content

TypeScript Clients

ShipQ automatically generates TypeScript HTTP clients from your handler registry. Every time you run shipq handler compile, ShipQ produces a fully typed client that mirrors your API surface — including request/response types, path parameters, and cookie-based authentication.

TypeScript codegen is controlled by the [typescript] section in shipq.ini:

[typescript]
framework = react
http_output = .
KeyDescriptionDefault
frameworkWhich framework helpers to generate alongside the base HTTP client. Options: react, svelte, or omit for plain TypeScript.react
http_outputOutput directory for generated TypeScript files, relative to your project root..

This section is created automatically by shipq init.

When you run shipq handler compile, ShipQ generates TypeScript files based on your configuration:

The base client is a plain TypeScript module with:

  • Typed request/response interfaces for every handler in your API
  • A function per endpoint with correct HTTP method, URL construction (including path parameters), request body typing, and response typing
  • Cookie-based authentication — requests include credentials: "include" so the browser sends the session cookie automatically
  • Error handling with typed error responses

This client has zero framework dependencies — it works with fetch and can be used in any TypeScript environment (browser, Node.js, Deno, Bun, etc.).

When the framework is set to react, ShipQ additionally generates React-specific hooks that wrap the base HTTP client:

  • Query hooks for GET endpoints (data fetching with loading/error states)
  • Mutation hooks for POST/PATCH/DELETE endpoints
  • Automatic cache invalidation patterns
  • TypeScript generics that preserve full type safety through the hook layer

When the framework is set to svelte, ShipQ generates Svelte-specific store-based helpers:

  • Readable stores for GET endpoints
  • Action helpers for mutations
  • Typed throughout using the same request/response interfaces

If you omit the framework key or leave it empty, only the base HTTP client is generated. This is useful if you’re using a framework ShipQ doesn’t have specific helpers for, or if you prefer to write your own data-fetching layer on top of the typed client.

All generated TypeScript files follow ShipQ’s ownership conventions:

  • Files with a zz_generated_ prefix or a // Code generated by shipq. DO NOT EDIT. comment at the top are overwritten on every handler compile. Do not hand-edit these.
  • The generated files are designed to be imported directly into your frontend code.

Point your frontend’s imports at the output directory configured in http_output. For example, if your frontend lives in a sibling directory:

[typescript]
framework = react
http_output = ../frontend/src/api

After running shipq handler compile, your frontend can import directly:

import { createPet, listPets, getPet } from './api/shipq-http';

The generated client functions accept typed parameters and return typed responses:

// Create a pet (POST /pets)
const newPet = await createPet({
name: "Luna",
species: "cat",
age: 3,
});
// newPet is fully typed: { id: string, name: string, species: string, age: number, created_at: string }
// Get a single pet (GET /pets/:id)
const pet = await getPet({ id: "abc123" });
// List pets (GET /pets)
const pets = await listPets();

ShipQ uses cookie-based authentication. The generated client sets credentials: "include" on fetch requests so the browser automatically sends the session cookie with every request. There’s no need to manually manage tokens — once the user logs in (which sets the cookie), all subsequent requests are authenticated:

import { login, listPets } from './api/shipq-http';
// Login sets the session cookie automatically
await login({ email: "[email protected]", password: "securepassword123" });
// Subsequent requests include the session cookie automatically
const pets = await listPets();

When using React hooks, the generated code provides ergonomic data-fetching primitives:

import { useListPets, useCreatePet } from './api/shipq-react';
function PetList() {
const { data: pets, isLoading, error } = useListPets();
const createPet = useCreatePet();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<ul>
{pets.map(pet => (
<li key={pet.id}>{pet.name} ({pet.species})</li>
))}
</ul>
<button onClick={() => createPet.mutate({ name: "New Pet", species: "dog", age: 1 })}>
Add Pet
</button>
</div>
);
}

If you’ve run shipq files to set up the file upload subsystem, ShipQ also generates a shipq-files.ts TypeScript helper that provides:

  • Typed upload functions for managed file uploads
  • Presigned URL handling
  • Integration with the base HTTP client’s cookie-based auth layer

If you’ve run shipq workers to set up the workers and channels system, ShipQ generates a shipq-channels.ts TypeScript client that provides:

  • Typed WebSocket channel subscriptions via Centrifugo
  • Channel-specific message types
  • Connection management and reconnection logic
  • Integration with the auth system for authenticated channel subscriptions (uses JWTs for Centrifugo WebSocket auth, separate from the cookie-based HTTP auth)

ShipQ maps Go types to TypeScript types in the generated client:

Go TypeTypeScript Type
stringstring
int, int32, int64number
float32, float64number
boolboolean
time.Timestring (ISO 8601)
*T (pointer)T | null
[]T (slice)T[]
Nested structInline interface

Response types avoid raw int64 values where possible — IDs are typically exposed as string (public IDs) to prevent JavaScript number precision issues.

The TypeScript client is regenerated every time you run:

Terminal window
shipq handler compile

This is the same command that regenerates server wiring, OpenAPI specs, and test harnesses. Any new or modified handlers are automatically reflected in the TypeScript client.

If you’ve only changed query definitions (not handlers), you only need shipq db compile. But if handler signatures changed — new fields, new endpoints, changed paths — you need shipq handler compile to update the TypeScript client.

  • Commit generated TypeScript files to version control. They’re part of your build output and your frontend developers need them.
  • Don’t hand-edit generated files. Customize behavior by wrapping the generated functions in your own modules.
  • Use http_output to place generated files directly in your frontend’s source tree so imports are clean and your editor gets full type inference.
  • Check the OpenAPI spec at GET /openapi (dev/test mode) if you want to verify the API surface that drives TypeScript codegen.