Documentation Index Fetch the complete documentation index at: https://mintlify.com/polarsource/polar/llms.txt
Use this file to discover all available pages before exploring further.
Integrate Polar seamlessly into your SvelteKit application using server-side load functions, form actions, and API routes.
Installation
npm install @polar-sh/sdk
Setup
Configure environment variables
Add your Polar credentials to .env: POLAR_ACCESS_TOKEN = your_access_token_here
PUBLIC_POLAR_SERVER_URL = https://api.polar.sh
Variables prefixed with PUBLIC_ are exposed to the client. Keep your access token private.
Create Polar utility
Create a server-side utility for Polar client instances: import { PolarCore } from '@polar-sh/sdk/core'
import { POLAR_ACCESS_TOKEN , PUBLIC_POLAR_SERVER_URL } from '$env/static/private'
export function getPolarClient () {
if ( ! POLAR_ACCESS_TOKEN ) {
throw new Error ( 'POLAR_ACCESS_TOKEN is not set' )
}
return new PolarCore ({
serverURL: PUBLIC_POLAR_SERVER_URL || 'https://api.polar.sh' ,
security: {
bearerAuth: POLAR_ACCESS_TOKEN ,
},
})
}
export function getPublicPolarClient () {
return new PolarCore ({
serverURL: PUBLIC_POLAR_SERVER_URL || 'https://api.polar.sh' ,
})
}
Place server-only code in src/lib/server/ to prevent client-side bundling.
Use in page load functions
Fetch data in your page’s +page.server.ts: src/routes/products/+page.server.ts
import { getPolarClient } from '$lib/server/polar'
import { productsSearch } from '@polar-sh/sdk/funcs/productsSearch'
import { error } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
export const load : PageServerLoad = async () => {
const client = getPolarClient ()
const { ok , value : products } = await productsSearch ( client , {
organizationId: 'your-org-id' ,
})
if ( ! ok || ! products ) {
throw error ( 500 , 'Failed to load products' )
}
return {
products: products . items ,
}
}
Use the data in your component: src/routes/products/+page.svelte
< script lang = "ts" >
import type { PageData } from './$types'
export let data : PageData
</ script >
< h1 > Products </ h1 >
< div class = "products" >
{# each data . products as product }
< div class = "product" >
< h2 > { product . name } </ h2 >
< p > { product . description } </ p >
</ div >
{/ each }
</ div >
Handle form submissions with SvelteKit actions:
src/routes/checkout/+page.server.ts
import { getPolarClient } from '$lib/server/polar'
import { checkoutsCreate } from '@polar-sh/sdk/funcs/checkoutsCreate'
import { redirect , fail } from '@sveltejs/kit'
import type { Actions } from './$types'
export const actions : Actions = {
default : async ({ request , url }) => {
const formData = await request . formData ()
const productId = formData . get ( 'productId' ) as string
if ( ! productId ) {
return fail ( 400 , { error: 'Product ID is required' })
}
const client = getPolarClient ()
const { ok , value : checkout , error } = await checkoutsCreate ( client , {
productId ,
successUrl: ` ${ url . origin } /success` ,
})
if ( ! ok ) {
return fail ( 400 , { error: error ?. message || 'Failed to create checkout' })
}
throw redirect ( 303 , checkout . url )
},
}
src/routes/checkout/+page.svelte
< script lang = "ts" >
import { enhance } from '$app/forms'
import type { ActionData } from './$types'
export let form : ActionData
</ script >
< h1 > Checkout </ h1 >
{# if form ?. error }
< div class = "error" > { form . error } </ div >
{/ if }
< form method = "POST" use : enhance >
< input type = "hidden" name = "productId" value = "prod_123" />
< button type = "submit" > Buy Now </ button >
</ form >
API Routes
Create server endpoints for API operations:
src/routes/api/webhooks/polar/+server.ts
import { getPolarClient } from '$lib/server/polar'
import { webhooksValidatePayload } from '@polar-sh/sdk/funcs/webhooksValidatePayload'
import { json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'
export const POST : RequestHandler = async ({ request }) => {
const signature = request . headers . get ( 'webhook-signature' )
const body = await request . text ()
if ( ! signature ) {
return json ({ error: 'Missing signature' }, { status: 401 })
}
const client = getPolarClient ()
const { ok , value : event } = await webhooksValidatePayload ( client , {
webhookSignatureHeader: signature ,
payload: body ,
})
if ( ! ok ) {
return json ({ error: 'Invalid signature' }, { status: 401 })
}
// Handle the webhook event
switch ( event . type ) {
case 'checkout.completed' :
console . log ( 'Checkout completed:' , event . data )
break
case 'subscription.created' :
console . log ( 'Subscription created:' , event . data )
break
default :
console . log ( 'Unhandled event type:' , event . type )
}
return json ({ received: true })
}
Dynamic Routes
Load individual resources with dynamic parameters:
src/routes/products/[id]/+page.server.ts
import { getPolarClient } from '$lib/server/polar'
import { productsGet } from '@polar-sh/sdk/funcs/productsGet'
import { error } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
export const load : PageServerLoad = async ({ params }) => {
const client = getPolarClient ()
const { ok , value : product } = await productsGet ( client , {
id: params . id ,
})
if ( ! ok || ! product ) {
throw error ( 404 , 'Product not found' )
}
return {
product ,
}
}
src/routes/products/[id]/+page.svelte
< script lang = "ts" >
import type { PageData } from './$types'
export let data : PageData
</ script >
< svelte : head >
< title > { data . product . name } </ title >
< meta name = "description" content = { data . product . description || '' } />
</ svelte : head >
< h1 > { data . product . name } </ h1 >
< p > { data . product . description } </ p >
< form method = "POST" action = "/checkout" >
< input type = "hidden" name = "productId" value = { data . product . id } />
< button type = "submit" > Buy Now </ button >
</ form >
Client-Side Interactions
Use progressive enhancement for dynamic features:
src/lib/components/BuyButton.svelte
< script lang = "ts" >
import type { Product } from '@polar-sh/sdk/models/components'
export let product : Product
let loading = false
async function handleBuy () {
loading = true
try {
const response = await fetch ( '/api/checkout' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({ productId: product . id }),
})
const data = await response . json ()
if ( data . url ) {
window . location . href = data . url
}
} catch ( error ) {
console . error ( 'Failed to create checkout:' , error )
} finally {
loading = false
}
}
</ script >
< button on : click = { handleBuy } disabled = { loading } >
{ loading ? 'Processing...' : `Buy ${ product . name } ` }
</ button >
Hooks
Add authentication or session handling in hooks:
import { getPolarClient } from '$lib/server/polar'
import { customersGet } from '@polar-sh/sdk/funcs/customersGet'
import type { Handle } from '@sveltejs/kit'
export const handle : Handle = async ({ event , resolve }) => {
const sessionToken = event . cookies . get ( 'polar_session' )
if ( sessionToken ) {
const client = getPolarClient ()
const { ok , value : customer } = await customersGet ( client , {
id: sessionToken ,
})
if ( ok && customer ) {
event . locals . customer = customer
}
}
return resolve ( event )
}
Define types in src/app.d.ts:
import type { Customer } from '@polar-sh/sdk/models/components'
declare global {
namespace App {
interface Locals {
customer ?: Customer
}
}
}
export {}
Access in load functions:
src/routes/+page.server.ts
import type { PageServerLoad } from './$types'
export const load : PageServerLoad = async ({ locals }) => {
return {
customer: locals . customer ,
}
}
Error Handling
Create custom error pages:
< script lang = "ts" >
import { page } from '$app/stores'
</ script >
< div class = "error-page" >
< h1 > { $ page . status } </ h1 >
< p > { $ page . error ?. message || 'An unexpected error occurred' } </ p >
< a href = "/" > Go home </ a >
</ div >
Handle specific Polar errors:
src/routes/products/[id]/+page.server.ts
import { getPolarClient } from '$lib/server/polar'
import { productsGet } from '@polar-sh/sdk/funcs/productsGet'
import { ResourceNotFound } from '@polar-sh/sdk/models/errors/resourcenotfound'
import { error } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
export const load : PageServerLoad = async ({ params }) => {
try {
const client = getPolarClient ()
const { ok , value : product , error : sdkError } = await productsGet ( client , {
id: params . id ,
})
if ( ! ok ) {
if ( sdkError instanceof ResourceNotFound ) {
throw error ( 404 , 'Product not found' )
}
throw error ( 500 , 'Failed to load product' )
}
return { product }
} catch ( err ) {
if ( err instanceof Error && 'status' in err ) {
throw err
}
throw error ( 500 , 'An unexpected error occurred' )
}
}
Prerendering
Prerender static pages at build time:
src/routes/products/+page.server.ts
import { getPolarClient } from '$lib/server/polar'
import { productsSearch } from '@polar-sh/sdk/funcs/productsSearch'
import type { PageServerLoad } from './$types'
export const prerender = true
export const load : PageServerLoad = async () => {
const client = getPolarClient ()
const { ok , value : products } = await productsSearch ( client , {
organizationId: 'your-org-id' ,
})
return {
products: products ?. items || [],
}
}
Best Practices
Server-only code Keep sensitive code in src/lib/server/ to prevent client bundling.
Progressive enhancement Build forms that work without JavaScript using SvelteKit actions.
Type safety Use generated TypeScript types for better developer experience.
Prerender when possible Use prerendering for static content to improve performance.
Next Steps
Checkout Implement Polar Checkout in your SvelteKit app.
Webhooks Set up webhook handlers for real-time events.
TypeScript SDK Explore the full TypeScript SDK.
API Reference Browse the complete API reference.