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

No packages published