@ord-api/ord-api-types
v1.0.37
Published
TypeScript types for ORD API - Auto-generated from OpenAPI specification
Readme
@ord-api/ord-api-types
TypeScript types for the ORD API, automatically generated from the OpenAPI specification.
Installation
# pnpm (recommended for SvelteKit)
pnpm add @ord-api/ord-api-types
# npm
npm install @ord-api/ord-api-types
# yarn
yarn add @ord-api/ord-api-typesSetup for SvelteKit
1. Create Axios Instance
// src/lib/api/client.ts
import axios from 'axios';
import { browser } from '$app/environment';
export const api = axios.create({
baseURL: browser ? '' : 'http://localhost:8080', // Use proxy in browser, direct in SSR
withCredentials: true, // Important for JWT cookies
});
// Optional: Add request/response interceptors
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Handle unauthorized
goto('/login');
}
return Promise.reject(error);
}
);2. Configure SvelteKit Proxy (Optional)
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
});3. Export Common Types
// src/lib/types/api-types.ts
import type { components } from '@ord-api/ord-api-types';
// Export enums
export type LanguageName = components['schemas']['LanguageName'];
export type WordType = components['schemas']['WordType'];
export type WordExtraMark = components['schemas']['WordExtraMark'];
export type WordToggleableProperty = components['schemas']['WordToggleableProperty'];
// Export DTOs
export type UserDTO = components['schemas']['UserDTO'];
export type WordDTO = components['schemas']['WordDTO'];
export type QuicklyAddedWordDTO = components['schemas']['QuicklyAddedWordDTO'];
export type CreateQAWRequest = components['schemas']['CreateQAWRequest'];
export type PaginatedDataResponse<T> = components['schemas']['PaginatedDataResponse'] & {
data: T[];
};Usage with TanStack Query (Svelte)
Setup Query Client
// src/routes/+layout.ts
import { browser } from '$app/environment';
import { QueryClient } from '@tanstack/svelte-query';
export const load = async () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
enabled: browser,
staleTime: 60 * 1000, // 1 minute
},
},
});
return { queryClient };
};<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { QueryClientProvider } from '@tanstack/svelte-query';
let { data, children } = $props();
</script>
<QueryClientProvider client={data.queryClient}>
{@render children()}
</QueryClientProvider>Create Query Functions
// src/lib/api/queries/words.ts
import { createQuery, createMutation } from '@tanstack/svelte-query';
import { api } from '$lib/api/client';
import type { QuicklyAddedWordDTO, CreateQAWRequest, PaginatedDataResponse } from '$lib/types/api-types';
export const createQuicklyAddedWordsQuery = (page = 0, perPage = 20) => {
return createQuery({
queryKey: ['quickly-added-words', page, perPage],
queryFn: async () => {
const response = await api.get<PaginatedDataResponse<QuicklyAddedWordDTO>>(
'/api/v1/quickly-added-words/',
{ params: { page, perPage } }
);
return response.data;
},
});
};
export const createCreateQAWMutation = () => {
return createMutation({
mutationFn: async (word: CreateQAWRequest) => {
const response = await api.post<QuicklyAddedWordDTO>(
'/api/v1/quickly-added-words/',
word
);
return response.data;
},
});
};
export const createDeleteQAWMutation = () => {
return createMutation({
mutationFn: async (id: string) => {
await api.delete(`/api/v1/quickly-added-words/${id}`);
},
});
};Use in Svelte 5 Components
<!-- src/routes/words/+page.svelte -->
<script lang="ts">
import { createQuicklyAddedWordsQuery, createCreateQAWMutation } from '$lib/api/queries/words';
import type { LanguageName } from '$lib/types/api-types';
let page = $state(0);
let perPage = $state(20);
// Query
const wordsQuery = createQuicklyAddedWordsQuery(page, perPage);
const words = $derived($wordsQuery.data?.data ?? []);
const pagination = $derived($wordsQuery.data?.pagination);
// Mutation
const createWordMutation = createCreateQAWMutation();
async function handleCreateWord(word: string, language: LanguageName) {
await $createWordMutation.mutateAsync({
word,
language,
});
// Invalidate and refetch
$wordsQuery.refetch();
}
</script>
{#if $wordsQuery.isPending}
<p>Loading...</p>
{:else if $wordsQuery.isError}
<p>Error: {$wordsQuery.error.message}</p>
{:else}
<div>
<h1>Words ({pagination?.totalResults ?? 0})</h1>
{#each words as word (word.id)}
<div>
<h3>{word.word}</h3>
<p>{word.language}</p>
</div>
{/each}
<!-- Pagination -->
{#if pagination}
<button
disabled={page === 0}
onclick={() => page--}
>
Previous
</button>
<span>Page {page + 1} of {pagination.totalPages}</span>
<button
disabled={page >= pagination.totalPages - 1}
onclick={() => page++}
>
Next
</button>
{/if}
</div>
{/if}Authentication Flow
// src/lib/api/queries/auth.ts
import { createMutation } from '@tanstack/svelte-query';
import { api } from '$lib/api/client';
import type { components, paths } from '@ord-api/ord-api-types';
type OtpRequestBody = paths['/api/v1/auth/otp-request']['post']['requestBody']['content']['application/json'];
type OtpVerifyBody = paths['/api/v1/auth/otp-verify']['post']['requestBody']['content']['application/json'];
type UserDTO = components['schemas']['UserDTO'];
export const createRequestOtpMutation = () => {
return createMutation({
mutationFn: async (body: OtpRequestBody) => {
await api.post('/api/v1/auth/otp-request', body);
},
});
};
export const createVerifyOtpMutation = () => {
return createMutation({
mutationFn: async (body: OtpVerifyBody) => {
const response = await api.post('/api/v1/auth/otp-verify', body);
return response.data;
},
});
};
export const createLogoutMutation = () => {
return createMutation({
mutationFn: async () => {
await api.delete('/api/v1/auth/logout');
},
});
};
export const createCurrentUserQuery = () => {
return createQuery({
queryKey: ['user', 'me'],
queryFn: async () => {
const response = await api.get<UserDTO>('/api/v1/users/me');
return response.data;
},
});
};<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { goto } from '$app/navigation';
import { createRequestOtpMutation, createVerifyOtpMutation } from '$lib/api/queries/auth';
let email = $state('');
let code = $state('');
let step = $state<'email' | 'code'>('email');
const requestOtpMutation = createRequestOtpMutation();
const verifyOtpMutation = createVerifyOtpMutation();
async function handleRequestOtp() {
await $requestOtpMutation.mutateAsync({ email });
step = 'code';
}
async function handleVerifyOtp() {
await $verifyOtpMutation.mutateAsync({ email, code });
goto('/dashboard');
}
</script>
{#if step === 'email'}
<form onsubmit={handleRequestOtp}>
<input
type="email"
bind:value={email}
placeholder="Enter your email"
required
/>
<button
type="submit"
disabled={$requestOtpMutation.isPending}
>
{$requestOtpMutation.isPending ? 'Sending...' : 'Send OTP'}
</button>
</form>
{:else}
<form onsubmit={handleVerifyOtp}>
<input
type="text"
bind:value={code}
placeholder="Enter 6-digit code"
required
/>
<button
type="submit"
disabled={$verifyOtpMutation.isPending}
>
{$verifyOtpMutation.isPending ? 'Verifying...' : 'Verify'}
</button>
</form>
{/if}Server Load Functions with Types
// src/routes/words/[id]/+page.ts
import type { PageLoad } from './$types';
import type { components } from '@ord-api/ord-api-types';
import { api } from '$lib/api/client';
type WordDTO = components['schemas']['WordDTO'];
export const load: PageLoad = async ({ params }) => {
const response = await api.get<WordDTO>(`/api/v1/words/${params.id}`);
return {
word: response.data,
};
};<!-- src/routes/words/[id]/+page.svelte -->
<script lang="ts">
let { data } = $props();
</script>
<h1>{data.word.word}</h1>
<p>Language: {data.word.language}</p>
<p>Type: {data.word.type ?? 'Not specified'}</p>Advanced Patterns
Type-Safe API Wrapper
// src/lib/api/wrapper.ts
import type { paths } from '@ord-api/ord-api-types';
import { api } from './client';
type ApiPath = keyof paths;
type ApiMethod = 'get' | 'post' | 'patch' | 'delete';
type RequestBody<Path extends ApiPath, Method extends ApiMethod> =
paths[Path][Method] extends { requestBody: { content: { 'application/json': infer Body } } }
? Body
: never;
type ResponseData<Path extends ApiPath, Method extends ApiMethod> =
paths[Path][Method] extends { responses: { 200: { content: { 'application/json': infer Data } } } }
? Data
: never;
export async function apiCall<Path extends ApiPath, Method extends ApiMethod>(
method: Method,
path: Path,
body?: RequestBody<Path, Method>
): Promise<ResponseData<Path, Method>> {
const response = await api.request({
method,
url: path as string,
data: body,
});
return response.data;
}
// Usage
const user = await apiCall('get', '/api/v1/users/me');
const word = await apiCall('post', '/api/v1/words/', { word: 'test', language: 'ENGLISH' });Form Actions with Types
// src/routes/words/create/+page.server.ts
import type { Actions } from './$types';
import type { CreateQAWRequest } from '$lib/types/api-types';
import { api } from '$lib/api/client';
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const wordData: CreateQAWRequest = {
word: data.get('word') as string,
language: data.get('language') as any,
definition: data.get('definition') as string || undefined,
};
try {
await api.post('/api/v1/quickly-added-words/', wordData);
return { success: true };
} catch (error) {
return fail(400, { error: 'Failed to create word' });
}
},
} satisfies Actions;Best Practices
- Use
$derivedfor computed values from queries - Use
$statefor local component state - Export query factories instead of hooks (Svelte pattern)
- Keep types in a central location (
src/lib/types/api-types.ts) - Use SvelteKit's proxy for API calls in development
- Handle SSR vs CSR with
browsercheck from$app/environment
Updating
This package is automatically published when the backend API changes. To update:
pnpm update @ord-api/ord-api-typesResources
License
MIT
