API Concepts
Understanding the Corsair approach to natural language APIs
API Concepts
The Problem with Vibe Coding
When we write a prompt to an LLM and get code back, we face a familiar problem: we forget what we asked for.
A few days later, you encounter this code:
const filteredUsers = users.filter(
u =>
u.status === 'active' &&
u.lastLogin > thirtyDaysAgo &&
u.subscription?.tier !== 'free'
)What was the original intent? You're stuck prompting the LLM again to figure out what this code does, or worse—guessing.
The Corsair Approach
Corsair solves this by keeping the prompt inline with your code:
const activeUsers = useCorsairQuery(
'active paid users who logged in within the last 30 days'
)The natural language is your code. Your intent is preserved forever.
How It Works
When you save a query with a prompt, Corsair immediately calls the LLM and generates the implementation.
- This happens at development time, not runtime
- The generated code is real TypeScript that compiles with your app
- Click any query to view and edit the generated code
- Don't like how it's written? Change it—there's no magical, inaccessible code
Type Safety Without the any
Here's the crucial difference between Corsair and raw LLM output:
Traditional Vibe Coding
// LLM generates this
const result = await fetchData() // result: any 😬Corsair
const posts = useCorsairQuery(
'all published posts with comments by author id',
{ authorId: string }
)
const data = posts({ authorId: 'abc123' })
// data is fully typed: Array<{
// id: string;
// title: string;
// content: string;
// publishedAt: Date;
// comments: Array<{
// id: string;
// content: string;
// createdAt: Date;
// }>
// }>What you get:
- Complete TypeScript types for every query
- Fully typed arguments and responses
- Autocomplete, type checking, and refactoring support
- All from natural language
Queries: Reading Data
Simple queries abstract away the boilerplate:
// Instead of writing joins and conditions
const data = useCorsairQuery('users with more than 5 orders in the last month')// Complex aggregations become readable
const stats = useCorsairQuery(
'average order value by user segment, grouped by month'
)Integrates with your stack:
- Works with Prisma, Drizzle, or your ORM of choice
- Integrates with TanStack Query for caching
- Compatible with tRPC for end-to-end type safety
Mutations: Writing Data
Mutations work the same way:
const createUser = useCorsairMutation(
'create user with email and hashed password'
)
await createUser.mutate({
email: 'user@example.com',
hashedPassword: '...',
})Plugins: Beyond the Database
Corsair extends natural language beyond your database through plugins.
The Key Difference
You're not vibe coding entire integrations from scratch. You're using vetted, type-safe Corsair plugins.
- The LLM only handles stringing plugins together
- When third-party APIs change, Corsair updates automatically
- Your code remains safe because plugins are maintained, not generated
Cross-Service Mutations
const placeOrder = useCorsairMutation(
'create order and send slack notification about order quantity and destination'
)
await placeOrder.mutate({
productId: '123',
quantity: 5,
destination: 'New York, NY',
slackChannel: '#orders',
})Multi-Step Workflows
const onboardUser = useCorsairMutation(
'create user account, send welcome email, and create onboarding tasks'
)Available plugins:
- Email services (SendGrid, Resend)
- Messaging platforms (Slack, Discord)
- Payment processors (Stripe, PayPal)
- Any service with an API
Schema Changes: Automatic Migration
Your queries and mutations update automatically when your database schema changes.
The Scenario
You've built your entire app with a users table that has a full_name field. Then you decide to split it into first_name and last_name.
Traditional Approach
- Manually update every query, mutation, and API endpoint
- Miss one? Runtime error
- Hours of tedious refactoring
Corsair Approach
- You update your schema
- Corsair detects type mismatches between your ORM and your queries
- Corsair regenerates every affected endpoint in seconds
Why This Works
Because Corsair retains the intent of your code ("get user's full name for display"), it knows how to adapt the implementation to match your new schema.
Example:
// Before schema change - Corsair generated this:
const userName = useCorsairQuery("user's full name by id", { userId: string })
// Implementation: SELECT full_name FROM users WHERE id = ?
// After schema change - Corsair automatically updates to:
const userName = useCorsairQuery("user's full name by id", { userId: string })
// Implementation: SELECT first_name, last_name FROM users WHERE id = ?
// Returns: { firstName: string, lastName: string }Your intent stays the same. The implementation adapts.
Guardrails for Production
Corsair isn't about blind trust in AI—it's about safe abstractions with escape hatches.
Type safety
Every query is validated against your schema at build time.
Code visibility
Click any query to see the generated implementation.
Editable implementations
Don't like how the LLM wrote something? Change it. It's your code.
Version control
Generated code is committed to your repo. Review it in PRs like any other code.
Incremental adoption
Use Corsair for new features while keeping existing code unchanged.
The Goal
Corsair provides a type-safe natural language API that:
- Preserves intent by keeping prompts inline with code
- Generates fully-typed implementations at development time
- Updates automatically when schemas or third-party APIs change
- Offers full control over generated code
- Extends beyond databases to any service integration
You get the speed of vibe coding with the safety and maintainability of hand-written code.