Elysia Tools

Explore ferramentas, exemplos, explicações visuais e guias temáticos em um espaço claro.

Categorias populares

Temas

Idioma
ZHESPTRUFRDEEN

Elysia Tools

Navegação

Frameworks de API

Exemplos de tRPC

Exemplos de tRPC incluindo APIs seguras de ponta a ponta, procedimentos, middleware, autenticação e padrões de integração cliente-servidor

Exemplos

Entradas desta coleção

CódigotypescriptSimples

Configuração Básica tRPC

Configuração básica de servidor e cliente tRPC com procedimentos simples e segurança de tipo

Dificuldade

4/10

Tempo estimado

30 min

Etiquetas

trpc, typescript, api, type-safe

Pré-requisitos

TypeScript basics, Express.js, REST API concepts

typescript
// tRPC Basic Setup Example
// Server-side setup and client integration

import { initTRPC } from '@trpc/server';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import { observable } from '@trpc/server/observable';
import express from 'express';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';

// 1. Initialize tRPC
const t = initTRPC.create();

// 2. Define context type
export type Context = {
  userId?: string;
  user?: {
    id: string;
    name: string;
  };
};

// 3. Create router
export const appRouter = t.router({
  // Simple query procedure
  hello: t.procedure
    .query(() => {
      return { greeting: 'Hello tRPC!' };
    }),

  // Query with input validation
  getUser: t.procedure
    .input((val: unknown) => {
      if (typeof val === 'string' || typeof val === 'number') {
        return String(val);
      }
      throw new Error(`Invalid input: expected string or number`);
    })
    .query(({ input }) => {
      return {
        id: input,
        name: `User ${input}`,
        email: `user${input}@example.com`,
        createdAt: new Date().toISOString()
      };
    }),

  // Simple mutation
  createUser: t.procedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected object');
      }
      const { name, email } = val as { name?: unknown; email?: unknown };

      if (typeof name !== 'string' || typeof email !== 'string') {
        throw new Error('Name and email must be strings');
      }

      return { name, email };
    })
    .mutation(({ input }) => {
      // Simulate database save
      const user = {
        id: Math.random().toString(36).substr(2, 9),
        name: input.name,
        email: input.email,
        createdAt: new Date().toISOString()
      };

      return { success: true, user };
    }),

  // Query with context
  me: t.procedure
    .query(({ ctx }) => {
      return ctx.user || null;
    }),

  // Subscription example
  onHello: t.procedure
    .subscription(() => {
      return observable((emit) => {
        const timer = setInterval(() => {
          emit.next({ greeting: 'Hello from subscription!' });
        }, 1000);

        return () => {
          clearInterval(timer);
        };
      });
    }),
});

// Export router type
export type AppRouter = typeof appRouter;

// 4. Express server setup
const app = express();

app.use('/trpc', createExpressMiddleware({
  router: appRouter,
  createContext: ({ req }) => {
    // Simulate authentication
    const userId = req.headers['x-user-id'] as string;

    return {
      userId,
      user: userId ? {
        id: userId,
        name: `User ${userId}`
      } : undefined,
    };
  },
}));

const server = app.listen(3000, () => {
  console.log('tRPC server running on port 3000');
});

// 5. Client setup (can be in separate file)
const trpc = createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3000/trpc',
      headers: {
        'x-user-id': 'user123', // Example auth header
      },
    }),
  ],
});

// 6. Client usage examples
async function clientExamples() {
  // Query
  const hello = await trpc.hello.query();
  console.log(hello); // { greeting: 'Hello tRPC!' }

  // Query with input
  const user = await trpc.getUser.query('123');
  console.log(user); // { id: '123', name: 'User 123', email: '[email protected]', ... }

  // Mutation
  const newUser = await trpc.createUser.mutate({
    name: 'John Doe',
    email: '[email protected]'
  });
  console.log(newUser); // { success: true, user: { id: 'abc', name: 'John Doe', ... } }

  // Query with context
  const me = await trpc.me.query();
  console.log(me); // { id: 'user123', name: 'User user123' }

  // Subscription
  const subscription = trpc.onHello.subscribe(undefined, {
    onData(data) {
      console.log(data); // { greeting: 'Hello from subscription!' } every second
    },
    onError(err) {
      console.error('Subscription error:', err);
    },
  });

  // Clean up subscription after 5 seconds
  setTimeout(() => {
    subscription.unsubscribe();
  }, 5000);
}

