0.1.163 • Published 1 year ago

react-edge v0.1.163

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

React Edge Framework 🚀

A revolutionary React framework for building blazing-fast applications on the edge, powered by Cloudflare Workers

TypeScript Vitest MIT License

🚀 Motivation

Read the full story on DEV.to

🌟 Features

  • Powerful Data Fetching & RPC: Write server-side methods and use them with an incredibly flexible fetching system:
// Define your types
type User = {
  id: string;
  name: string;
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
  };
};

type Product = {
  id: string;
  name: string;
  price: number;
  category: string;
};

// Server: Your API methods
class API extends Rpc {
  async getUser(id: string): Promise<User> {
    return this.db.users.findById(id);
  }

  async getProducts(category: string): Promise<Product[]> {
    return this.db.products.findByCategory(category);
  }

  async searchProducts(query: string): Promise<Product[]> {
    return this.db.products.search(query);
  }
}

// Client: Unleash the power of useFetch!
const Dashboard = () => {
  const { rpc } = app.useContext<App.Context>();
  const [searchQuery, setSearchQuery] = useState('');
  const [selectedCategory, setSelectedCategory] = useState('all');

  // 🚀 Basic fetch with automatic SSR and hydration
  const { data: user } = app.useFetch(async (ctx, id) => {
	return ctx.rpc.getUser(id ?? '123');
  });

  // 🚀 Reactive fetch with dependencies
  const { data: products, loading, error, fetch: refreshProducts } = app.useFetch(
    async ctx => ctx.rpc.getProducts(selectedCategory),
    {
      // Refetch when category changes
      deps: [selectedCategory]
    }
  );

  // 🚀 Debounced search with automatic error handling
  const { data: searchResults } = app.useFetch(
    async ctx => ctx.rpc.searchProducts(searchQuery),
    {
      // Only fetch if we have a query
      shouldFetch: () => searchQuery.length > 2,
	  deps: [searchQuery],
      // Debounce the API calls
      depsDebounce: 300
    }
  );

  // 🚀 Parallel fetching with batch
  const { data: dashboardData } = app.useFetch(
    async ctx => {
      const [
        userProfile,
        recentProducts,
        recommendations
      ] = await ctx.rpc.batch([
        ctx.rpc.getUser('123'),
        ctx.rpc.getProducts('recent'),
        ctx.rpc.searchProducts('recommended')
      ]);

      return {
        profile: userProfile,
        recent: recentProducts,
        recommended: recommendations
      };
    },
    {
      // Automatic polling every 30 seconds
      interval: 30000
    }
  );

  return (
    <div>
      {/* All your data is fully typed! */}
      <UserProfile user={user} />

      <ProductGrid
        products={products}
        loading={loading}
        onRefresh={refreshProducts}
      />

      <SearchResults results={searchResults} />

      <DashboardWidgets data={dashboardData} />
    </div>
  );
};

🎯 useFetch Features:

  • Seamless SSR & Hydration: Data is preloaded during SSR and hydrated on the client automatically
  • Dependency-based Refetching: Reactive updates when dependencies change
  • Smart Debouncing: Control the frequency of API calls with depsDebounce
  • Intelligent Batching: Multiple RPC calls are automatically combined into a single request
  • Simple Polling: Easy periodic data updates with interval
  • Type Safety: Full TypeScript support with zero manual type declarations
  • Conditional Fetching: Control when fetching should occur with shouldFetch
  • Manual Controls: Programmatic refresh capabilities with fetch function
  • Performance Optimized: Automatic request deduplication and caching

📦 Installation

npm install react-edge

🚀 Quick Start

1. Set up your Worker

// worker.ts
const handler = {
	fetch: async (req: Request, env: types.Worker.Env, context: ExecutionContext) => {
		const workerApp = new AppWorkerEntry({
			i18n: {
				en: await import('./translations/en'),
				es: await import('./translations/es')
			}
		});

		return await workerApp.fetch();
	}
};

2. Create your first RPC endpoint

class UserAPI extends Rpc {
	async getProfile(id: string) {
		const user = await this.db.users.findById(id);

		return this.createResponse(user, {
			cache: {
				tags: [`user:${id}`],
				ttlSeconds: 300 // Cache for 5 minutes
			}
		});
	}
}

3. Use it in your components

