Skip to content

bitswired/quick-effect-tanstack-boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 TanStack Start + Effect HttpAPI Integration

A comprehensive tutorial repository demonstrating how to integrate TanStack Start (React framework) with Effect HttpAPI (functional backend) in a modern Bun monorepo setup.

📋 Table of Contents

🎯 Overview

This repository demonstrates a production-ready integration between:

  • Backend: Effect HttpAPI with functional programming patterns
  • Frontend: TanStack Start with React 19 and TypeScript
  • Monorepo: Bun workspace for optimal dependency management
  • Type Safety: End-to-end type safety from database to UI
  • Error Handling: Comprehensive error management with Effect's type system

Why This Stack?

  • 🔒 Type Safety: Bidirectional type safety between frontend and backend
  • ⚡ Performance: Bun's fast runtime and bundling
  • 🛡️ Error Handling: Effect's robust error management system
  • 🔄 Real-time Ready: Built with live data synchronization in mind
  • 📦 Monorepo Benefits: Shared types and unified development experience

✨ Features

Backend (Effect HttpAPI)

  • ✅ Functional HTTP API with Effect
  • ✅ Type-safe endpoints with automatic serialization
  • ✅ Comprehensive error handling
  • ✅ CORS configuration
  • ✅ Swagger documentation
  • ✅ Structured logging
  • ✅ Service layer architecture

Frontend (TanStack Start)

  • ✅ React 19 with modern hooks
  • ✅ TanStack Router for navigation
  • ✅ TanStack Query for data fetching
  • ✅ Custom Effect integration hooks
  • ✅ Tailwind CSS + Radix UI components
  • ✅ TypeScript with strict configuration
  • ✅ Biome for formatting and linting

Development Experience

  • ✅ Hot reload for both frontend and backend
  • ✅ Shared TypeScript configuration
  • ✅ Workspace dependencies
  • ✅ Type generation and validation
  • ✅ Development server setup

🏗️ Architecture

┌─────────────────┐    HTTP/JSON    ┌─────────────────┐
│   TanStack      │ ◄─────────────► │   Effect        │
│   Start App     │                 │   HttpAPI       │
│                 │                 │                 │
│ • React 19      │                 │ • Functional    │
│ • TanStack      │                 │ • Type-safe     │
│   Router        │                 │ • Error         │
│ • TanStack      │                 │   handling      │
│   Query         │                 │ • Bun runtime   │
│ • Custom hooks  │                 │ • Swagger docs  │
└─────────────────┘                 └─────────────────┘

Data Flow

  1. API Contract Definition: Effect HttpAPI endpoints with schemas
  2. Type Generation: Shared types between frontend and backend
  3. Data Fetching: Custom hooks wrapping TanStack Query + Effect
  4. Error Handling: Comprehensive error boundaries and user feedback
  5. State Management: TanStack Query for server state

🔧 Prerequisites

Before getting started, ensure you have:

  • Bun: Version 1.0+ (Install Bun)
  • Git: For version control

Verify Installation

bun --version

⚡ Quick Start

1. Clone and Install

# Clone the repository
git clone <repository-url>
cd quick-effect-tanstack

# Install dependencies for the entire monorepo
bun install

2. Start Development Servers

Terminal 1 - Backend (Effect HttpAPI):

bun --filter=api run dev

Terminal 2 - Frontend (TanStack Start):

bun --filter=app run dev

3. Open Your Browser

4. Test the Integration

The application includes a fully functional Todo app demonstrating:

  • Creating todos
  • Listing all todos
  • Toggling completion status
  • Deleting individual or all todos
  • Real-time updates via TanStack Query

📁 Project Structure

quick-effect-tanstack/
├── 📦 package.json                 # Root workspace configuration
├── 🔧 tsconfig.json               # Shared TypeScript config
├── 🔒 bun.lock                    # Dependency lock file
└── 📁 apps/
    ├── 📁 api/                     # Effect HttpAPI Backend
    │   ├── 📦 package.json
    │   ├── 🚀 index.ts            # Entry point
    │   └── 📁 src/
    │       ├── 🎯 index.ts         # Server setup
    │       └── 📁 domains/
    │           ├── 📋 api.ts       # API composition
    │           ├── 📄 index.ts
    │           └── 📁 todos/
    │               ├── 📜 contract.ts   # HTTP endpoints
    │               ├── 📊 dtos.ts       # Data schemas
    │               ├── 🏷️ group.ts      # API group
    │               └── 🔧 service.ts    # Business logic
    └── 📁 app/                     # TanStack Start Frontend
        ├── 📦 package.json
        ├── ⚙️ vite.config.ts
        ├── 🎨 biome.json
        ├── 📦 components.json      # Radix UI config
        ├── 🔧 tsconfig.json
        ├── 📁 public/              # Static assets
        └── 📁 src/
            ├── 🎨 styles.css
            ├── 🧭 router.tsx       # Router setup
            ├── 🌳 routeTree.gen.ts # Generated routes
            ├── 📁 components/
            │   ├── 🧩 Header.tsx
            │   └── 📁 ui/          # Radix UI components
            ├── 📁 integrations/
            │   └── 📁 tanstack-query/
            ├── 📁 lib/
            │   ├── 📊 data.ts      # Effect + TanStack Query
            │   └── 🛠️ utils.ts
            └── 📁 routes/
                ├── 🏠 __root.tsx
                └── 📄 index.tsx    # Todo app demo