// Execute client examples
clientExamples().catch(console.error);

// 7. Error handling example
const errorRouter = t.router({
  mightFail: t.procedure
    .query(() => {
      // Simulate potential error
      if (Math.random() > 0.5) {
        throw new Error('Random failure occurred!');
      }
      return { success: true };
    }),
});

// 8. Combined router with error handling
export const combinedRouter = t.router({
  app: appRouter,
  error: errorRouter,
});

export type CombinedRouter = typeof combinedRouter;

// 9. Async procedure example
const asyncRouter = t.router({
  fetchData: t.procedure
    .input((val: unknown) => {
      if (typeof val !== 'string') {
        throw new Error('Expected string URL');
      }
      return val;
    })
    .query(async ({ input }) => {
      // Simulate async operation (e.g., database query or API call)
      await new Promise(resolve => setTimeout(resolve, 1000));

      return {
        url: input,
        data: `Data fetched from ${input}`,
        timestamp: new Date().toISOString()
      };
    }),

  processItem: t.procedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected object');
      }
      return val as { id: string; data: any };
    })
    .mutation(async ({ input }) => {
      // Simulate async processing
      await new Promise(resolve => setTimeout(resolve, 500));

      return {
        processed: true,
        id: input.id,
        result: `Processed: ${JSON.stringify(input.data)}`,
        processedAt: new Date().toISOString()
      };
    }),
});

export type AsyncRouter = typeof asyncRouter;
CódigotypescriptIntermediário

Middleware e Autenticação tRPC

Padrões avançados de tRPC incluindo middleware, autenticação, autorização e tratamento de erros

Dificuldade

7/10

Tempo estimado

45 min

Etiquetas

trpc, middleware, auth, security, typescript

Pré-requisitos

tRPC basics, TypeScript, JWT, Express.js, Security concepts

typescript
// tRPC Middleware and Authentication Example

import { initTRPC, TRPCError } from '@trpc/server';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import express from 'express';
import jwt from 'jsonwebtoken';

// 1. Initialize tRPC
const t = initTRPC.create();

// 2. Define database/user types
interface User {
  id: string;
  email: string;
  name: string;
  role: 'admin' | 'user';
}

interface Database {
  users: User[];
  posts: Array<{
    id: string;
    title: string;
    content: string;
    authorId: string;
    published: boolean;
    createdAt: Date;
  }>;
}

// Mock database
const db: Database = {
  users: [
    { id: '1', email: '[email protected]', name: 'Admin User', role: 'admin' },
    { id: '2', email: '[email protected]', name: 'Regular User', role: 'user' },
  ],
  posts: [
    { id: '1', title: 'First Post', content: 'Hello World', authorId: '1', published: true, createdAt: new Date() },
    { id: '2', title: 'Draft Post', content: 'This is a draft', authorId: '2', published: false, createdAt: new Date() },
  ],
};

// 3. Define context type
export interface Context {
  user?: User;
  req: express.Request;
}

// 4. Authentication middleware
const isAuthed = t.middleware(async ({ next, ctx }) => {
  const authHeader = ctx.req.headers.authorization;

  if (!authHeader?.startsWith('Bearer ')) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret-key') as { userId: string };
    const user = db.users.find(u => u.id === decoded.userId);

    if (!user) {
      throw new TRPCError({ code: 'UNAUTHORIZED', message: 'User not found' });
    }

    return next({
      ctx: {
        ...ctx,
        user,
      },
    });
  } catch (error) {
    throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid token' });
  }
});

