Usage Metering और Subscription Enforcement
प्रत्येक tenant का usage track करें और plan limits तथा billing status के आधार पर access नियंत्रित करें।
Usage Metering और Subscription Enforcement, CoddyKit पर Next.js 15 फुलस्टैक (App Router + Server Actions) का एक निःशुल्क पाठ है। यह 4 में से 4वाँ पाठ है। इस अध्ययन पथ के 3 तक कोई भी पाठ पूरा पढ़ना निःशुल्क है — इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ व्यावहारिक अभ्यास भी उपलब्ध कराता है। यह Next.js 15 फुलस्टैक (App Router + Server Actions) सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Next.js 15 फुलस्टैक (App Router + Server Actions) पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
SaaS में उपयोग मापन क्यों महत्वपूर्ण है
बहु-Tenant SaaS अनुप्रयोग में अलग-अलग ग्राहक अलग-अलग स्तरों के लिए भुगतान करते हैं। Starter योजना प्रति माह 1,000 API कॉल की अनुमति दे सकती है, जबकि Enterprise योजना असीमित पहुँच दे सकती है। उपयोग मापन के बिना, ग्राहक चाहे जितना भुगतान करें, हर Tenant को एक जैसा अनुभव मिलेगा।
उपयोग मापन के दो उद्देश्य हैं:
- प्रवर्तन: सीमा पूरी होने पर सेवा रोकना या सीमित करना
- बिलिंग संकेत: सटीक उपयोग डेटा आपके बिलिंग प्रदाता (जैसे Stripe) को देना
App Router वाले Next.js 15 में मापन स्वाभाविक रूप से Server Actions और Route Handlers में फिट बैठता है—यही वे दो स्थान हैं जहाँ सर्वर पर वास्तविक काम होता है।
डेटा मॉडल: Tenant, योजनाएँ और उपयोग
ऐसे Schema से शुरुआत करें जो Tenant, उसकी सक्रिय सदस्यता योजना और उसके वर्तमान उपयोग काउंटरों के बीच संबंध दर्ज करे। न्यूनतम Postgres Schema इस प्रकार दिखता है:
tenants— प्रत्येक संगठन के लिए एक पंक्तिsubscription_plans— प्रत्येक स्तर की सुविधा के लिए सीमाएँtenant_usage— बिलिंग चक्र पर रीसेट होने वाले चलते काउंटर
प्रत्येक अनुरोध पर इवेंट लॉग का योग निकालने के बजाय उपयोग को समर्पित तालिका में रखने से सीमा की जाँच एकल इंडेक्स की गई रीड बन जाती है, जो कम-विलंबता वाले Server Actions के लिए अत्यंत महत्वपूर्ण है।
// types/billing.ts
export type PlanTier = 'starter' | 'pro' | 'enterprise';
export interface SubscriptionPlan {
id: string;
tier: PlanTier;
limits: {
apiCallsPerMonth: number; // -1 = unlimited
teamMembers: number;
storageMb: number;
};
}
export interface TenantUsage {
tenantId: string;
billingPeriodStart: Date;
apiCallsUsed: number;
teamMembersActive: number;
storageMbUsed: number;
}
export interface Tenant {
id: string;
name: string;
planId: string;
subscriptionStatus: 'active' | 'past_due' | 'canceled' | 'trialing';
}App Router में वर्तमान Tenant निर्धारित करना
प्रत्येक प्रवर्तन जाँच को यह जानना आवश्यक है कि कार्रवाई कौन-सा Tenant कर रहा है। App Router में Tenant की पहचान आम तौर पर सत्र JWT में एन्कोड की जाती है या सबडोमेन से प्राप्त होती है। एक साझा उपयोगिता फ़ंक्शन इसे एक बार निर्धारित करता है और अप्रमाणित होने पर त्रुटि उत्पन्न करता है।
इसे lib/tenant.ts मॉड्यूल में रखने से Server Actions और Route Handlers में दोहराव नहीं होता—दोनों सत्र को अलग-अलग पार्स करने के बजाय उसी निर्धारक को बुलाते हैं।
// lib/tenant.ts
import { cookies } from 'next/headers';
import { createServerClient } from '@supabase/ssr';
export interface TenantContext {
tenantId: string;
userId: string;
planId: string;
subscriptionStatus: string;
}
export async function requireTenantContext(): Promise<TenantContext> {
const cookieStore = await cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{ cookies: { getAll: () => cookieStore.getAll() } }
);
const { data: { user }, error } = await supabase.auth.getUser();
if (error || !user) throw new Error('Unauthenticated');
const { data: membership } = await supabase
.from('tenant_memberships')
.select('tenant_id, tenants(plan_id, subscription_status)')
.eq('user_id', user.id)
.single();
if (!membership) throw new Error('No tenant found for user');
return {
tenantId: membership.tenant_id,
userId: user.id,
planId: (membership.tenants as any).plan_id,
subscriptionStatus: (membership.tenants as any).subscription_status,
};
}किसी भी कार्रवाई से पहले सदस्यता स्थिति जाँचना
उपयोग सीमाओं की जाँच से पहले हमेशा सत्यापित करें कि Tenant की सदस्यता अच्छी स्थिति में है। past_due या canceled खाते को तब भी रोक देना चाहिए, जब उन्होंने अभी तक अपनी उपयोग सीमा पूरी न की हो।
इस तर्क को एक गार्ड फ़ंक्शन में केंद्रीकृत करें, जिसे Server Actions और Route Handlers दोनों बुला सकें। टाइप की गई त्रुटि उत्पन्न करें, ताकि कॉल करने वाले सही UI रेंडर कर सकें (जैसे, “सदस्यता पुनः सक्रिय करें” बैनर)।
// lib/billing-guard.ts
import { requireTenantContext, TenantContext } from './tenant';
export class SubscriptionError extends Error {
constructor(
message: string,
public readonly code: 'SUBSCRIPTION_INACTIVE' | 'LIMIT_EXCEEDED' | 'FEATURE_NOT_AVAILABLE'
) {
super(message);
this.name = 'SubscriptionError';
}
}
const ACTIVE_STATUSES = new Set(['active', 'trialing']);
export async function assertActiveSubscription(ctx?: TenantContext): Promise<TenantContext> {
const tenantCtx = ctx ?? await requireTenantContext();
if (!ACTIVE_STATUSES.has(tenantCtx.subscriptionStatus)) {
throw new SubscriptionError(
`Subscription is ${tenantCtx.subscriptionStatus}. Please update your billing details.`,
'SUBSCRIPTION_INACTIVE'
);
}
return tenantCtx;
}सीमा जाँच के साथ परमाणु उपयोग वृद्धि
मापन का सबसे महत्वपूर्ण भाग परमाणु जाँच और वृद्धि है। एक सरल तरीका—वर्तमान उपयोग पढ़ना, सीमा से तुलना करना, फिर लिखना—दौड़ की स्थिति पैदा करता है: दो समवर्ती अनुरोध काउंटर बढ़ने से पहले ही जाँच पार कर सकते हैं।
सही तरीका एकल SQL कथन का उपयोग करता है, जो परमाणु रूप से जाँच और वृद्धि करता है तथा बताता है कि कार्रवाई सफल हुई या नहीं। Postgres में यह सशर्त UPDATE ... RETURNING या संग्रहीत प्रक्रिया होती है।
// lib/usage.ts
import { createClient } from '@/lib/supabase-admin';
export async function incrementApiCallUsage(
tenantId: string,
limit: number
): Promise<{ allowed: boolean; used: number }> {
const supabase = createClient();
// Atomic: only increment if under the limit (-1 means unlimited)
const { data, error } = await supabase.rpc('increment_api_usage', {
p_tenant_id: tenantId,
p_limit: limit,
});
if (error) throw new Error(`Usage increment failed: ${error.message}`);
return {
allowed: data.allowed as boolean,
used: data.new_count as number,
};
}
/*
Corresponding Postgres function (run once as a migration):
CREATE OR REPLACE FUNCTION increment_api_usage(
p_tenant_id UUID,
p_limit INT
) RETURNS JSON AS $$
DECLARE
v_current INT;
v_allowed BOOLEAN;
BEGIN
SELECT api_calls_used INTO v_current FROM tenant_usage
WHERE tenant_id = p_tenant_id
AND billing_period_start = date_trunc('month', now())
FOR UPDATE;
v_allowed := (p_limit = -1) OR (v_current < p_limit);
IF v_allowed THEN
UPDATE tenant_usage
SET api_calls_used = api_calls_used + 1
WHERE tenant_id = p_tenant_id
AND billing_period_start = date_trunc('month', now());
END IF;
RETURN json_build_object('allowed', v_allowed, 'new_count', v_current + 1);
END;
$$ LANGUAGE plpgsql;
*/रनटाइम पर योजना सीमाएँ प्राप्त करना
योजना सीमाएँ रनटाइम पर डेटाबेस (या तेज़ कैश) से प्राप्त की जानी चाहिए—उन्हें अनुप्रयोग तर्क में कभी हार्डकोड न करें। इससे आप बिना परिनियोजन के योजना सीमाएँ बदल सकते हैं।
योजना डेटा को आक्रामक रूप से कैश करें: सीमाएँ बहुत कम बदलती हैं, इसलिए TTL वाला इन-मेमोरी कैश (या Next.js का अंतर्निहित unstable_cache) हर अनुरोध पर DB तक जाने से बचाता है।
// lib/plans.ts
import { unstable_cache } from 'next/cache';
import { createClient } from '@/lib/supabase-admin';
import type { SubscriptionPlan } from '@/types/billing';
export const getPlanLimits = unstable_cache(
async (planId: string): Promise<SubscriptionPlan> => {
const supabase = createClient();
const { data, error } = await supabase
.from('subscription_plans')
.select('id, tier, limit_api_calls, limit_team_members, limit_storage_mb')
.eq('id', planId)
.single();
if (error || !data) throw new Error(`Plan ${planId} not found`);
return {
id: data.id,
tier: data.tier,
limits: {
apiCallsPerMonth: data.limit_api_calls,
teamMembers: data.limit_team_members,
storageMb: data.limit_storage_mb,
},
};
},
['plan-limits'],
{ revalidate: 300 } // 5-minute TTL
);Server Action में सब कुछ जोड़ना
अब guard, plan lookup और usage increment को एक ही Server Action में संयोजित कीजिए। यह action पूरी तरह server पर चलता है; client billing logic को कभी नहीं छूता।
पैटर्न में हमेशा यही तीन चरण होते हैं:
- 1. प्रमाणीकरण — tenant context निर्धारित कीजिए
- 2. Guard — active subscription की पुष्टि कीजिए, limits प्राप्त कीजिए और usage जाँचिए
- 3. निष्पादन — वास्तविक business logic चलाइए
// app/actions/generate-report.ts
'use server';
import { requireTenantContext } from '@/lib/tenant';
import { assertActiveSubscription, SubscriptionError } from '@/lib/billing-guard';
import { getPlanLimits } from '@/lib/plans';
import { incrementApiCallUsage } from '@/lib/usage';
export async function generateReportAction(
reportType: string
): Promise<{ success: boolean; error?: string; reportId?: string }> {
try {
// Step 1: Resolve tenant
const ctx = await requireTenantContext();
// Step 2: Guard — subscription status + usage
await assertActiveSubscription(ctx);
const plan = await getPlanLimits(ctx.planId);
const { allowed, used } = await incrementApiCallUsage(
ctx.tenantId,
plan.limits.apiCallsPerMonth
);
if (!allowed) {
return {
success: false,
error: `Monthly API limit of ${plan.limits.apiCallsPerMonth} reached (used: ${used}). Upgrade your plan to continue.`,
};
}
// Step 3: Real work
const reportId = await createReport(ctx.tenantId, reportType);
return { success: true, reportId };
} catch (err) {
if (err instanceof SubscriptionError) {
return { success: false, error: err.message };
}
throw err; // unexpected errors bubble up
}
}
async function createReport(tenantId: string, type: string): Promise<string> {
// ... actual report generation logic
return crypto.randomUUID();
}API Routes के लिए Middleware-Level Enforcement
Server Actions form-driven flows के लिए बहुत उपयोगी हैं, लेकिन tenants public API routes (जैसे /api/v1/data) के माध्यम से भी usage करते हैं। हर Route Handler में guard code को बार-बार लिखने के बजाय, यहाँ reusable middleware wrapper के माध्यम से metering लागू कीजिए।
इस wrapper pattern को कभी-कभी API middleware chain या handler factory कहा जाता है। इससे प्रत्येक Route Handler business logic पर केंद्रित रहता है।
// lib/with-metering.ts
import { NextRequest, NextResponse } from 'next/server';
import { requireTenantContext } from './tenant';
import { assertActiveSubscription, SubscriptionError } from './billing-guard';
import { getPlanLimits } from './plans';
import { incrementApiCallUsage } from './usage';
type Handler = (req: NextRequest, ctx: { tenantId: string }) => Promise<NextResponse>;
export function withMetering(handler: Handler) {
return async (req: NextRequest): Promise<NextResponse> => {
try {
const tenant = await requireTenantContext();
await assertActiveSubscription(tenant);
const plan = await getPlanLimits(tenant.planId);
const { allowed } = await incrementApiCallUsage(
tenant.tenantId,
plan.limits.apiCallsPerMonth
);
if (!allowed) {
return NextResponse.json(
{ error: 'rate_limit_exceeded', message: 'Monthly API limit reached.' },
{ status: 429 }
);
}
return handler(req, { tenantId: tenant.tenantId });
} catch (err) {
if (err instanceof SubscriptionError) {
return NextResponse.json(
{ error: 'subscription_inactive', message: err.message },
{ status: 402 }
);
}
return NextResponse.json({ error: 'internal_error' }, { status: 500 });
}
};
}
// Usage in app/api/v1/data/route.ts:
// export const GET = withMetering(async (req, { tenantId }) => {
// const data = await fetchData(tenantId);
// return NextResponse.json(data);
// });Tenant को Usage दिखाना: Usage Dashboard
Tenants को अपने usage की जानकारी चाहिए, ताकि वे upgrade करने के बारे में सोच-समझकर निर्णय ले सकें। एक usage API endpoint वर्तमान period की खपत के साथ plan limits भी लौटाता है।
used और limit दोनों लौटाने से frontend अलग से plan details जाने बिना progress bar दिखा सकता है। Client को सरल रखने के लिए server पर पहले से गणना किया हुआ percentage field लौटाइए।
// app/api/billing/usage/route.ts
import { NextResponse } from 'next/server';
import { requireTenantContext } from '@/lib/tenant';
import { getPlanLimits } from '@/lib/plans';
import { createClient } from '@/lib/supabase-admin';
export async function GET() {
const ctx = await requireTenantContext();
const plan = await getPlanLimits(ctx.planId);
const supabase = createClient();
const { data: usage } = await supabase
.from('tenant_usage')
.select('api_calls_used, storage_mb_used, team_members_active')
.eq('tenant_id', ctx.tenantId)
.gte('billing_period_start', new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString())
.single();
const apiCallsUsed = usage?.api_calls_used ?? 0;
const limit = plan.limits.apiCallsPerMonth;
return NextResponse.json({
planTier: plan.tier,
billingPeriod: new Date().toISOString().slice(0, 7),
apiCalls: {
used: apiCallsUsed,
limit,
percentage: limit === -1 ? 0 : Math.round((apiCallsUsed / limit) * 100),
unlimited: limit === -1,
},
storage: {
usedMb: usage?.storage_mb_used ?? 0,
limitMb: plan.limits.storageMb,
},
teamMembers: {
active: usage?.team_members_active ?? 0,
limit: plan.limits.teamMembers,
},
});
}Billing Cycle के नवीनीकरण पर Usage रीसेट करना
Billing period के नवीनीकृत होने पर usage counters को रीसेट करना आवश्यक है। इसका सबसे साफ़ तरीका एक Stripe webhook handler है, जो invoice.paid events को सुनता है और उस tenant के counters को रीसेट करता है।
ऐसे cron job पर कभी निर्भर न रहें जो dates जाँचता हो — उसमें समय का अंतर आ सकता है, वह दो बार चल सकता है या renewal छूट सकता है। Stripe events ही इस बात का प्रामाणिक संकेत हैं कि नया billing period शुरू हो गया है।
stripe.webhooks.constructEventसे webhook signature सत्यापित कीजिए- नए period के लिए
tenant_usagerow बनाने या रीसेट करने हेतुupsertका उपयोग कीजिए - Tenant पर
stripe_subscription_idसंग्रहीत कीजिए, ताकि event से उसे खोज सकें
// app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from 'next/server';
import Stripe from 'stripe';
import { createClient } from '@/lib/supabase-admin';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: NextRequest) {
const body = await req.text();
const sig = req.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
if (event.type === 'invoice.paid') {
const invoice = event.data.object as Stripe.Invoice;
const subscriptionId = invoice.subscription as string;
const periodStart = new Date((invoice.period_start) * 1000);
const supabase = createClient();
const { data: tenant } = await supabase
.from('tenants')
.select('id')
.eq('stripe_subscription_id', subscriptionId)
.single();
if (tenant) {
await supabase.from('tenant_usage').upsert({
tenant_id: tenant.id,
billing_period_start: periodStart.toISOString(),
api_calls_used: 0,
storage_mb_used: 0,
team_members_active: 0,
}, { onConflict: 'tenant_id,billing_period_start' });
}
}
return NextResponse.json({ received: true });
}Plan Tier के अनुसार Feature Gating
मात्रा की सीमाओं (कितने API calls) के अतिरिक्त, SaaS products plan tier के आधार पर features तक पहुँच भी नियंत्रित करते हैं। Starter tenant को usage count की परवाह किए बिना SSO सक्षम करने या audit log तक पहुँचने की अनुमति नहीं होनी चाहिए।
Feature flags को tier के आधार पर keyed static map के रूप में model कीजिए। आगे बढ़ने से पहले Server Action या Route Handler में feature जाँचिए। इससे feature definitions एक ही स्थान पर रहती हैं और tier में बदलाव केवल code update से किया जा सकता है।
// lib/features.ts
import type { PlanTier } from '@/types/billing';
const FEATURE_MAP: Record<PlanTier, Set<string>> = {
starter: new Set(['basic_reports', 'api_access']),
pro: new Set(['basic_reports', 'api_access', 'advanced_reports', 'webhooks', 'audit_log']),
enterprise: new Set([
'basic_reports', 'api_access', 'advanced_reports',
'webhooks', 'audit_log', 'sso', 'custom_roles', 'sla_support'
]),
};
export function hasFeature(tier: PlanTier, feature: string): boolean {
return FEATURE_MAP[tier]?.has(feature) ?? false;
}
// Usage in a Server Action:
// import { hasFeature } from '@/lib/features';
// import { SubscriptionError } from '@/lib/billing-guard';
//
// const plan = await getPlanLimits(ctx.planId);
// if (!hasFeature(plan.tier, 'sso')) {
// throw new SubscriptionError(
// 'SSO is only available on the Enterprise plan.',
// 'FEATURE_NOT_AVAILABLE'
// );
// }ज्ञान जाँच: Atomic Usage Enforcement
एक team multi-tenant SaaS app बना रही है। वे API calls के लिए usage metering इस प्रकार लागू करते हैं:
- Database से
api_calls_usedपढ़ना - यदि
used < limitहो, तो आगे बढ़ना - Operation पूरी होने के बाद
api_calls_usedमें 1 बढ़ाना
इस approach की मुख्य समस्या क्या है?
पुनरावलोकन: Usage Metering और Subscription Enforcement
अब आपके पास multi-tenant Next.js 15 SaaS के लिए production-grade metering system है। यहाँ सीखे गए मुख्य patterns का सारांश दिया गया है:
- Typed data model: तेज़ reads और deploy किए बिना configurable limits के लिए
subscription_plans(limits) कोtenant_usage(counters) से अलग रखिए। - Tenant context resolver: सभी Server Actions और Route Handlers में उपयोग किया जाने वाला एकमात्र
requireTenantContext()function। - Subscription status guard: limits जाँचने से पहले हमेशा
activeयाtrialingstatus जाँचिए — usage की परवाह किए बिनाpast_dueaccount को रोक दिया जाता है। - Atomic increment: एक ही statement में जाँच और increment करने के लिए
FOR UPDATEवाले Postgres function का उपयोग कीजिए, जिससे race conditions समाप्त हो जाती हैं। - Cached plan limits: हर request पर plan data प्राप्त न करना पड़े, इसके लिए कम TTL के साथ
unstable_cacheका उपयोग कीजिए। - Reusable wrapper:
withMetering()Route Handlers को साफ़ तरीके से wrap करता है और business logic को billing logic से अलग रखता है। - Stripe webhook resets: usage counters रीसेट करने के लिए
invoice.paidसुनिए — billing period की सीमाओं के लिए cron jobs पर कभी निर्भर न रहें। - Feature gating: प्रत्येक tier के लिए एक static
FEATURE_MAP, quantity limits से स्वतंत्र रूप से capability access नियंत्रित करता है।
इन patterns को मिलाने से प्रत्येक tenant को fair, enforceable और transparent service experience मिलता है और आपका infrastructure अत्यधिक consumption से सुरक्षित रहता है।
एआई शिक्षक के साथ TypeScript सीखें — निःशुल्क
अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।
- पाठ्यक्रम
- 22
- पाठ
- 88
अक्सर पूछे जाने वाले प्रश्न
क्या “Usage Metering और Subscription Enforcement” पाठ निःशुल्क है?
हाँ — Next.js 15 फुलस्टैक (App Router + Server Actions) अध्ययन पथ के 3 तक कोई भी पाठ, जिसमें “Usage Metering और Subscription Enforcement” भी शामिल है, यहाँ वेब पर पूरा पढ़ना निःशुल्क है। इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ इंटरैक्टिव अभ्यास भी उपलब्ध कराता है। Next.js 15 फुलस्टैक (App Router + Server Actions) पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
“Usage Metering और Subscription Enforcement” में मैं क्या सीखूँगा?
प्रत्येक tenant का usage track करें और plan limits तथा billing status के आधार पर access नियंत्रित करें। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Next.js 15 फुलस्टैक (App Router + Server Actions) का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।
क्या Next.js 15 फुलस्टैक (App Router + Server Actions) शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?
पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Next.js 15 फुलस्टैक (App Router + Server Actions) शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 4वाँ पाठ है।
“Usage Metering और Subscription Enforcement” पाठ पूरा करने में कितना समय लगता है?
CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।
क्या मैं इस Next.js 15 फुलस्टैक (App Router + Server Actions) पाठ में कोड लिख और चला सकता हूँ?
हाँ। हर Next.js 15 फुलस्टैक (App Router + Server Actions) पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।
इस पाठ्यक्रम के सभी पाठ
- Subdomain और Path-Based Tenant Resolution
- Row-Level Tenant Data Isolation Patterns
- Per-Tenant Theming और Feature Flags
- Usage Metering और Subscription Enforcement