Documentation Index
Fetch the complete documentation index at: https://docs.builderbox.ai/llms.txt
Use this file to discover all available pages before exploring further.
When to Use Subscribed Routes
Use subscribed routes for:
- Premium features
- Subscription-gated content
- Advanced functionality
- Paid API endpoints
- Premium data access
- Subscription-specific features
Creating Subscribed Routes
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 };
}),
});
Example Subscribed Routes
Subscription Check Flow
Subscribed routes automatically check for both authentication and an active subscription:
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,
},
});
});
Example: Premium Content Management
// 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 };
}),
});
Client Usage
// 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>
);
}
Best Practices for Subscribed Routes
-
Subscription Management
- Check subscription status
- Handle subscription expiration
- Implement tier-based access
-
Feature Access
- Implement feature flags
- Handle tier limitations
- Provide upgrade paths
-
Error Handling
- Handle subscription errors
- Provide clear upgrade messages
- Implement proper error logging
-
Security
- Validate subscription status
- Implement proper access controls
- Use database-level security
Security Considerations
-
Subscription Security
- Validate subscription status
- Check subscription expiration
- Handle subscription changes
-
Feature Access
- Implement proper access controls
- Check feature availability
- Handle tier limitations
-
Data Protection
- Protect premium content
- Implement proper access controls
- Use database-level security
Next Steps