// 5. Role-based authorization middleware
const hasRole = (role: 'admin' | 'user') =>
  t.middleware(async ({ next, ctx }) => {
    if (!ctx.user) {
      throw new TRPCError({ code: 'UNAUTHORIZED' });
    }

    if (ctx.user.role !== role && ctx.user.role !== 'admin') {
      throw new TRPCError({
        code: 'FORBIDDEN',
        message: `Requires ${role} role or higher`
      });
    }

    return next({
      ctx: {
        ...ctx,
      },
    });
  });

// 6. Logging middleware
const logger = t.middleware(async ({ next, path, type, input }) => {
  const start = Date.now();
  const result = await next({ ctx: {} });
  const duration = Date.now() - start;

  console.log(`[${new Date().toISOString()}] ${type.toUpperCase()} ${path}: ${duration}ms`);

  return result;
});

// 7. Error handling middleware
const errorHandler = t.middleware(async ({ next }) => {
  try {
    return await next();
  } catch (error) {
    console.error('tRPC Error:', error);
    throw error;
  }
});

// 8. Rate limiting middleware
const rateLimit = t.middleware(async ({ next, ctx }) => {
  const clientIP = ctx.req.ip;
  const currentTime = Date.now();

  // In production, you'd use Redis or a proper rate limiting store
  const requestCounts = new Map<string, { count: number; resetTime: number }>();

  const existing = requestCounts.get(clientIP);

  if (existing && currentTime < existing.resetTime) {
    if (existing.count >= 10) { // 10 requests per minute
      throw new TRPCError({
        code: 'TOO_MANY_REQUESTS',
        message: 'Rate limit exceeded. Please try again later.'
      });
    }
    existing.count++;
  } else {
    requestCounts.set(clientIP, {
      count: 1,
      resetTime: currentTime + 60000, // 1 minute
    });
  }

  return next();
});

// 9. Protected procedure creator
const protectedProcedure = t.procedure
  .use(errorHandler)
  .use(logger)
  .use(rateLimit)
  .use(isAuthed);

const adminProcedure = protectedProcedure.use(hasRole('admin'));

// 10. Create router with middleware
export const authRouter = t.router({
  // Public procedures
  login: t.procedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected credentials object');
      }
      return val as { email: string; password: string };
    })
    .mutation(({ input }) => {
      // Simulate authentication
      const user = db.users.find(u => u.email === input.email);

      if (!user || input.password !== 'password') {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'Invalid credentials'
        });
      }

      const token = jwt.sign(
        { userId: user.id },
        process.env.JWT_SECRET || 'secret-key',
        { expiresIn: '24h' }
      );

      return { token, user };
    }),

  // Protected procedures
  getProfile: protectedProcedure
    .query(({ ctx }) => {
      return ctx.user;
    }),

  updateProfile: protectedProcedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected profile data');
      }
      return val as { name?: string; email?: string };
    })
    .mutation(({ input, ctx }) => {
      if (!ctx.user) {
        throw new TRPCError({ code: 'UNAUTHORIZED' });
      }

      const userIndex = db.users.findIndex(u => u.id === ctx.user!.id);
      if (userIndex === -1) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
      }

      // Update user
      const updatedUser = { ...db.users[userIndex] };
      if (input.name) updatedUser.name = input.name;
      if (input.email) updatedUser.email = input.email;

      db.users[userIndex] = updatedUser;

      return updatedUser;
    }),

  // Admin-only procedures
  getUsers: adminProcedure
    .query(() => {
      return db.users;
    }),

  deleteUser: adminProcedure
    .input((val: unknown) => {
      if (typeof val !== 'string') {
        throw new Error('Expected user ID string');
      }
      return val;
    })
    .mutation(({ input }) => {
      const userIndex = db.users.findIndex(u => u.id === input);
      if (userIndex === -1) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
      }

      const deletedUser = db.users[userIndex];
      db.users.splice(userIndex, 1);

      return { success: true, deletedUser };
    }),

  // Post management with ownership validation
  createPost: protectedProcedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected post data');
      }
      const { title, content } = val as { title?: unknown; content?: unknown };

      if (typeof title !== 'string' || typeof content !== 'string') {
        throw new Error('Title and content must be strings');
      }

      return { title, content };
    })
    .mutation(({ input, ctx }) => {
      const post = {
        id: Math.random().toString(36).substr(2, 9),
        title: input.title,
        content: input.content,
        authorId: ctx.user!.id,
        published: false,
        createdAt: new Date(),
      };

      db.posts.push(post);
      return post;
    }),

  // Procedure with multiple middleware
  getSensitiveData: protectedProcedure
    .use(logger)
    .use(rateLimit)
    .query(({ ctx }) => {
      // This procedure uses both auth middleware and additional middleware
      return {
        user: ctx.user,
        data: 'This is sensitive data only accessible to authenticated users',
        timestamp: new Date().toISOString(),
      };
    }),

  // Custom error handling
  riskyOperation: protectedProcedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected config object');
      }
      return val as { shouldFail: boolean };
    })
    .mutation(({ input }) => {
      if (input.shouldFail) {
        throw new TRPCError({
          code: 'INTERNAL_SERVER_ERROR',
          message: 'The operation failed as requested',
          cause: new Error('Simulated failure')
        });
      }

      return { success: true, message: 'Operation completed successfully' };
    }),
});

