API Application
The API application (apps/api
) is the backend server that handles all business logic, database operations, and external service integrations. Built with Next.js 15 App Router for serverless deployment.
Why Next.js for the API?
Section titled “Why Next.js for the API?”While you can replace this with any TypeScript framework, we chose Next.js because:
🚀 Serverless-First
Section titled “🚀 Serverless-First”- Automatic scaling - Handle traffic spikes without configuration
- Edge deployment - Deploy to Vercel, AWS, or any serverless platform
- Zero cold starts - Optimized for serverless performance
- Built-in optimizations - Automatic code splitting and bundling
🔧 Developer Experience
Section titled “🔧 Developer Experience”- TypeScript native - Full type safety out of the box
- File-based routing - API routes match file structure
- Built-in middleware - Request/response handling
- Hot reload - Instant development feedback
🏗️ Production Ready
Section titled “🏗️ Production Ready”- Automatic HTTPS - SSL certificates handled automatically
- Built-in monitoring - Error tracking and performance metrics
- Easy deployment - One command to deploy to production
- Environment management - Secure environment variable handling
Architecture
Section titled “Architecture”Framework: Next.js 15 App Router (Serverless)
Port: 3002
(development)
Database: Neon PostgreSQL with Drizzle ORM
Authentication: Custom JWT-based authentication
Deployment: Vercel (or any serverless platform)
Alternative Backend Frameworks
Section titled “Alternative Backend Frameworks”While we provide a complete Next.js API, you can replace it with any TypeScript framework:
🚀 High-Performance Options
Section titled “🚀 High-Performance Options”- Fastify - High-performance alternative to Express
- Hono - Lightweight, edge-first framework
- Bun - Ultra-fast JavaScript runtime with built-in HTTP server
🏗️ Enterprise Frameworks
Section titled “🏗️ Enterprise Frameworks”- NestJS - Enterprise-grade framework with decorators
- Express - Most popular Node.js framework
- Koa - Modern Express alternative
🔗 Type-Safe APIs
Section titled “🔗 Type-Safe APIs”- tRPC - End-to-end typesafe APIs
- GraphQL - Query language with strong typing
- gRPC - High-performance RPC framework
🌐 Edge-First
Section titled “🌐 Edge-First”- Cloudflare Workers - Edge computing platform
- Deno Deploy - Deno’s serverless platform
- Vercel Edge Functions - Edge runtime for Vercel
All maintain the same TypeScript types and monorepo structure!
Structure
Section titled “Structure”apps/api/
├── app/
│ ├── auth/
│ │ ├── login/route.ts # User login endpoint
│ │ ├── logout/route.ts # User logout endpoint
│ │ ├── me/route.ts # Get current user
│ │ └── register/route.ts # User registration endpoint
│ ├── account/
│ │ ├── delete/route.ts # Delete user account
│ │ └── profile/route.ts # User profile management
│ ├── billing-portal/route.ts # Stripe Customer Portal
│ ├── checkout/route.ts # Stripe Checkout Sessions
│ ├── health/route.ts # Health check endpoint
│ ├── preferences/route.ts # User preferences CRUD
│ ├── subscription/route.ts # Subscription management
│ ├── tasks/[id]/route.ts # Task CRUD operations
│ ├── tasks/route.ts # Task list operations
│ └── webhooks/stripe/route.ts # Stripe webhook handler
├── lib/
│ └── validation.ts # Zod error formatting
├── middleware.ts # Request middleware
└── package.json # Dependencies and scripts
API Endpoints
Section titled “API Endpoints”Authentication
Section titled “Authentication”POST /auth/register # Create new user account
POST /auth/login # Sign in with email/password
GET /auth/me # Get current user info
POST /auth/logout # Sign out (clear cookie)
Account Management
Section titled “Account Management”GET /account/profile # Get user profile
PATCH /account/profile # Update user profile
DELETE /account/delete # Delete user account
Example Registration:
// POST /auth/register
const response = await fetch("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: "John Doe",
email: "john@example.com",
password: "securepassword123",
}),
});
Example Login:
// POST /auth/login
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "john@example.com",
password: "securepassword123",
}),
});
// Returns: { success: true, token: "...", user: {...} }
Health Check
Section titled “Health Check”GET /health
Returns 200 OK
for health monitoring.
GET /tasks # List user's tasks
POST /tasks # Create new task
PATCH /tasks/[id] # Update task
DELETE /tasks/[id] # Delete task
Example Request:
// GET /tasks (uses httpOnly cookie for auth)
const response = await fetch("/api/tasks");
// POST /tasks
const response = await fetch("/api/tasks", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: "Complete project",
description: "Finish the Orion Kit documentation",
status: "todo",
}),
});
User Preferences
Section titled “User Preferences”GET /preferences # Get user preferences
PATCH /preferences # Update user preferences
Example Response:
{
"success": true,
"data": {
"plan": "pro",
"defaultStatus": "todo",
"emailNotifications": true,
"taskReminders": false,
"weeklyDigest": true,
"stripeCustomerId": "cus_1234567890",
"stripeSubscriptionId": "sub_1234567890",
"stripeSubscriptionStatus": "active"
}
}
Billing & Payments
Section titled “Billing & Payments”POST /checkout # Create Stripe Checkout Session
POST /billing-portal # Create Stripe Customer Portal Session
GET /subscription # Get subscription details
DELETE /subscription # Cancel subscription
Example Checkout:
// POST /checkout
const response = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
priceId: "price_1234567890",
}),
});
// Returns: { success: true, data: { url: "https://checkout.stripe.com/..." } }
Webhooks
Section titled “Webhooks”POST /webhooks/stripe # Stripe webhook handler
Handles Stripe events:
checkout.session.completed
- New subscriptioncustomer.subscription.updated
- Plan changescustomer.subscription.deleted
- Cancellationinvoice.payment_succeeded
- Successful paymentinvoice.payment_failed
- Failed payment
Authentication
Section titled “Authentication”All protected endpoints require authentication via JWT tokens stored in httpOnly cookies:
import { getCurrentUser } from "@workspace/auth/server";
export async function GET(req: NextRequest) {
const user = await getCurrentUser(req);
if (!user) {
return NextResponse.json(
{ success: false, error: "Unauthorized" },
{ status: 401 }
);
}
// Authenticated logic here - user.id is available
}
Authentication Flow
Section titled “Authentication Flow”- Login/Register - User submits credentials
- JWT Creation - Server creates signed JWT token
- Cookie Storage - Token stored in httpOnly cookie
- Automatic Verification - Middleware verifies token on protected routes
- User Context -
getCurrentUser()
extracts user info from token
Database Operations
Section titled “Database Operations”Uses Drizzle ORM with type-safe queries:
import { db, tasks, userPreferences } from "@workspace/database";
import { eq } from "drizzle-orm";
// Get user's tasks
const userTasks = await db.select().from(tasks).where(eq(tasks.userId, userId));
// Update user preferences
await db
.update(userPreferences)
.set({ plan: "pro" })
.where(eq(userPreferences.userId, userId));
Validation
Section titled “Validation”All requests are validated with Zod schemas:
import { createTaskInputSchema } from "@workspace/types";
import { validationErrorResponse } from "@/lib/validation";
export async function POST(request: Request) {
try {
const body = await request.json();
const validated = createTaskInputSchema.parse(body);
// Process validated data
} catch (error) {
if (error instanceof ZodError) {
return validationErrorResponse(error.errors);
}
throw error;
}
}
Error Handling
Section titled “Error Handling”Consistent error responses:
// Validation error
{
"success": false,
"error": "Validation failed",
"details": [
"Title is required - title",
"Status must be one of: todo, in-progress, completed - status"
]
}
// Authentication error
{
"success": false,
"error": "Unauthorized"
}
// Server error
{
"success": false,
"error": "Internal server error"
}
Environment Variables
Section titled “Environment Variables”# apps/api/.env.local
DATABASE_URL=postgresql://...
AUTH_JWT_SECRET=your-super-secret-key-min-32-chars
NEXT_PUBLIC_APP_URL=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3002
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_AXIOM_TOKEN=xaat-...
NEXT_PUBLIC_AXIOM_DATASET=orion-kit
Development
Section titled “Development”Start Development Server
Section titled “Start Development Server”cd apps/api
pnpm dev
Server runs on http://localhost:3002
API Testing
Section titled “API Testing”# Health check
curl http://localhost:3002/health
# Register user
curl -X POST http://localhost:3002/auth/register \
-H "Content-Type: application/json" \
-d '{"name":"Test User","email":"test@example.com","password":"password123"}'
# Login (sets httpOnly cookie)
curl -X POST http://localhost:3002/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password123"}' \
-c cookies.txt
# Get tasks (uses cookie for auth)
curl -b cookies.txt http://localhost:3002/tasks
Database Studio
Section titled “Database Studio”# Open Drizzle Studio
pnpm db:studio
Environment Variables
Section titled “Environment Variables”Set production environment variables in Vercel dashboard:
AUTH_JWT_SECRET
- JWT signing secret (32+ characters)DATABASE_URL
- Production database URLNEXT_PUBLIC_APP_URL
- Production app URLNEXT_PUBLIC_API_URL
- Production API URLSTRIPE_SECRET_KEY
- Stripe live keySTRIPE_WEBHOOK_SECRET
- Stripe webhook secretNEXT_PUBLIC_AXIOM_TOKEN
- Axiom logging tokenNEXT_PUBLIC_AXIOM_DATASET
- Axiom dataset name
Webhook Configuration
Section titled “Webhook Configuration”Configure Stripe webhook endpoint:
https://api.orion-kit.dev/webhooks/stripe
Events to listen for:
checkout.session.completed
customer.subscription.updated
customer.subscription.deleted
invoice.payment_succeeded
invoice.payment_failed
Monitoring
Section titled “Monitoring”Health Checks
Section titled “Health Checks”Monitor API health:
# Basic health check
curl https://api.orion-kit.dev/health
# Should return: OK
Logging
Section titled “Logging”Logs are sent to Axiom for monitoring:
import { logger } from "@workspace/observability";
logger.info("Task created", { userId, taskId });
logger.error("Payment failed", { userId, error: error.message });
Analytics
Section titled “Analytics”Track API usage with PostHog:
import { track } from "@workspace/analytics";
track("task_created", { userId, taskId });
track("subscription_upgraded", { userId, plan: "pro" });