🔌 API Documentation

Endpoints Overview

Method Endpoint Description
GET /todos Get all todos
POST /todos Create a new todo
GET /todos/:id Get todo by ID
PUT /todos/:id Update todo
DELETE /todos/:id Delete todo
DELETE /todos Delete all todos

Schema Definitions

Todo Schema

class Todo extends Schema.Class<Todo>("Todo")({
  id: Schema.String,
  title: Schema.String,
  description: Schema.optional(Schema.String),
  completed: Schema.Boolean,
  createdAt: Schema.Date,
  updatedAt: Schema.Date,
}) {}

CreateTodo Schema

class CreateTodo extends Schema.Class<CreateTodo>("CreateTodo")({
  title: Schema.String,
  description: Schema.optional(Schema.String),
}) {}

Error Handling

The API uses Effect's structured error handling:

  • HttpApiError.BadRequest - Invalid input data
  • HttpApiError.NotFound - Resource not found
  • HttpApiError.InternalServerError - Server errors

🎨 Frontend Integration

Custom Effect Hooks

The frontend uses custom hooks that integrate Effect with TanStack Query:

useEffectQuery

const todos = useEffectQuery("todos", "getAllTodos", {});

Benefits:

  • Type-safe API calls
  • Automatic error handling
  • Caching and background updates
  • Loading states

useEffectMutation

const createTodo = useEffectMutation("todos", "createTodo", {
  onSuccess: () => {
    todos.refetch();
  },
});

Features:

  • Optimistic updates
  • Error recovery
  • Loading states
  • Success callbacks

Example Usage

function TodoApp() {
  const todos = useEffectQuery("todos", "getAllTodos", {});
  
  const createTodo = useEffectMutation("todos", "createTodo", {
    onSuccess: () => todos.refetch(),
  });

  const handleSubmit = (data: CreateTodo) => {
    createTodo.mutate({ payload: data });
  };

  if (todos.isLoading) return <div>Loading...</div>;
  if (todos.error) return <div>Error: {todos.error.message}</div>;

  return (
    <div>
      {todos.data?.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </div>
  );
}

🔄 Development Workflow

Backend Development

  1. Add New Endpoint:

    // apps/api/src/domains/todos/contract.ts
    const newEndpoint = HttpApiEndpoint.get("newEndpoint", "/endpoint")
      .addSuccess(Schema.String)
      .addError(HttpApiError.BadRequest);
  2. Implement Service:

    // apps/api/src/domains/todos/service.ts
    const newEndpointImpl = (request: HttpRequest) =>
      Effect.gen(function* () {
        // Implementation logic
      });
  3. Update Group:

    // apps/api/src/domains/todos/group.ts
    export const TodoGroup = HttpApiGroup.make("todos")
      .add(newEndpoint)
      .handleRaw(newEndpoint, newEndpointImpl);

Frontend Development

  1. Use the New Endpoint:

    // Automatically available through the shared API type
    const data = useEffectQuery("todos", "newEndpoint", {});
  2. Handle Loading States:

    if (data.isLoading) return <Spinner />;
    if (data.error) return <ErrorMessage error={data.error} />;
    return <DataComponent data={data.data} />;

Hot Reload

Both applications support hot reload:

  • Backend: Automatic restart on file changes
  • Frontend: Fast refresh with state preservation

🛡️ Type Safety & Error Handling

End-to-End Type Safety

  1. Schema Definition (Backend):

    class User extends Schema.Class<User>("User")({
      id: Schema.String,
      email: Schema.String.pipe(Schema.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)),
      name: Schema.String,
    }) {}
  2. API Contract (Backend):

    const getUser = HttpApiEndpoint.get("getUser", "/users/:id")
      .setPath(Schema.Struct({ id: Schema.String }))
      .addSuccess(User)
      .addError(HttpApiError.NotFound);
  3. Frontend Usage (Automatically typed):

    const user = useEffectQuery("users", "getUser", { path: { id: "123" } });
    // user.data is automatically typed as User | undefined

Error Handling Patterns

Backend Error Handling

const getTodo = (id: string) =>
  Effect.gen(function* () {
    const todo = yield* findTodoById(id);
    if (!todo) {
      return yield* Effect.fail(new HttpApiError.NotFound());
    }
    return todo;
  });

Frontend Error Handling

const todos = useEffectQuery("todos", "getAllTodos", {
  onError: (error) => {
    console.error("Failed to fetch todos:", error);
    // Show user-friendly error message
  },
});

⚙️ Configuration

Environment Variables

Backend (.env)

SERVER_PORT=8080
SERVER_CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001
LOG_LEVEL=info

Frontend (.env)

VITE_API_URL=http://localhost:8080
VITE_APP_TITLE=Todo App

CORS Configuration

The backend automatically configures CORS for development:

HttpApiBuilder.middlewareCors({
  allowedOrigins: ["http://localhost:3000"],
  credentials: true,
})

TypeScript Configuration

The monorepo uses a shared TypeScript configuration with strict settings:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "moduleResolution": "bundler"
  }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors