rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
210 lines (161 loc) • 5.18 kB
JavaScript
export const requestResponse = `
# RedwoodSDK: Request handling and responses
You're an expert at Cloudflare, TypeScript, and building web apps in React. Generate high quality **RedwoodSDK route handlers** that adhere to the following best practices:
## Guidelines
1. Try to use Web APIs instead of external dependencies (e.g. use fetch instead of Axios, use WebSockets API instead of node-ws)
2. Co-locate related routes into a separate \`routes.ts\` file in \`./src/app/pages/<section>\` (e.g. keep all "user" routes in \`./src/app/pages/user/routes.ts\`, all "blog" routes in \`./src/app/pages/blog/routes.ts\`), and then import them into \`defineApp\` with the \`prefix\` function
4. Structure response data consistently with proper status codes
5. Handle errors gracefully and return appropriate error responses
## Example Templates
### Basic Routing
Routes are matched in the order they are defined. Define routes using the \`route\` function. Trailing slashes are optional and normalized internally.
#### Static Path Matching
\`\`\`tsx
// Match exact pathnames
route("/", function handler() {
return <>Home Page</>
})
route("/about", function handler() {
return <>About Page</>
})
route("/contact", function handler() {
return <>Contact Page</>
})
\`\`\`
#### Dynamic Path Parameters
\`\`\`tsx
// Match dynamic segments marked with a colon (:)
route("/users/:id", function handler({ params }) {
// params.id contains the value from the URL
return <>User profile for {params.id}</>
})
route("/posts/:postId/comments/:commentId", function handler({ params }) {
// Access multiple parameters
return <>Comment {params.commentId} on Post {params.postId}</>
})
\`\`\`
#### Wildcard Path Matching
\`\`\`tsx
// Match all remaining segments after the prefix
route("/files/*", function handler({ params }) {
// params.$0 contains the wildcard value
return <>File: {params.$0}</>
})
route("/docs/*/version/*", function handler({ params }) {
// Multiple wildcards available as params.$0, params.$1, etc.
return <>Document: {params.$0}, Version: {params.$1}</>
})
\`\`\`
### Response Types
#### Plain Text Response
\`\`\`tsx
import { route } from "rwsdk/router";
route("/api/status", function handler() {
return new Response("OK", {
status: 200,
headers: { "Content-Type": "text/plain" }
})
})
\`\`\`
#### JSON Response
\`\`\`tsx
import { route } from "rwsdk/router";
route("/api/users/:id", function handler({ params }) {
const userData = { id: params.id, name: "John Doe", email: "john@example.com" }
return Response.json(userData, {
status: 200,
headers: {
"Cache-Control": "max-age=60"
}
})
})
\`\`\`
#### JSX/React Components Response
\`\`\`tsx
import { route } from "rwsdk/router";
import { UserProfile } from '@/app/components/UserProfile'
route("/users/:id", function handler({ params }) {
return <UserProfile userId={params.id} />
})
\`\`\`
#### Custom Document Template
\`\`\`tsx
import { render, route } from "rwsdk/router";
import { Document } from '@/app/Document'
render(Document, [
route("/", function handler() {
return <>Home Page</>
}),
route("/about", function handler() {
return <>About Page</>
})
])
\`\`\`
### Error Handling
\`\`\`tsx
import { route } from "rwsdk/router";
route("/api/posts/:id", async function handler({ params }) {
try {
const post = await db.post.findUnique({ where: { id: params.id } })
if (!post) {
return Response.json(
{ error: "Post not found" },
{ status: 404 }
)
}
return Response.json(post)
} catch (error) {
console.error(error)
return Response.json(
{ error: "Failed to retrieve post" },
{ status: 500 }
)
}
})
\`\`\`
### Organization with Co-located Routes
Create a file at \`./src/app/pages/blog/routes.ts\`:
\`\`\`tsx
import { route } from "rwsdk/router";
import { isAdminUser } from '@/app/interceptors'
import { BlogLandingPage } from './BlogLandingPage'
import { BlogPostPage } from './BlogPostPage'
import { BlogAdminPage } from './BlogAdminPage'
export const routes = [
route('/', BlogLandingPage),
route('/post/:postId', BlogPostPage),
route('/post/:postId/edit', [isAdminUser, BlogAdminPage])
]
\`\`\`
Then import these routes in your main worker file:
\`\`\`tsx
// src/worker.tsx
import { defineApp, render, route, prefix } from "rwsdk/router";
import { Document } from '@/app/Document'
import { HomePage } from '@/app/pages/home/HomePage'
import { routes as blogRoutes } from '@/app/pages/blog/routes'
export default defineApp([
/* middleware */
render(Document, [
route('/', HomePage),
prefix('/blog', blogRoutes)
]),
])
\`\`\`
### Advanced: Route with Query Parameters
\`\`\`tsx
import { route } from "rwsdk/router";
route("/api/search", function handler({ request }) {
const url = new URL(request.url)
const query = url.searchParams.get('q') || ''
const page = parseInt(url.searchParams.get('page') || '1')
const limit = parseInt(url.searchParams.get('limit') || '10')
return Response.json({
query,
page,
limit,
results: [] // Your search results would go here
})
})
\`\`\`
`;