react-edge
v0.1.70
Published
> A revolutionary React framework for building blazing-fast applications on the edge, powered by Cloudflare Workers
Downloads
1,358
Readme
React Edge Framework 🚀
A revolutionary React framework for building blazing-fast applications on the edge, powered by Cloudflare Workers
🚀 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 (request: 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: {
ttl: 300, // Cache for 5 minutes
tags: [`user:${id}`]
}
});
}
}
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: {
ttl: 3600, // 1 hour
tags: [`category:${category}`, 'products']
}
});
}
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
RPC Organization
- Keep related functionality in dedicated RPC classes
- Use private methods for internal logic
- Leverage caching for frequently accessed data
State Management
- Use
useUrlState
for shareable state - Use
useStorageState
for persistent preferences - Use
useDistinct
to optimize updates
- Use
Performance
- Implement appropriate cache strategies
- Use Link component for preloading
- Leverage edge caching when possible
📝 License
MIT © Felipe Rohde
👨💻 Author
Felipe Rohde
- Twitter: @felipe_rohde
- Github: @feliperohdee
- Email: [email protected]