const UserProfile = () => {
  const { rpc } = app.useContext<App.Context>();

  const { data: profile, loading } = app.useFetch(
    async ctx => ctx.rpc.getProfile('123')
  );

  if (loading) return <Loading />;

  return <div>Welcome, {profile.name}!</div>;
};

🛠 Core Features

Typed RPC System

The RPC system enables seamless client-server communication with full TypeScript support:

class ProductsAPI extends Rpc {
	// Private methods (not exposed to client)
	private async _validateProduct(data: ProductData) {
		// or $validateProduct , or #validateProduct
		return productSchema.parse(data);
	}

	// Public methods (accessible via RPC)
	async createProduct(data: ProductData) {
		const validated = await this._validateProduct(data);
		return this.db.products.create(validated);
	}
}

Smart Caching

Built-in caching system with tag-based invalidation:

class CacheExample extends Rpc {
	async getProducts(category: string) {
		const products = await this.db.products.findByCategory(category);

		return this.createResponse(products, {
			cache: {
				tags: [`category:${category}`, 'products'],
				ttlSeconds: 3600 // 1 hour
			}
		});
	}

	async updateProduct(id: string, data: ProductData) {
		await this.db.products.update(id, data);

		// Invalidate specific caches
		await this.cache.deleteBy({
			tags: [`product:${id}`, `category:${data.category}`]
		});
	}
}

Authentication

Simple JWT authentication with secure cookie management:

class AuthAPI extends Rpc {
	private auth = new AuthJwt({
		cookie: 'token',
		encrypt: true,
		expires: { days: 1 },
		secret: process.env.JWT_SECRET
	});

	async login(credentials: LoginData) {
		const { headers } = await this.auth.sign(credentials);
		return this.createResponse({ success: true }, { headers });
	}
}

State Management

URL-Synced State

const FiltersComponent = () => {
  const [filters, setFilters] = app.useUrlState({
    category: 'all',
    minPrice: 0,
    maxPrice: 1000
  }, {
    debounce: 500,
    kebabCase: true
  });

  return <FilterPanel value={filters} onChange={setFilters} />;
};

Persistent State

const UserPreferences = () => {
  const [prefs, setPrefs] = app.useStorageState('user-prefs', {
    theme: 'light',
    fontSize: 16
  }, {
    storage: 'local',
    debounce: 300
  });

  return <PreferencesPanel value={prefs} onChange={setPrefs} />;
};

🛠 CLI Commands

React Edge comes with a powerful CLI for development and deployment:

Development

# Start development server
yarn edge dev

# Start development server for app only
yarn edge dev --app

# Start development server for worker only
yarn edge dev --worker

Build

# Build entire project
yarn edge build

# Build for specific environment
yarn edge build --env production

# Build app or worker separately
yarn edge build --app
yarn edge build --worker

Other Commands

# Deploy to Cloudflare Workers
yarn edge deploy

# View worker logs
yarn edge logs

# Run linting
yarn edge lint

# Run tests
yarn edge test

# Type checking
yarn edge type-check

🌍 Internationalization

Easy i18n support with variable interpolation:

// translations/fr.ts
export default {
  'Welcome, {name}!': 'Bienvenue, {name}!'
};

// Component
const Welcome = () => (
  <h1>{__('Welcome, {name}!', { name: 'John' })}</h1>
);

📚 Best Practices

  1. RPC Organization

    • Keep related functionality in dedicated RPC classes
    • Use private methods for internal logic
    • Leverage caching for frequently accessed data
  2. State Management

    • Use useUrlState for shareable state
    • Use useStorageState for persistent preferences
    • Use useDistinct to optimize updates
  3. Performance

    • Implement appropriate cache strategies
    • Use Link component for preloading
    • Leverage edge caching when possible

📝 License

MIT © Felipe Rohde

👨‍💻 Author

Felipe Rohde

0.1.52

2 years ago

0.1.53

2 years ago

0.1.54

2 years ago

0.1.55

2 years ago

0.1.56

2 years ago

0.1.57

2 years ago

0.1.58

2 years ago

0.1.59

2 years ago

0.1.50

2 years ago

0.1.51

2 years ago

0.1.118

1 year ago

0.1.117

1 year ago

0.1.119

1 year ago

0.1.114

1 year ago

0.1.113

1 year ago

0.1.116

1 year ago

0.1.115

