Skip to content

BenAbdou1001/tRPC-Walkthrough

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

WriteSpace πŸ“

A modern, full-stack content creation and sharing platform built with Next.js 14, featuring a robust tRPC API, secure authentication, and real-time interactions.

Author: Abdallah Benassloun

WriteSpace Logo

🌟 Overview

WriteSpace is a comprehensive blogging and content sharing platform where writers can create, publish, and engage with stories. Built with modern web technologies, it showcases advanced patterns in full-stack TypeScript development, particularly focusing on type-safe APIs with tRPC.

Based on: This project was built using the Next.js Session Auth Template as a starting point, then extensively enhanced with additional features including social interactions, content management, and a complete UI redesign.

πŸš€ Key Features

πŸ“– Content Management

  • Rich Markdown Editor with real-time preview
  • Draft & Published States with seamless publishing workflow
  • Public Content Discovery page for published stories
  • Personal Dashboard for managing your content

πŸ‘₯ Social Features

  • Nested Comments System with infinite pagination
  • Like/Unlike functionality for posts and comments
  • Real-time Interaction Counts with optimistic updates
  • Community Engagement tools

πŸ” Authentication & Security

  • Lucia Auth integration with credential-based authentication
  • Email Verification with secure token system
  • Password Reset functionality
  • Protected Routes and middleware
  • Discord OAuth integration (configurable)

πŸ’³ Subscription System

  • Stripe Integration for premium features
  • Webhook Handling for payment events
  • Subscription Management dashboard

οΏ½ Tech Stack

Frontend

Backend & API

  • tRPC - End-to-end type-safe APIs
  • Drizzle ORM - Type-safe database toolkit
  • PostgreSQL - Robust relational database
  • Lucia - Authentication library
  • Zod - Schema validation

Infrastructure & Tools

πŸ”§ tRPC Implementation Deep Dive

This project showcases advanced tRPC patterns and best practices:

πŸ— tRPC Architecture & Folder Structure

The tRPC implementation follows a modular, organized structure that separates concerns and maintains type safety throughout:

src/
β”œβ”€β”€ server/api/
β”‚   β”œβ”€β”€ root.ts                    # Main app router - combines all feature routers
β”‚   β”œβ”€β”€ trpc.ts                   # tRPC configuration, context, and middleware
β”‚   └── routers/
β”‚       β”œβ”€β”€ post/
β”‚       β”‚   β”œβ”€β”€ post.procedure.ts  # tRPC procedures (queries & mutations)
β”‚       β”‚   β”œβ”€β”€ post.input.ts     # Zod validation schemas
β”‚       β”‚   └── post.service.ts   # Business logic and database operations
β”‚       β”œβ”€β”€ comment/
β”‚       β”‚   β”œβ”€β”€ comment.procedure.ts
β”‚       β”‚   β”œβ”€β”€ comment.input.ts
β”‚       β”‚   └── comment.service.ts
β”‚       β”œβ”€β”€ like/
β”‚       β”‚   β”œβ”€β”€ like.procedure.ts
β”‚       β”‚   β”œβ”€β”€ like.input.ts
β”‚       β”‚   └── like.service.ts
β”‚       β”œβ”€β”€ user/
β”‚       β”‚   β”œβ”€β”€ user.procedure.ts
β”‚       β”‚   β”œβ”€β”€ user.input.ts
β”‚       β”‚   └── user.service.ts
β”‚       └── stripe/
β”‚           β”œβ”€β”€ stripe.procedure.ts
β”‚           β”œβ”€β”€ stripe.input.ts
β”‚           └── stripe.service.ts
β”œβ”€β”€ trpc/
β”‚   β”œβ”€β”€ react.tsx                 # tRPC React Query client setup
β”‚   β”œβ”€β”€ server.ts                # Server-side tRPC caller
β”‚   └── shared.ts                # Shared utilities and transformers
└── app/api/trpc/[trpc]/
    └── route.ts                 # Next.js API route handler

πŸ“‚ Folder Structure Explanation

/server/api/ - Core tRPC server implementation

  • root.ts: Combines all feature routers into the main appRouter
  • trpc.ts: Contains context creation, middleware, and procedure definitions

/server/api/routers/{feature}/ - Feature-based organization

  • {feature}.procedure.ts: Defines tRPC procedures (queries/mutations)
  • {feature}.input.ts: Zod schemas for input validation
  • {feature}.service.ts: Business logic separated from API layer

/trpc/ - Client-side tRPC configuration

  • react.tsx: React Query integration with tRPC
  • server.ts: Server-side tRPC calls for SSR
  • shared.ts: Common utilities and type transformers

πŸ”„ Data Flow Pattern

Client Request β†’ tRPC Procedure β†’ Input Validation β†’ Service Layer β†’ Database β†’ Response
     ↑                ↓              (Zod)           (Business Logic)    (Drizzle)     ↓
React Component ← Type-safe Response ← Transformed Data ← Database Result β†β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”‘ Key tRPC Features Implemented

1. Type-Safe Context with Authentication

// src/server/api/trpc.ts
export const createTRPCContext = async (opts: { headers: Headers }) => {
  const { session, user } = await uncachedValidateRequest();
  return {
    session,
    user,
    db,
    headers: opts.headers,
    stripe: stripe,
  };
};

// Protected procedure with guaranteed user context
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
  if (!ctx.session || !ctx.user) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({
    ctx: {
      session: { ...ctx.session },
      user: { ...ctx.user },
    },
  });
});

2. Modular Router Organization

Each feature has its own router with input validation and service layer:

// src/server/api/routers/post/post.procedure.ts
export const postRouter = createTRPCRouter({
  // Public endpoints
  publicPosts: publicProcedure
    .input(inputs.publicPostsSchema)
    .query(({ ctx, input }) => services.getPublicPosts(ctx, input)),

  // Protected endpoints
  create: protectedProcedure
    .input(inputs.createPostSchema)
    .mutation(({ ctx, input }) => services.createPost(ctx, input)),

  update: protectedProcedure
    .input(inputs.updatePostSchema)
    .mutation(({ ctx, input }) => services.updatePost(ctx, input)),
});

3. Input Validation with Zod

// src/server/api/routers/post/post.input.ts
export const createPostSchema = z.object({
  title: z.string().min(1, "Title is required").max(255),
  excerpt: z.string().min(1, "Excerpt is required").max(255),
  content: z.string().min(1, "Content is required"),
});

export const updatePostSchema = createPostSchema.extend({
  id: z.string(),
  status: z.enum(["draft", "published"]).default("draft"),
});

4. Service Layer Pattern

// src/server/api/routers/post/post.service.ts
export const createPost = async (ctx: ProtectedTRPCContext, input: CreatePostInput) => {
  const { id } = await ctx.db.insert(posts).values({
    id: generateId(15),
    userId: ctx.user.id,
    title: input.title,
    excerpt: input.excerpt,
    content: input.content,
  }).returning({ id: posts.id });

  return { id };
};

5. Client-Side Integration with React Query

// Client-side usage with full type safety
const { data: posts, isLoading } = api.post.publicPosts.useQuery({
  page: 1,
  perPage: 12,
});

const createPost = api.post.create.useMutation({
  onSuccess: () => {
    // Optimistic updates and cache invalidation
    utils.post.myPosts.invalidate();
  },
});

6. Advanced Features

  • Infinite Queries for pagination:
const { data, fetchNextPage, hasNextPage } = api.comment.getByPost.useInfiniteQuery(
  { postId: "123", limit: 10 },
  { getNextPageParam: (lastPage) => lastPage.nextCursor }
);
  • Optimistic Updates for real-time UX:
const toggleLike = api.like.togglePostLike.useMutation({
  onMutate: async ({ postId }) => {
    // Cancel outgoing refetches
    await utils.like.getPostLikes.cancel({ postId });
    
    // Snapshot previous value
    const previousLikes = utils.like.getPostLikes.getData({ postId });
    
    // Optimistically update
    utils.like.getPostLikes.setData({ postId }, (old) => ({
      ...old!,
      isLiked: !old?.isLiked,
      count: old!.count + (old?.isLiked ? -1 : 1),
    }));
    
    return { previousLikes };
  },
});
  • Error Handling with custom error types:
if (!post) {
  throw new TRPCError({
    code: "NOT_FOUND",
    message: "Post not found",
  });
}

