📘️ apity - Typed API client for Svelte and SvelteKit
A typed fetch client for openapi-typescript compatible with SvelteKit's custom fetch
Installation
npm install @cocreators-ee/apity
Or
pnpm add @cocreators-ee/apity
Features
On the roadmap:
Live demo
Apity has a live demo with code samples.
Also you can read an introductionary blog post.
Usage
Generate typescript definition from schema
Before working with the library, you need to generate an API spec using openapi-typescript:
npx openapi-typescript https://petstore3.swagger.io/api/v3/openapi.json --output src/petstore.ts
🚀 https://petstore3.swagger.io/api/v3/openapi.json → file:./src/petstore.ts [870ms]
Using Apity
Configure Apity instance and generate functions for making API calls:
import { Apity } from '@cocreators-ee/apity'
import type { paths } from 'src/petstore'
const apity = Apity.for<paths>()
apity.configure({
baseUrl: 'https://petstore.swagger.io/v2',
init: {
},
})
export const findPetsByStatus = apity
.path('/pet/findByStatus')
.method('get')
.create()
export const addPet = apity.path('/pet').method('post').create()
Each API call is represented as a request object that has the following properties:
type ApiRequest<R = any> = {
readonly resp: Writable<ApiResponse<R> | undefined>
readonly ready: Writable<undefined | Promise<ApiResponse<R>>>
reload: () => Promise<ApiResponse<R>>
result: Promise<ApiResponse<R>>
}
Each response is a Svelte store returning either an undefined
, or the following object:
type SuccessfulResp<R> = {
ok: true
data: R
status: number
}
type FailedResp = {
ok: false
data: any
status: number
}
type ApiResponse<R> = SuccessfulResp<R> | FailedResp
Error handling
There are certain conditions under which an API request could throw an exception without
actually reaching the desired server, for example, unpredictable network issues. For such
cases, the api response will contain a status set to a negative number, indicating that
an exception was thrown.
{
ok: false,
status: -1,
data: undefined,
}
Using Apity with await syntax in templates
Assuming you've created an src/api.ts
from using Apity section:
<script lang="ts">
import { findPetByStatus } from 'src/api.ts'
const request = findPetByStatus({ status: 'sold' })
const petsReady = request.ready
</script>
<div>
{#await $petsReady}
<p>Loading..</p>
{:then resp}
{#if resp.ok}
{#each resp.data as pet}
<p>{pet.name}</p>
{/each}
{:else}
<p>Error while loading pets</p>
{/if}
{/await}
<button on:click={() => {request.reload()}}>
Reload pets
</button>
</div>
Subscribing to response store
Assuming you've created an src/api.ts
from using Apity section:
<script lang="ts">
import { findPetByStatus } from 'src/api.ts'
const request = findPetByStatus({ status: 'sold' })
let names = []
request.resp.subscribe(resp => {
if (resp.ok) {
names = resp.data.map(pet => pet.name)
}
})
</script>
<div>
{#each names as name}
<p>{name}</p>
{/each}
</div>
Using in load functions
Fetch operations support SvelteKit's load function from +page.ts
and +page.server.ts
.
Assuming you've created an src/api.ts
from using Apity section:
import { findPetByStatus } from 'src/api.ts'
export async function load({ fetch }) {
const request = findPetByStatus({ status: 'sold' })
const resp = await request.result
if (resp.ok) {
return { pets: resp.data, error: undefined }
} else {
return { pets: [], error: 'Failed to load pets' }
}
}
Financial support
This project has been made possible thanks to Cocreators. You can help us continue our open source work by supporting us on Buy me a coffee.