Subscribed routes require both user authentication and an active subscription. These routes are perfect for premium features and subscription-gated content.
import { router, subscribedProcedure } from '../lib/trpc'; import { z } from 'zod'; export const yourRouter = router({ // Your subscribed route yourSubscribedRoute: subscribedProcedure .input(z.object({ yourInput: z.string(), yourInput2: z.string(), })) .mutation(async ({ ctx, input }) => { // Your logic here return { success: true }; }), });
export const subscribedProcedure = protectedProcedure.use(async ({ ctx, next }) => { const subscription = await prisma.subscription.findFirst({ where: { userId: ctx.session.userId, status: 'active', currentPeriodEnd: { gt: new Date(), }, }, }); if (!subscription) { throw new TRPCError({ code: 'FORBIDDEN', message: 'Active subscription required', }); } return next({ ctx: { ...ctx, subscription, }, }); });
// apps/server/src/routers/premium.ts import { router, subscribedProcedure } from '../lib/trpc'; import { z } from 'zod'; export const premiumRouter = router({ // Get premium content getContent: subscribedProcedure .input(z.object({ contentType: z.enum(['article', 'video', 'download']), })) .query(async ({ ctx, input }) => { const content = await prisma.premiumContent.findMany({ where: { type: input.contentType, subscriptionTier: { lte: ctx.subscription.tier, }, }, }); return content; }), // Access premium feature useFeature: subscribedProcedure .input(z.object({ featureId: z.string(), })) .mutation(async ({ ctx, input }) => { // Check if feature is available in subscription tier const feature = await prisma.premiumFeature.findFirst({ where: { id: input.featureId, requiredTier: { lte: ctx.subscription.tier, }, }, }); if (!feature) { throw new TRPCError({ code: 'FORBIDDEN', message: 'Feature not available in your subscription tier', }); } // Implement feature logic return { success: true }; }), });
// In your React component function PremiumContentComponent() { const { data: content, isLoading } = trpc.premiumRouter.getContent.useQuery({ contentType: 'article', }); const useFeature = trpc.premiumRouter.useFeature.useMutation(); const handleFeatureUse = async (featureId: string) => { try { await useFeature.mutateAsync({ featureId }); } catch (error) { if (error.code === 'FORBIDDEN') { // Handle subscription upgrade prompt } } }; return ( <div> {isLoading ? 'Loading...' : ( <div> {content.map(item => ( <div key={item.id}> {item.title} <button onClick={() => handleFeatureUse(item.featureId)}> Use Feature </button> </div> ))} </div> )} </div> ); }