// 11. Validation middleware example
const validateInput = <T>(validator: (input: unknown) => T) =>
  t.middleware(async ({ next, rawInput }) => {
    if (!rawInput) {
      throw new TRPCError({ code: 'BAD_REQUEST', message: 'Input is required' });
    }

    try {
      const parsed = validator(rawInput);
      return next({ ctx: { parsedInput: parsed } });
    } catch (error) {
      throw new TRPCError({
        code: 'BAD_REQUEST',
        message: `Validation error: ${error.message}`
      });
    }
  });

// Example with validation middleware
const validatedRouter = t.router({
  createUserWithValidation: t.procedure
    .input((val: unknown) => {
      if (typeof val !== 'object' || val === null) {
        throw new Error('Expected user object');
      }
      const user = val as { name: unknown; email: unknown; age: unknown };

      if (typeof user.name !== 'string' || user.name.length < 2) {
        throw new Error('Name must be at least 2 characters');
      }

      if (typeof user.email !== 'string' || !user.email.includes('@')) {
        throw new Error('Valid email is required');
      }

      if (typeof user.age !== 'number' || user.age < 18) {
        throw new Error('Age must be at least 18');
      }

      return user as { name: string; email: string; age: number };
    })
    .use(validateInput((input: unknown) => {
      // Additional validation logic here
      return input as { name: string; email: string; age: number };
    }))
    .mutation(({ ctx }) => {
      const { parsedInput } = ctx as { parsedInput: { name: string; email: string; age: number } };

      const newUser: User = {
        id: Math.random().toString(36).substr(2, 9),
        email: parsedInput.email,
        name: parsedInput.name,
        role: 'user'
      };

      db.users.push(newUser);
      return { success: true, user: newUser };
    }),
});

// 12. Combined router
export const combinedRouter = t.router({
  auth: authRouter,
  validated: validatedRouter,
});

export type AppRouter = typeof combinedRouter;

// 13. Express server setup with middleware
const app = express();
app.use(express.json());

app.use('/trpc', createExpressMiddleware({
  router: combinedRouter,
  createContext: ({ req }) => ({ req }),
  onError: ({ error, type, path, input, ctx, req }) => {
    // Global error handling
    console.error(`tRPC Error on ${type.toUpperCase()} ${path}:`, error);

    if (error.code === 'INTERNAL_SERVER_ERROR') {
      // Log internal errors
      console.error('Internal error details:', error.cause);
    }
  },
}));

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`tRPC server with middleware running on port ${PORT}`);
});

export default app;
CódigotypescriptIntermediário

Integração tRPC React

Integração de tRPC com React incluindo hooks, queries, mutations, subscrições e integração com TanStack Query

Dificuldade

8/10

Tempo estimado

50 min

Etiquetas

trpc, react, frontend, integration, typescript

Pré-requisitos

tRPC basics, React hooks, TanStack Query, TypeScript

typescript
// tRPC React Integration Example
// Frontend React integration with tRPC and TanStack Query

import React, { useState } from 'react';
import { createTRPCReact } from '@trpc/react-query';
import { httpBatchLink } from '@trpc/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