1 year ago

0.1.110

1 year ago

0.1.49

2 years ago

0.1.112

1 year ago

0.1.111

1 year ago

0.1.41

2 years ago

0.1.42

2 years ago

0.1.43

2 years ago

0.1.44

2 years ago

0.1.45

2 years ago

0.1.46

2 years ago

0.1.47

2 years ago

0.1.48

2 years ago

0.1.40

2 years ago

0.1.129

1 year ago

0.1.128

1 year ago

0.1.125

1 year ago

0.1.124

1 year ago

0.1.127

1 year ago

0.1.126

1 year ago

0.1.121

1 year ago

0.1.38

2 years ago

0.1.120

1 year ago

0.1.39

2 years ago

0.1.123

1 year ago

0.1.122

1 year ago

0.1.30

2 years ago

0.1.31

2 years ago

0.1.32

2 years ago

0.1.33

2 years ago

0.1.34

2 years ago

0.1.35

2 years ago

0.1.36

2 years ago

0.1.37

2 years ago

0.1.139

1 year ago

0.1.136

1 year ago

0.1.135

1 year ago

0.1.138

1 year ago

0.1.137

1 year ago

0.1.132

1 year ago

0.1.27

2 years ago

0.1.131

1 year ago

0.1.28

2 years ago

0.1.134

1 year ago

0.1.29

2 years ago

0.1.133

1 year ago

0.1.130

1 year ago

0.1.20

2 years ago

0.1.21

2 years ago

0.1.22

2 years ago

0.1.23

2 years ago

0.1.24

2 years ago

0.1.25

2 years ago

0.1.26

2 years ago

0.1.147

1 year ago

0.1.146

1 year ago

0.1.149

1 year ago

0.1.148

1 year ago

0.1.143

1 year ago

0.1.142

1 year ago

0.1.145

1 year ago

0.1.144

1 year ago

0.1.141

1 year ago

0.1.140

1 year ago

0.1.99

1 year ago

0.1.90

1 year ago

0.1.91

1 year ago

0.1.92

1 year ago

0.1.93

1 year ago

0.1.94

1 year ago

0.1.95

1 year ago

0.1.158

1 year ago

0.1.157

1 year ago

0.1.159

1 year ago

0.1.154

1 year ago

0.1.153

1 year ago

0.1.156

1 year ago

0.1.155

1 year ago

0.1.150

1 year ago

0.1.152

1 year ago

0.1.151

1 year ago

0.1.85

1 year ago

0.1.86

1 year ago

0.1.87

1 year ago

0.1.88

1 year ago

0.1.89

1 year ago

0.1.80

1 year ago

0.1.81

1 year ago

0.1.82

1 year ago

0.1.83

1 year ago

0.1.84

1 year ago

0.1.161

1 year ago

0.1.160

1 year ago

0.1.163

1 year ago

0.1.162

1 year ago

0.1.74

1 year ago

0.1.75

1 year ago

0.1.76

1 year ago

0.1.77

1 year ago

0.1.78

1 year ago

0.1.79

1 year ago

0.1.70

1 year ago

0.1.71

1 year ago

0.1.72

1 year ago

0.1.73

1 year ago

0.1.63

2 years ago

0.1.64

2 years ago

0.1.65

2 years ago

0.1.66

2 years ago

0.1.67

2 years ago

0.1.68

2 years ago

0.1.69

1 year ago

0.1.60

2 years ago

0.1.61

2 years ago

0.1.62

2 years ago

0.1.107

1 year ago

0.1.106

1 year ago

0.1.109

1 year ago

0.1.108

1 year ago

0.1.103

1 year ago

0.1.102

1 year ago

0.1.105

1 year ago

0.1.104

1 year ago

0.1.101

1 year ago

0.1.100

1 year ago

0.1.19

2 years ago

0.1.18

2 years ago

0.1.17

2 years ago

0.1.16

2 years ago

0.1.15

2 years ago

0.1.14

2 years ago

0.1.13

2 years ago

0.1.12

2 years ago

0.1.11

2 years ago

0.1.10

2 years ago

0.1.9

2 years ago

0.1.7

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

4 years ago

0.0.5

4 years ago

0.0.4

4 years ago

0.0.3

4 years ago

0.0.2

4 years ago

0.0.1

4 years ago

0.0.0

4 years ago