Astro
Wire Kizlo into an Astro app and make your first call.
This quickstart covers the app side. Make sure WordPress is ready first; see Installation. Either connect your own WordPress with the plugin and an Application Password, or let Kizlo run one locally in Docker.
Set up Kizlo in a new project
Starting fresh? Scaffold an Astro app with Kizlo already wired:
npx kizlo@latest create astro my-appIt scaffolds a fully wired project and walks you through your WordPress credentials. When it
finishes, cd my-app.
Set up Kizlo in an existing project
Add Kizlo to an app you already have. Use the CLI (recommended) or wire the files by hand. Both produce the same result.
Kizlo renders on demand, so the Astro project runs on the server. Make sure your astro.config.mjs
sets output: "server" with a server adapter such as @astrojs/node. create and init handle
this for you.
CLI setup (recommended)
Run the initializer in your project root:
npx kizlo@latest initIt detects your framework, src dir (if present), package manager, import alias, etc. and scaffolds:
Along the way it asks whether to run WordPress locally in Docker or connect your own, writes
the matching credentials to .env, and generates the contract. When init finishes, you're
ready to make your first call.
Kizlo's SEO tags render through a BaseHead.astro component you drop into your layout's <head>.
init can't edit a layout you own, so it prints one manual step at the end: add BaseHead to your
root layout's <head>.
---
import BaseHead from "../components/BaseHead.astro"
---
<head>
<BaseHead />
</head>Feel free to customize this folder structure however you like. Point the dir option in
kizlo.config.ts at it, so kizlo knows where your server files and kizlo instance live and can
generate the server contract.
Manual setup
Prefer to wire it yourself? Create the same files the CLI would.
Install the package
npm install kizlo@latestAdd kizlo.config.ts
Add this at your project root. It tells the CLI where Kizlo lives and which import alias to use:
import { defineConfig } from "kizlo/config"
export default defineConfig({
dir: "src/lib/kizlo",
alias: "@",
})Enable server output
Kizlo renders on demand, so Astro runs on the server. In astro.config.mjs, set output: "server"
and add a server adapter:
// @ts-check
import node from "@astrojs/node"
import { defineConfig } from "astro/config"
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
})Create the server
At src/lib/kizlo/server/index.ts. Pass Astro's native getSecret reader so astro() can map its Kizlo
values:
import { getSecret } from "astro:env/server"
import { createKizlo } from "kizlo"
import { astro } from "kizlo/astro/server"
export const { procedures, client, context, handler } = createKizlo({
integrations: [astro({ env: getSecret })],
})Mount the route handler
At src/pages/api/kizlo/[...rest].ts, ALL forwards every HTTP method to the server's handler:
import type { APIRoute } from "astro"
import { handler } from "../../../lib/kizlo/server"
export const ALL: APIRoute = ({ request }) => handler(request)Create the client
Create src/lib/kizlo/client.ts from the generated contract. It is typed automatically. Pass the public
API URL at the call site so Vite inlines it into the browser bundle:
import { createKizloClient } from "kizlo/astro"
import { contract } from "./server/generated"
export const client = createKizloClient(contract, { url: import.meta.env.PUBLIC_KIZLO_BASE_URL })Environment
Finally, connect WordPress. Use a local stack while you build and your hosted WordPress in production.
Local
Let Kizlo run WordPress for you. There is nothing to fill in:
npx kizlo devIt starts WordPress in Docker, writes its dev credentials to .env for you, and watches your server files
to keep the contract in sync. This gives you one command for the whole local loop.
Remote
For your hosted WordPress, add its credentials to .env:
KIZLO_WP_URL=https://your-site.com
KIZLO_WP_USERNAME=admin
KIZLO_WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
KIZLO_WP_SECRET= # a long random string
PUBLIC_KIZLO_BASE_URL=http://localhost:4321/api/kizloGenerate KIZLO_WP_SECRET with openssl rand -hex 32. While developing, run npx kizlo dev to keep
the generated contract in sync as you edit your server files.
Render a post
Here is a complete dynamic blog post route. A single page fetches the post
by slug and maps its SEO straight into the document <head>:
resolvePageHead: turns the post's SEO head into the flat descriptorsBaseHead.astrorenders as<title>, canonical, Open Graph, and Twitter tags, so you never hand-write meta tags.BaseHead.astro: the shared head componentinitwires into your layout; brand and site data (icons, web manifest, theme color) come from WordPress settings.
Each route renders on demand for its slug, so there's no build-time list step.
---
import { resolvePageHead } from "kizlo/astro/server"
import Layout from "../../layouts/Layout.astro"
import { client } from "../../lib/kizlo/server"
const { slug } = Astro.params
const { data } = await client.posts.get({ params: { identifier: slug ?? "" } })
if (!data) {
return new Response(null, { status: 404 })
}
// SEO tags (title, canonical, Open Graph, Twitter) mapped straight from the post's SEO head.
const head = data.seo ? resolvePageHead(data.seo.head) : undefined
---
<Layout head={head}>
<article>
<h1>{data.title ?? "Untitled"}</h1>
<div set:html={data.content ?? ""} />
</article>
</Layout>From here, head to Concepts to see how procedures, integrations, and adapters compose into something production-ready.