// 1. tRPC React Query setup
export const trpc = createTRPCReact<AppRouter>();

// 2. Create tRPC client
const getTRPCClient = () => {
  return trpc.createClient({
    links: [
      httpBatchLink({
        url: 'http://localhost:3000/trpc',
        headers: () => {
          const token = localStorage.getItem('auth-token');
          return token ? { Authorization: `Bearer ${token}` } : {};
        },
      }),
    ],
  });
};

// 3. Query Client setup
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      retry: (failureCount, error) => {
        // Don't retry on 4xx errors
        if (error.data?.code === 'UNAUTHORIZED' || error.data?.code === 'FORBIDDEN') {
          return false;
        }
        return failureCount < 3;
      },
    },
  },
});

// 4. App wrapper component
export function App() {
  const [trpcClient] = useState(() => getTRPCClient());

  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <div className="app">
          <Navigation />
          <Routes />
        </div>
      </QueryClientProvider>
    </trpc.Provider>
  );
}

// 5. Navigation component
function Navigation() {
  const utils = trpc.useContext();

  const handleLogout = () => {
    localStorage.removeItem('auth-token');
    // Invalidate all queries when logging out
    utils.auth.getProfile.invalidate();
  };

  return (
    <nav>
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/profile">Profile</a></li>
        <li><a href="/posts">Posts</a></li>
      </ul>
      <button onClick={handleLogout}>Logout</button>
    </nav>
  );
}

