SolidJS Start
Full-stack SolidJS framework with Web Standards support - no adapter needed!
SolidJS Start
SolidJS Start is a full-stack SolidJS framework with built-in Web Standards support. Since SolidJS Start provides event.request
as a Web Standard Request
object, pushduck handlers work directly without any adapters!
Web Standards Native: SolidJS Start's API handlers receive event.request
as a Web Standard Request
object, making pushduck integration seamless with zero overhead.
Quick Setup
Install dependencies
npm install pushduck
yarn add pushduck
pnpm add pushduck
bun add pushduck
Configure upload router
import { createUploadConfig } from 'pushduck/server';
const { s3, createS3Router } = createUploadConfig()
.provider("cloudflareR2",{
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: 'auto',
endpoint: process.env.AWS_ENDPOINT_URL!,
bucket: process.env.S3_BUCKET_NAME!,
accountId: process.env.R2_ACCOUNT_ID!,
})
.build();
export const uploadRouter = createS3Router({
imageUpload: s3.image().max("5MB"),
documentUpload: s3.file().max("10MB")
});
export type AppUploadRouter = typeof uploadRouter;
Create API route
import { APIEvent } from '@solidjs/start/server';
import { uploadRouter } from '~/lib/upload';
export async function GET(event: APIEvent) {
return uploadRouter.handlers(event.request);
}
export async function POST(event: APIEvent) {
return uploadRouter.handlers(event.request);
}
Basic Integration
API Route Handler
import { APIEvent } from '@solidjs/start/server';
import { uploadRouter } from '~/lib/upload';
// Method 1: Individual handlers
export async function GET(event: APIEvent) {
return uploadRouter.handlers.GET(event.request);
}
export async function POST(event: APIEvent) {
return uploadRouter.handlers.POST(event.request);
}
// Method 2: Combined handler (alternative approach)
// export async function handler(event: APIEvent) {
// return uploadRouter.handlers(event.request);
// }
With CORS Middleware
import { APIEvent } from '@solidjs/start/server';
import { uploadRouter } from '~/lib/upload';
function addCORSHeaders(response: Response) {
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
export async function GET(event: APIEvent) {
const response = await uploadRouter.handlers.GET(event.request);
return addCORSHeaders(response);
}
export async function POST(event: APIEvent) {
const response = await uploadRouter.handlers.POST(event.request);
return addCORSHeaders(response);
}
export async function OPTIONS() {
return new Response(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
});
}
Advanced Configuration
Authentication with SolidJS Start
import { createUploadConfig } from 'pushduck/server';
const { s3, createS3Router } = createUploadConfig()
.provider("cloudflareR2",{
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: 'auto',
endpoint: process.env.AWS_ENDPOINT_URL!,
bucket: process.env.S3_BUCKET_NAME!,
accountId: process.env.R2_ACCOUNT_ID!,
})
.paths({
prefix: 'uploads',
generateKey: (file, metadata) => {
return `${metadata.userId}/${Date.now()}/${file.name}`;
}
})
.build();
export const uploadRouter = createS3Router({
// Private uploads with authentication
privateUpload: s3
.image()
.max("5MB")
.middleware(async ({ req }) => {
const authHeader = req.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new Error('Authorization required');
}
const token = authHeader.substring(7);
try {
const user = await verifyJWT(token);
return {
userId: user.id,
userRole: user.role
};
} catch (error) {
throw new Error('Invalid token');
}
}),
// Public uploads (no auth)
publicUpload: s3
.image()
.max("2MB")
// No middleware = public access
});
async function verifyJWT(token: string) {
// Your JWT verification logic here
return { id: 'user-123', role: 'user' };
}
export type AppUploadRouter = typeof uploadRouter;
Protected API Routes
import { APIEvent } from '@solidjs/start/server';
import { uploadRouter } from '~/lib/upload';
async function requireAuth(request: Request) {
const authHeader = request.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new Response(JSON.stringify({ error: 'Authorization required' }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
const token = authHeader.substring(7);
// Verify token logic here
return { userId: 'user-123' };
}
export async function GET(event: APIEvent) {
await requireAuth(event.request);
return uploadRouter.handlers.GET(event.request);
}
export async function POST(event: APIEvent) {
await requireAuth(event.request);
return uploadRouter.handlers.POST(event.request);
}
Client-Side Integration
Upload Component
import { createSignal } from 'solid-js';
import { createUploadClient } from 'pushduck/client';
import type { AppUploadRouter } from '~/lib/upload';
const uploadClient = createUploadClient<AppUploadRouter>({
baseUrl: import.meta.env.DEV
? 'http://localhost:3000'
: 'https://your-domain.com'
});
export function UploadDemo() {
const [uploading, setUploading] = createSignal(false);
const [results, setResults] = createSignal<any[]>([]);
const handleUpload = async (files: File[]) => {
if (!files.length) return;
setUploading(true);
try {
const uploadResults = await uploadClient.upload('imageUpload', {
files,
metadata: { userId: 'demo-user' }
});
setResults(uploadResults);
console.log('Upload successful:', uploadResults);
} catch (error) {
console.error('Upload failed:', error);
} finally {
setUploading(false);
}
};
return (
<div class="space-y-4">
<div>
<input
type="file"
multiple
accept="image/*"
onChange={(e) => {
const files = e.target.files;
if (files) {
handleUpload(Array.from(files));
}
}}
disabled={uploading()}
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
/>
</div>
{uploading() && (
<div class="text-blue-600">Uploading...</div>
)}
{results().length > 0 && (
<div class="space-y-2">
<h3 class="font-medium">Upload Results:</h3>
{results().map((result, index) => (
<div key={index} class="p-2 bg-gray-50 rounded">
<div class="text-sm">
<strong>File:</strong> {result.name}
</div>
<div class="text-sm">
<strong>URL:</strong>
<a href={result.url} target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:underline ml-1">
{result.url}
</a>
</div>
</div>
))}
</div>
)}
</div>
);
}
Page Integration
import { Title } from '@solidjs/meta';
import { UploadDemo } from '~/components/UploadDemo';
export default function UploadPage() {
return (
<>
<Title>File Upload Demo</Title>
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-8">File Upload Demo</h1>
<div class="max-w-md mx-auto">
<UploadDemo />
</div>
</div>
</>
);
}
Full Application Example
Project Structure
src/
├── components/
│ └── UploadDemo.tsx
├── lib/
│ └── upload.ts
├── routes/
│ ├── api/
│ │ └── upload/
│ │ └── [...path].ts
│ ├── upload.tsx
│ └── (home).tsx
├── app.tsx
└── entry-server.tsx
Root Layout
import { Router } from '@solidjs/router';
import { FileRoutes } from '@solidjs/start/router';
import { Suspense } from 'solid-js';
import './app.css';
export default function App() {
return (
<Router
root={props => (
<div class="min-h-screen bg-gray-50">
<nav class="bg-white shadow">
<div class="container mx-auto px-4 py-4">
<h1 class="text-xl font-semibold">Upload Demo</h1>
</div>
</nav>
<main>
<Suspense>{props.children}</Suspense>
</main>
</div>
)}
>
<FileRoutes />
</Router>
);
}
Home Page
import { A } from '@solidjs/router';
import { Title } from '@solidjs/meta';
export default function Home() {
return (
<>
<Title>SolidJS Start + Pushduck</Title>
<div class="container mx-auto px-4 py-8">
<div class="text-center">
<h1 class="text-4xl font-bold text-gray-900 mb-4">
SolidJS Start + Pushduck
</h1>
<p class="text-xl text-gray-600 mb-8">
High-performance file uploads with SolidJS
</p>
<A
href="/upload"
class="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
Try Upload Demo
</A>
</div>
</div>
</>
);
}
Performance Benefits
🚀 Zero Overhead
Direct Web Standards integration
No adapter layer means zero performance overhead - pushduck handlers run directly in SolidJS Start.
⚡ SolidJS Performance
Fastest reactive framework
SolidJS provides exceptional performance with fine-grained reactivity and no virtual DOM.
📦 Full-Stack TypeScript
End-to-end type safety
Complete type safety from server to client with shared types.
🔧 File-Based Routing
Intuitive API structure
Clean, organized API routes with SolidJS Start's file-based routing system.
Deployment
Vercel Deployment
import { defineConfig } from '@solidjs/start/config';
export default defineConfig({
server: {
preset: 'vercel'
}
});
Netlify Deployment
import { defineConfig } from '@solidjs/start/config';
export default defineConfig({
server: {
preset: 'netlify'
}
});
Node.js Deployment
import { defineConfig } from '@solidjs/start/config';
export default defineConfig({
server: {
preset: 'node-server'
}
});
Docker Deployment
FROM node:18-alpine as base
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Build the app
RUN npm run build
# Expose port
EXPOSE 3000
# Start the app
CMD ["npm", "start"]
SolidJS + Pushduck: The perfect combination of SolidJS's exceptional performance and pushduck's universal design for lightning-fast file upload experiences.