UNPKG

@limitly/limitly-nextjs

Version:

Official Next.js SDK for Limitly - API Key management, plans, users and request validation optimized for server-side

540 lines (416 loc) • 12.5 kB
# @limitly/limitly-nextjs Official Next.js SDK for [Limitly](https://www.limitly.dev) - API Key management, plans, users and request validation optimized for server-side rendering. ## šŸš€ Installation ```bash npm install @limitly/limitly-nextjs ``` ```bash pnpm add @limitly/limitly-nextjs ``` ```bash yarn add @limitly/limitly-nextjs ``` ## šŸ“– Basic Usage ### Initialization ```typescript import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: 'your_limitly_api_key' }); ``` ### Request Validation in Next.js API Routes The most common use case is validating your users' requests in Next.js API routes: ```typescript // app/api/users/route.ts import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); export async function GET(request: Request) { const apiKey = request.headers.get('authorization')?.replace('Bearer ', ''); if (!apiKey) { return Response.json({ error: 'API Key required' }, { status: 401 }); } const result = await limitly.validation.validate( apiKey, '/api/users', 'GET' ); if (!result.success) { return Response.json({ error: 'Rate limit exceeded', details: result.details }, { status: 429 }); } // Your API logic here return Response.json({ message: 'Request allowed' }); } ``` ### Using the withRateLimit Wrapper For cleaner code, use the built-in rate limiting wrapper: ```typescript // app/api/users/route.ts import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); const apiHandler = async (request: Request) => { // Your API logic here return Response.json({ message: 'Success', data: { users: [] } }); }; // Wrap your handler with rate limiting export const GET = limitly.withRateLimit(apiHandler); export const POST = limitly.withRateLimit(apiHandler); ``` ### Middleware for Express-style APIs If you're using Express-style middleware: ```typescript // pages/api/users.ts (Pages Router) import { Limitly } from '@limitly/limitly-nextjs'; import type { NextApiRequest, NextApiResponse } from 'next'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); const rateLimitMiddleware = limitly.createMiddleware({ onRateLimitExceeded: (req, res) => { res.status(429).json({ error: 'Custom rate limit message' }); } }); export default async function handler(req: NextApiRequest, res: NextApiResponse) { // Apply rate limiting middleware await rateLimitMiddleware(req, res, () => { // Your API logic here res.json({ message: 'Success' }); }); } ``` ## šŸ”§ Configuration ### Configuration Options ```typescript const limitly = new Limitly({ apiKey: 'your_limitly_api_key', baseUrl: 'https://xfkyofkqbukqtxcuapvf.supabase.co/functions/v1', // optional timeout: 30000 // optional, default: 30000ms }); ``` ### Next.js Specific Configuration ```typescript const limitly = new Limitly({ apiKey: 'your_limitly_api_key', cache: true, // Enable Next.js caching revalidate: 3600 // Cache for 1 hour }); ``` ### Request Options with Caching ```typescript // Get data with caching for SSR const plans = await limitly.plans.list({ cache: true, revalidate: 3600, // Cache for 1 hour timeout: 10000 }); // Get real-time data without caching const usage = await limitly.apiKeys.getUsage('key-id', { cache: false }); ``` ## šŸ“š Complete API ### Request Validation #### `validation.validate(apiKey, endpoint, method, options?)` Validates a user request. ```typescript const result = await limitly.validation.validate( 'user_api_key', '/api/users', 'GET' ); ``` #### `validation.validateWithContext(apiKey, endpoint, method, context?, options?)` Validates a request with additional context for Next.js. ```typescript const result = await limitly.validation.validateWithContext( 'user_api_key', '/api/users', 'GET', { userId: 123, sessionId: 'abc123' } ); ``` ### API Keys #### `apiKeys.list(options?)` Lists all API Keys. #### `apiKeys.listWithUsage(options?)` Lists API Keys with usage statistics (Next.js optimized). #### `apiKeys.create(data, options?)` Creates a new API Key. ```typescript const key = await limitly.apiKeys.create({ name: 'New API Key', user_id: 123, // optional plan_id: 'plan-id', // optional status: 'active' // optional }); ``` #### `apiKeys.get(keyId, options?)` Gets a specific API Key. #### `apiKeys.update(keyId, data, options?)` Updates an API Key. #### `apiKeys.delete(keyId, options?)` Deletes an API Key (soft delete). #### `apiKeys.regenerate(keyId, options?)` Regenerates an API Key. #### `apiKeys.getUsage(keyId, options?)` Gets usage statistics for an API Key. #### `apiKeys.getRequests(keyId, options?)` Gets request history for an API Key. ### Plans #### `plans.list(options?)` Lists all plans. #### `plans.listWithUsage(options?)` Lists plans with usage statistics (Next.js optimized). #### `plans.create(data, options?)` Creates a new plan. ```typescript const plan = await limitly.plans.create({ name: 'Basic Plan', description: 'Plan for basic users', max_requests: 10000, request_period: 'month', // 'day', 'week', 'month', 'year' is_active: true }); ``` #### `plans.get(planId, options?)` Gets a specific plan. #### `plans.update(planId, data, options?)` Updates a plan. #### `plans.delete(planId, options?)` Deletes a plan. #### `plans.getUsage(planId, options?)` Gets usage statistics for a plan. #### `plans.getUsers(planId, options?)` Gets all users assigned to a plan. #### `plans.getKeys(planId, options?)` Gets all API Keys assigned to a plan. ### Users #### `users.list(options?)` Lists all users. #### `users.listWithUsage(options?)` Lists users with usage statistics (Next.js optimized). #### `users.create(data, options?)` Creates a new user. ```typescript const user = await limitly.users.create({ name: 'John Doe', email: 'john@example.com', // optional plan_id: 'plan-id', // optional custom_start: '2024-01-01T00:00:00.000Z' // optional }); ``` #### `users.get(userId, options?)` Gets a specific user. #### `users.update(userId, data, options?)` Updates a user. #### `users.delete(userId, options?)` Deletes a user. #### `users.getUsage(userId, options?)` Gets user usage. #### `users.getKeys(userId, options?)` Gets all API Keys for a user. #### `users.createKey(userId, data, options?)` Creates a new API Key for a user. ```typescript const key = await limitly.users.createKey(123, { name: 'API Key for John' }); ``` ## šŸ› ļø Error Handling The SDK throws specific errors that you can catch: ```typescript try { const result = await limitly.validation.validate( 'invalid_api_key', '/api/users', 'GET' ); } catch (error) { if (error instanceof LimitlyError) { console.log('Limitly error:', error.message); console.log('Status code:', error.statusCode); console.log('Full response:', error.response); } else { console.log('Unexpected error:', error); } } ``` ## šŸ” Advanced Examples ### Next.js App Router with Rate Limiting ```typescript // app/api/users/route.ts import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); export async function GET(request: Request) { const apiKey = request.headers.get('authorization')?.replace('Bearer ', ''); if (!apiKey) { return Response.json({ error: 'API Key required' }, { status: 401 }); } try { const result = await limitly.validation.validate( apiKey, '/api/users', 'GET' ); if (!result.success) { return Response.json({ error: 'Rate limit exceeded', details: result.details }, { status: 429 }); } // Your API logic here const users = await fetchUsers(); return Response.json({ users }); } catch (error) { console.error('API error:', error); return Response.json({ error: 'Internal error' }, { status: 500 }); } } ``` ### Server-Side Rendering with Caching ```typescript // app/dashboard/page.tsx import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); export default async function DashboardPage() { // Get data with caching for SSR const [plans, keysWithUsage] = await Promise.all([ limitly.plans.list({ cache: true, revalidate: 3600 }), limitly.apiKeys.listWithUsage({ cache: true, revalidate: 300 }) ]); return ( <div> <h1>Dashboard</h1> <div> <h2>Plans ({plans.data?.length || 0})</h2> {/* Render plans */} </div> <div> <h2>API Keys ({keysWithUsage.data?.length || 0})</h2> {/* Render API keys with usage */} </div> </div> ); } ``` ### Custom Middleware ```typescript // middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); export async function middleware(request: NextRequest) { // Only apply to API routes if (!request.nextUrl.pathname.startsWith('/api/')) { return NextResponse.next(); } const apiKey = request.headers.get('authorization')?.replace('Bearer ', ''); if (!apiKey) { return NextResponse.json( { error: 'API Key required' }, { status: 401 } ); } try { const result = await limitly.validation.validate( apiKey, request.nextUrl.pathname, request.method ); if (!result.success) { return NextResponse.json( { error: 'Rate limit exceeded' }, { status: 429 } ); } return NextResponse.next(); } catch (error) { console.error('Middleware error:', error); return NextResponse.json( { error: 'Validation error' }, { status: 500 } ); } } export const config = { matcher: '/api/:path*', }; ``` ### Usage Monitoring ```typescript // app/api/monitor/route.ts import { Limitly } from '@limitly/limitly-nextjs'; const limitly = new Limitly({ apiKey: process.env.LIMITLY_API_KEY! }); export async function GET() { try { const keys = await limitly.apiKeys.list(); const alerts = []; for (const key of keys.data || []) { const usage = await limitly.apiKeys.getUsage(key.id); if (usage.data && usage.data.percentageUsed > 80) { alerts.push({ keyId: key.id, keyName: key.name, usage: usage.data.percentageUsed }); } } return Response.json({ alerts }); } catch (error) { console.error('Monitoring error:', error); return Response.json({ error: 'Monitoring failed' }, { status: 500 }); } } ``` ## šŸ“¦ Project Structure ``` src/ ā”œā”€ā”€ index.ts # Main SDK class with Next.js optimizations ā”œā”€ā”€ client.ts # HTTP client with caching support ā”œā”€ā”€ types/ # TypeScript type definitions │ └── index.ts └── modules/ # Specific modules ā”œā”€ā”€ api-keys.ts ā”œā”€ā”€ plans.ts ā”œā”€ā”€ users.ts └── validation.ts ``` ## šŸ”„ Differences from @limitly/limitly-js This Next.js SDK includes several optimizations for server-side rendering: 1. **Caching Support**: Built-in support for Next.js caching with `cache` and `revalidate` options 2. **Middleware Integration**: `createMiddleware()` and `withRateLimit()` for easy integration 3. **SSR Optimized Methods**: `listWithUsage()` methods for efficient data fetching 4. **Next.js Types**: Additional TypeScript types for Next.js specific features 5. **App Router Support**: Optimized for Next.js 13+ App Router 6. **Server-Side Focus**: Designed for server-side usage in Next.js applications ## šŸ¤ Contributing 1. Fork the project 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 4. Push to the branch (`git push origin feature/AmazingFeature`) 5. Open a Pull Request ## šŸ“„ License This project is licensed under the MIT License. See the `LICENSE` file for details. ## šŸ†˜ Support - šŸ“§ Email: hi@limitly.dev - šŸ’» Limitly: https://www.limitly.dev - šŸ“– Documentation: https://docs.limitly.com - šŸ› Issues: https://github.com/limitlydev/limitly-nextjs/issues