πŸ”„ Real-time Features

The project implements real-time-like experiences through:

  • Optimistic Updates for immediate UI feedback
  • Cache Invalidation strategies for data consistency
  • Infinite Pagination for smooth content loading
  • Background Refetching for fresh data

πŸ“ Project Structure

src/
β”œβ”€β”€ app/                 # Next.js App Router pages
β”‚   β”œβ”€β”€ (auth)/         # Authentication routes
β”‚   β”œβ”€β”€ (landing)/      # Public landing page
β”‚   └── (main)/         # Protected application routes
β”œβ”€β”€ components/         # Reusable UI components
β”œβ”€β”€ server/
β”‚   β”œβ”€β”€ api/            # tRPC routers and procedures
β”‚   └── db/             # Database schema and connection
β”œβ”€β”€ lib/                # Utility functions and configurations
β”œβ”€β”€ trpc/               # tRPC client setup
└── styles/             # Global styles and Tailwind config

πŸš€ Getting Started

Prerequisites

  • Node.js 18+
  • PostgreSQL database
  • npm/yarn/pnpm

Installation

  1. Clone the repository
git clone https://github.com/yourusername/writespace.git
cd writespace
  1. Install dependencies
npm install
  1. Set up environment variables
cp .env.example .env

Fill in your environment variables:

# Database
DATABASE_URL='postgresql://user:password@localhost:5432/writespace'

# App URL
NEXT_PUBLIC_APP_URL='http://localhost:3000'

# Email (for development, use MOCK_SEND_EMAIL=true)
MOCK_SEND_EMAIL=true
SMTP_HOST='smtp.gmail.com'
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER='[email protected]'
SMTP_PASSWORD='your-app-password'

# OAuth (optional)
DISCORD_CLIENT_ID='your-discord-client-id'
DISCORD_CLIENT_SECRET='your-discord-client-secret'

# Stripe (optional)
STRIPE_API_KEY='sk_test_...'
STRIPE_WEBHOOK_SECRET='whsec_...'
STRIPE_PRO_MONTHLY_PLAN_ID='price_...'
  1. Set up the database
npm run db:push
  1. Start the development server
npm run dev

Visit http://localhost:3000 to see the application!

πŸ“– Usage Examples

Creating a Post

// In a React component
const createPost = api.post.create.useMutation({
  onSuccess: () => {
    router.push('/dashboard');
  },
});

const handleSubmit = (data: CreatePostInput) => {
  createPost.mutate(data);
};

Liking a Post

const { data: likes } = api.like.getPostLikes.useQuery({ postId });
const toggleLike = api.like.togglePostLike.useMutation({
  // Optimistic updates for instant feedback
});

Real-time Comments

const {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
} = api.comment.getByPost.useInfiniteQuery(
  { postId, limit: 10 },
  {
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  }
);

πŸ§ͺ Testing

Run the test suite:

npm run test:e2e

πŸš€ Deployment

The application is ready for deployment on platforms like Vercel, Netlify, or any Node.js hosting service.

Build for production:

npm run build
npm start

Environment Setup:

  • Set up a PostgreSQL database (Supabase, PlanetScale, etc.)
  • Configure email service (SendGrid, AWS SES, etc.)
  • Set up Stripe webhooks for production
  • Update environment variables

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • SaasyKits for providing the excellent starter template that served as the foundation for this project
  • T3 Stack for the excellent development experience and architectural patterns
  • tRPC Team for the amazing type-safe API solution that makes full-stack TypeScript development seamless
  • Next.js Team for the powerful React framework with App Router
  • Tailwind CSS for the utility-first styling approach
  • Lucia Auth team for the flexible authentication library

πŸ”— Links

  • Database Studio: Run npm run db:studio

πŸ‘¨β€πŸ’» About the Author

Abdallah Benassloun - Full-stack developer passionate about modern web technologies and type-safe development.

  • πŸš€ Specialized in Next.js, TypeScript, and tRPC
  • πŸ“ Building WriteSpace to showcase advanced full-stack patterns
  • 🌟 Open to collaboration and learning opportunities

Built with ❀️ and TypeScript by Abdallah Benassloun

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published