// 6. Authentication component
function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const login = trpc.auth.login.useMutation({
    onSuccess: (data) => {
      localStorage.setItem('auth-token', data.token);
      // Invalidate and refetch user profile
      trpc.auth.getProfile.invalidate();
    },
    onError: (error) => {
      alert(`Login failed: ${error.message}`);
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    login.mutate({ email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <h2>Login</h2>
      <div>
        <label>Email:</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>
      <div>
        <label>Password:</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>
      <button type="submit" disabled={login.isLoading}>
        {login.isLoading ? 'Logging in...' : 'Login'}
      </button>
      {login.error && <p className="error">{login.error.message}</p>}
    </form>
  );
}

// 7. Profile component with tRPC hooks
function Profile() {
  const { data: user, isLoading, error } = trpc.auth.getProfile.useQuery();
  const utils = trpc.useContext();

  const [isEditing, setIsEditing] = useState(false);
  const [editName, setEditName] = useState(user?.name || '');

  const updateProfile = trpc.auth.updateProfile.useMutation({
    onSuccess: () => {
      setIsEditing(false);
      // Invalidate to refetch updated data
      utils.auth.getProfile.invalidate();
    },
  });

  const handleSave = () => {
    updateProfile.mutate({ name: editName });
  };

  if (isLoading) return <div>Loading profile...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return <div>Please log in to view your profile</div>;

  return (
    <div className="profile">
      <h2>Profile</h2>
      <div>
        <strong>Email:</strong> {user.email}
      </div>
      <div>
        <strong>Role:</strong> {user.role}
      </div>
      <div>
        <strong>Name:</strong>
        {isEditing ? (
          <>
            <input
              type="text"
              value={editName}
              onChange={(e) => setEditName(e.target.value)}
            />
            <button onClick={handleSave} disabled={updateProfile.isLoading}>
              {updateProfile.isLoading ? 'Saving...' : 'Save'}
            </button>
            <button onClick={() => setIsEditing(false)}>Cancel</button>
          </>
        ) : (
          <>
            {user.name}
            <button onClick={() => {
              setEditName(user.name);
              setIsEditing(true);
            }}>
              Edit
            </button>
          </>
        )}
      </div>
      {updateProfile.error && (
        <p className="error">{updateProfile.error.message}</p>
      )}
    </div>
  );
}

// 8. Posts management component
function PostsManagement() {
  const { data: posts, isLoading } = trpc.auth.createPost.useQuery(undefined, {
    // Custom query key to fetch posts
    queryKey: ['posts'],
    // This is a workaround since we don't have a dedicated getPosts procedure
  });

  const createPost = trpc.auth.createPost.useMutation();
  const utils = trpc.useContext();

  const [newPost, setNewPost] = useState({ title: '', content: '' });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    createPost.mutate(newPost, {
      onSuccess: () => {
        setNewPost({ title: '', content: '' });
        // Invalidate posts list
        utils.auth.createPost.invalidate();
      },
    });
  };

  if (isLoading) return <div>Loading posts...</div>;

  return (
    <div className="posts">
      <h2>Create Post</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <input
            type="text"
            placeholder="Title"
            value={newPost.title}
            onChange={(e) => setNewPost({ ...newPost, title: e.target.value })}
            required
          />
        </div>
        <div>
          <textarea
            placeholder="Content"
            value={newPost.content}
            onChange={(e) => setNewPost({ ...newPost, content: e.target.value })}
            required
          />
        </div>
        <button type="submit" disabled={createPost.isLoading}>
          {createPost.isLoading ? 'Creating...' : 'Create Post'}
        </button>
        {createPost.error && (
          <p className="error">{createPost.error.message}</p>
        )}
      </form>

      <h3>Posts</h3>
      {posts && (
        <div className="posts-list">
          {/* Render posts here */}
          <p>Posts would be rendered here with actual data</p>
        </div>
      )}
    </div>
  );
}

// 9. Data fetching with custom hooks
function useUserData() {
  const { data: user, isLoading: userLoading } = trpc.auth.getProfile.useQuery();
  const { data: posts, isLoading: postsLoading } = trpc.auth.createPost.useQuery(undefined, {
    queryKey: ['user-posts'],
  });

  return {
    user,
    posts,
    isLoading: userLoading || postsLoading,
  };
}

function UserDashboard() {
  const { user, posts, isLoading } = useUserData();

  if (isLoading) return <div>Loading dashboard...</div>;
  if (!user) return <div>Please log in</div>;

  return (
    <div className="dashboard">
      <h2>Welcome, {user.name}!</h2>
      <p>You have {posts?.length || 0} posts</p>

      <div className="stats">
        <div className="stat-card">
          <h3>{user.role === 'admin' ? 'Admin Dashboard' : 'User Dashboard'}</h3>
          <p>Role: {user.role}</p>
        </div>
      </div>
    </div>
  );
}

// 10. Optimistic updates example
function TodoList() {
  const { data: todos = [] } = trpc.todos.list.useQuery();
  const utils = trpc.useContext();

  const addTodo = trpc.todos.add.useMutation({
    onMutate: async (newTodo) => {
      // Cancel any outgoing refetches
      await utils.todos.list.cancel();

      // Snapshot the previous value
      const previousTodos = utils.todos.list.getData();

      // Optimistically update to the new value
      utils.todos.list.setData(undefined, (old) => [
        ...(old || []),
        { ...newTodo, id: 'temp-' + Date.now(), completed: false }
      ]);

      // Return context with the previous value
      return { previousTodos };
    },
    onError: (err, newTodo, context) => {
      // If the mutation fails, use the context returned from onMutate
      utils.todos.list.setData(undefined, context?.previousTodos);
    },
    onSettled: () => {
      // Always refetch after error or success
      utils.todos.list.invalidate();
    },
  });

  return (
    <div>
      <h2>Todos</h2>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
      <button
        onClick={() => addTodo.mutate({ title: 'New Todo' })}
        disabled={addTodo.isLoading}
      >
        Add Todo
      </button>
    </div>
  );
}

// 11. Subscription component
function RealTimeData() {
  const { data, error, isLoading } = trpc.onHello.useSubscription(undefined, {
    onData(data) {
      console.log('Received data:', data);
    },
    onError(err) {
      console.error('Subscription error:', err);
    },
  });

  if (isLoading) return <div>Connecting to real-time updates...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h2>Real-time Updates</h2>
      <p>Latest message: {data?.greeting}</p>
    </div>
  );
}

// 12. Error boundary for tRPC errors
class TrpcErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean; error?: Error }
> {
  constructor(props: { children: React.ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('tRPC Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>Something went wrong</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false, error: undefined })}>
            Try again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// 13. Main app routes
function Routes() {
  const [route, setRoute] = useState('/');

  const renderRoute = () => {
    switch (route) {
      case '/':
        return <UserDashboard />;
      case '/profile':
        return <Profile />;
      case '/posts':
        return <PostsManagement />;
      case '/todos':
        return <TodoList />;
      case '/realtime':
        return <RealTimeData />;
      default:
        return <LoginForm />;
    }
  };

  return (
    <TrpcErrorBoundary>
      <div>
        <nav style={{ marginBottom: '20px' }}>
          <button onClick={() => setRoute('/')}>Dashboard</button>
          <button onClick={() => setRoute('/profile')}>Profile</button>
          <button onClick={() => setRoute('/posts')}>Posts</button>
          <button onClick={() => setRoute('/todos')}>Todos</button>
          <button onClick={() => setRoute('/realtime')}>Real-time</button>
        </nav>
        {renderRoute()}
      </div>
    </TrpcErrorBoundary>
  );
}

// 14. Custom hook for offline support
function useOfflineSupport() {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  React.useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

// Usage example
function AppWithOfflineSupport() {
  const isOnline = useOfflineSupport();

  return (
    <div className={isOnline ? 'online' : 'offline'}>
      {!isOnline && <div className="offline-warning">You are offline</div>}
      <App />
    </div>
  );
}

export default AppWithOfflineSupport;

Ferramentas

Ferramentas frequentemente usadas com este exemplo

Relacionado

Coleções de exemplos relacionadas

Guia de uso da ferramenta

Contexto para entender esta coleção de exemplos

Título

Exemplos de tRPC

Descrição

Exemplos de tRPC incluindo APIs seguras de ponta a ponta, procedimentos, middleware, autenticação e padrões de integração cliente-servidor

Visão geral

Exemplos de tRPC incluindo APIs seguras de ponta a ponta, procedimentos, middleware, autenticação e padrões de integração cliente-servidor

Quando usar

  • Use this sample set when you want reference inputs for Exemplos de tRPC.
  • Use it to preview output formats and compare related sample variants.

Como funciona

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Casos de uso

  • Configuração Básica tRPC
  • Middleware e Autenticação tRPC
  • Integração tRPC React

Perguntas frequentes

What is included in Exemplos de tRPC?

This page includes curated sample entries, related tools, and neighboring sample collections for Exemplos de tRPC.

Início
Ferramentas3197
Exemplos593
Visualizações461
Visualizadores de arquivos103
Temas229
Etiquetas333
Imagens, áudio e vídeo655

Ferramentas de processamento, conversão e otimização de imagens, áudio e vídeo

Matemática, datas e finanças644

Calculadoras, ferramentas numéricas, lógica de datas, estatística e finanças

Design e cor296

Ferramentas de cor, layout, gráficos, estilo visual e apoio ao design

Texto e escrita209

Ferramentas de escrita, limpeza, formatação, extração e análise de texto

Desenvolvimento e Web193

Utilitários para desenvolvedores, rede, depuração web e automação

Conversão e codificação175

Ferramentas de conversão de formatos, arquivos, unidades, arquivos compactados e codificação

Estruturar uma captura de interface, revisar e entregar ao desenvolvimento
Esboço de piano, revisão MIDI e entrega de uma demonstração de áudio
Revisão de texturas retrô e entrega de recursos
Revisão e entrega de guias de procedimento com capturas
Revisão visual de alterações responsivas e entrega de evidências

© 2026 Elysia Tools. Todos os direitos reservados.

DocumentaçãoPreçosSobreTermos de serviçoPolítica de privacidadeCookiesContato

Development

JSON para TypeScript

Converte dados JSON em interfaces TypeScript ou aliases de tipo, com tipos união, campos opcionais e inferência de objetos aninhados

Frameworks Web

Exemplos Elysia.js

Exemplos do framework web TypeScript Elysia.js incluindo Hello World, roteamento, validação e desenvolvimento de plugins

Frameworks Web

Exemplos NestJS

Exemplos do framework enterprise Node.js NestJS incluindo módulos, controladores, serviços e implantação