Framework Guides
Corral embeds into your existing framework. The JS/TS adapters handle mounting routes and providing components. Non-JS backends validate sessions by calling Corral's session endpoint.
Auto-detected frameworks in v0.4.1
Corral embeds directly into existing servers/frameworks (no rewrite).
Detected: Express, Hono, Fastify, Koa, Hapi, Polka, Restify, NestJS, AdonisJS, Elysia, H3/Nitro, Oak, Sails, Next.js, Vite+React, CRA, FastAPI, Django, Flask.
Next.js App Router
Next.js โ App Router
The recommended setup for Next.js 13+. Corral mounts as catch-all route handlers.
bash
npm install @corral/core @corral/next
src/lib/corral.tstypescript
import { createCorral } from '@corral/core'
import { NextAdapter } from '@corral/next'
import config from '../../../corral.yaml'
export const corral = createCorral({ config, adapter: NextAdapter })
export const { auth, billing, getSession, requireSession } = corral
src/app/api/auth/[...corral]/route.tstypescript
import { corral } from '@/lib/corral'
export const { GET, POST } = corral.auth.handlers
src/app/api/billing/[...corral]/route.tstypescript
import { corral } from '@/lib/corral'
export const { GET, POST } = corral.billing.handlers
Server Components
src/app/dashboard/page.tsxtypescript
import { getSession } from '@/lib/corral'
import { FeatureGate } from '@corral/next'
import { redirect } from 'next/navigation'
export default async function DashboardPage() {
const session = await getSession()
if (!session) redirect('/login')
return (
<div>
<h1>Welcome, {session.user.name}</h1>
<p>Plan: {session.plan.id}</p>
{/* Only rendered for pro/enterprise users */}
<FeatureGate feature="ai_analysis" session={session}>
<AIAnalysisPanel />
</FeatureGate>
</div>
)
}
Route protection middleware
middleware.tstypescript
import { corralMiddleware } from '@corral/next'
import type { NextRequest } from 'next/server'
export function middleware(req: NextRequest) {
return corralMiddleware(req, {
// Protect these routes โ redirect to /login if not authenticated
protectedRoutes: ['/dashboard', '/reports', '/admin'],
// Require pro plan for these routes
planRoutes: {
'/reports/advanced': 'pro',
'/admin': { role: 'admin' },
}
})
}
export const config = {
matcher: ['/dashboard/:path*', '/reports/:path*', '/admin/:path*']
}
Next.js Pages Router
Next.js โ Pages Router
pages/api/auth/[...corral].tstypescript
import { corral } from '@/lib/corral'
export default corral.auth.pageHandler
pages/dashboard.tsxtypescript
import { getServerSideSession } from '@corral/next'
import type { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getServerSideSession(ctx)
if (!session) return { redirect: { destination: '/login', permanent: false } }
return { props: { session } }
}
React + Vite
React + Vite
With Vite, Corral runs on your backend (Express/Hono/Fastify),
and the React frontend uses the @corral/react client package.
bash
npm install @corral/core @corral/express @corral/react # frontend + backend
src/main.tsxtsx
import { CorralProvider } from '@corral/react'
import App from './App'
createRoot(document.getElementById('root')!).render(
<CorralProvider baseUrl="/api">
<App />
</CorralProvider>
)
src/components/Dashboard.tsxtsx
import { useSession, FeatureGate } from '@corral/react'
export function Dashboard() {
const { session, loading } = useSession()
if (loading) return <Spinner />
if (!session) return <LoginPrompt />
return (
<div>
<FeatureGate feature="ai_analysis">
<AIPanel />
</FeatureGate>
</div>
)
}
Express
Express
src/server.tstypescript
import express from 'express'
import { createCorral } from '@corral/core'
import { ExpressAdapter } from '@corral/express'
import config from '../corral.yaml'
const app = express()
const corral = createCorral({ config, adapter: ExpressAdapter })
// Mount Corral routes
app.use('/api/auth', corral.auth.router)
app.use('/api/billing', corral.billing.router)
// Protected route
app.get('/api/intel', corral.requireSession(), async (req, res) => {
const { session } = req
if (!corral.hasFeature(session, 'ai_analysis')) {
return res.status(403).json({ error: 'upgrade_required' })
}
res.json({ data: 'classified' })
})
app.listen(3000)
Hono
Hono
Hono runs on Node.js, Cloudflare Workers, Bun, and Deno. Use @corral/hono.
src/index.tstypescript
import { Hono } from 'hono'
import { corralHono } from '@corral/hono'
import config from './corral.yaml'
const app = new Hono()
const { auth, billing, session } = corralHono({ config })
app.route('/api/auth', auth)
app.route('/api/billing', billing)
// Protected route with plan gate
app.get('/api/intel', session({ feature: 'ai_analysis' }), (c) => {
return c.json({ data: 'classified' })
})
export default app
Fastify
Fastify
src/server.tstypescript
import Fastify from 'fastify'
import corralPlugin from '@corral/fastify'
const app = Fastify()
await app.register(corralPlugin, {
configPath: './corral.yaml',
prefix: '/api'
})
app.get('/api/intel', {
preHandler: [app.corral.requireFeature('ai_analysis')]
}, async (req, reply) => {
return { data: 'classified', user: req.corralSession.user.email }
})
Python ยท FastAPI
Python โ FastAPI
Non-JS backends validate sessions by calling Corral's session endpoint.
Use the corral-py helper library, or call the API directly.
bash
pip install corral-py
app/auth.pypython
from corral_py import CorralClient
from fastapi import Depends, HTTPException, Request
corral = CorralClient(base_url="https://horizon.mil")
async def require_session(request: Request):
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
session = await corral.get_session(token)
if not session:
raise HTTPException(status_code=401)
return session
async def require_feature(feature: str):
async def dep(session=Depends(require_session)):
if feature not in session["features"]:
raise HTTPException(status_code=403, detail="upgrade_required")
return session
return dep
# Usage in route:
from fastapi import APIRouter
router = APIRouter()
@router.get("/intel/analysis")
async def get_analysis(session=Depends(require_feature("ai_analysis"))):
return {"analyst": session["user"]["email"], "data": "classified"}
Go
Go
bash
go get github.com/llama-farm/corral-go
middleware/auth.gogo
package middleware
import (
"net/http"
corral "github.com/llama-farm/corral-go"
)
var client = corral.NewClient("https://horizon.mil")
func RequireFeature(feature string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session, err := client.GetSession(r)
if err != nil || !session.HasFeature(feature) {
http.Error(w, "forbidden", 403)
return
}
next.ServeHTTP(w, r.WithContext(ContextWithSession(r.Context(), session)))
})
}
}
Rust ยท Axum
Rust โ Axum
Cargo.tomltoml
[dependencies]
corral = "0.1"
src/auth.rsrust
use axum::{extract::FromRequestParts, http::StatusCode};
use corral::{CorralClient, Session};
pub struct RequireFeature(pub Session);
impl<S> FromRequestParts<S> for RequireFeature {
type Rejection = StatusCode;
async fn from_request_parts(parts: &mut Parts, _: &S)
-> Result<Self, StatusCode>
{
let token = extract_bearer(&parts.headers)
.ok_or(StatusCode::UNAUTHORIZED)?;
let session = CORRAL_CLIENT.get_session(&token).await
.map_err(|_| StatusCode::UNAUTHORIZED)?;
Ok(RequireFeature(session))
}
}
Ruby ยท Rails
Ruby โ Rails
bash
gem install corral-ruby
app/controllers/application_controller.rbruby
class ApplicationController < ActionController::Base
include Corral::Rails
CORRAL_BASE_URL = "https://horizon.mil"
before_action :require_session!
def require_feature!(feature)
unless corral_session["features"].include?(feature.to_s)
render json: { error: "upgrade_required" }, status: :forbidden
end
end
end
# In IntelController:
class IntelController < ApplicationController
before_action { require_feature! :ai_analysis }
def analysis
render json: { analyst: corral_session["user"]["email"] }
end
end