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