Building a modern full-stack application involves juggling many moving parts: client-side rendering, server logic, database management, and deployment. This guide will walk you through creating a robust boilerplate using a powerful stack: React Router for routing, Cloudflare for infrastructure (including the D1 database), and Drizzle as our ORM.
By the end of this tutorial, you'll have a solid foundation for your next project, complete with a supercharged development environment featuring Biome, Vitest, and Husky.
Prerequisites
Before we begin, make sure you have the following installed:
- Node.js (v18 or later)
- npm
- A Cloudflare account
Step 1: Project Initialization
We'll start by creating a new project using the official React Router template for Cloudflare D1. This gives us a great starting point with all the necessary configurations.
Open your terminal and run the following command:
npx create-react-router@latest react-router-cloudflare-boilerplate --template remix-run/react-router-templates/cloudflare-d1
This command scaffolds a new project in a directory named react-router-cloudflare-boilerplate
.
Step 2: Setting Up Cloudflare D1
Next, let's set up our database on Cloudflare. We'll use D1, Cloudflare's native serverless SQL database.
Create the D1 Database
Run this command to create a new D1 database. Feel free to replace d1-app-test-db
with your preferred name.
npx wrangler d1 create d1-app-test-db
Upon successful creation, you'll get a confirmation message with the binding information.
✅ Successfully created DB 'd1-app-test-db' in region APAC
Created your new D1 database.
{
"d1_databases": [
{
"binding": "DB",
"database_name": "d1-app-test-db",
"database_id": "***************"
}
]
}
Configure Wrangler
Now, open wrangler.jsonc
in your project and add the d1_databases
block you received from the previous command. This tells Wrangler how to connect to your D1 database.
{
// ... other configurations
"d1_databases": [
{
"binding": "DB",
"database_name": "d1-app-test-db",
"database_id": "***************",
"migrations_dir": "drizzle"
}
]
}
Set Up Environment Variables
For Drizzle to communicate with your D1 database from your local machine (for production migrations), you need to set up some environment variables. Create a .env
file in your project root.
CLOUDFLARE_D1_ID="<your-database-id>"
CLOUDFLARE_ACCOUNT_ID="<your-account-id>"
CLOUDFLARE_TOKEN="<your-api-token>"
-
CLOUDFLARE_D1_ID
: Thedatabase_id
from the previous step. -
CLOUDFLARE_ACCOUNT_ID
: Find this on your Cloudflare dashboard under Workers & Pages. -
CLOUDFLARE_TOKEN
: Create a new API token from your Cloudflare dashboard > Manage Account > API Tokens. Use the "Create Custom Token" option with "Account > D1 > Edit" permissions.
Configure Drizzle
Finally, let's tell Drizzle how to connect to our D1 database for production migrations. Update drizzle.config.ts
with your environment variables.
import type { Config } from "drizzle-kit";
export default {
out: "./drizzle",
schema: "./database/schema.ts",
dialect: "sqlite",
driver: "d1-http",
dbCredentials: {
databaseId: process.env.CLOUDFLARE_D1_ID!,
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
token: process.env.CLOUDFLARE_TOKEN!,
},
} satisfies Config;
Step 3: Database Migration and Deployment
With our configuration in place, we can now migrate our schema and deploy the application.
Run Production Migration
This command applies your schema (database/schema.ts
) to the production D1 database.
npm run db:migrate-production
Deploy to Production
Deploy your application to Cloudflare Pages.
npm run deploy
Once deployed, you can visit the URL and interact with your live application. You can verify the data in your D1 database via the Cloudflare dashboard.
Step 4: Setting Up the Local Development Environment
For local development, Wrangler creates a local SQLite file to simulate D1.
Run Local Migration
Apply the schema to your local database.
npm run db:migrate
This creates a .sqlite
file inside the .wrangler/
directory, allowing you to develop offline.
Start the Dev Server
Launch the development server with Hot Module Replacement (HMR).
npm run dev
Step 5: Enhancing the Development Environment
Let's add some modern tooling to improve code quality and development experience.
Biome for Linting and Formatting
Install Biome, an all-in-one tool for linting, formatting, and more.
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
You can customize the generated biome.json
to fit your needs.
Vitest for Testing
Install Vitest, a fast and modern testing framework.
npm i -D vitest
To enable global imports for tests, modify vite.config.ts
and tsconfig.cloudflare.json
.
// ...
export default defineConfig(({ mode }) => ({
// ...
test: {
globals: true,
setupFiles: ['./test/setup.ts'],
},
}));
{
"compilerOptions": {
// ...
"types": [
// ...
"vitest/globals"
],
}
}
Create an empty test/setup.ts
file, which you can use for test-wide setup later.
File-based Routing
Install @react-router/fs-routes
to enable conventional file-based routing.
npm i @react-router/fs-routes
Update app/routes.ts
to use it:
import type { RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
const routes: RouteConfig = flatRoutes();
export default routes;
shadcn/ui for Components
Initialize shadcn/ui to easily add beautiful and accessible components.
npx shadcn-ui@latest init
This command will guide you through the setup process. It may ask you to configure path aliases in tsconfig.json
.
Husky for Pre-commit Hooks
Install Husky to run scripts automatically before you commit.
npm i -D husky
npx husky init
Now, edit .husky/pre-commit
to automatically lint and format your staged files.
npx biome check --staged --fix --no-errors-on-unmatched
git update-index --again
This ensures that your code is always clean before it gets committed.
Conclusion
Congratulations! You've successfully built and deployed a full-stack application with a powerful and modern tech stack. This boilerplate provides a solid foundation with a first-class development experience, allowing you to focus on building features instead of wrestling with configuration.
You can find the source code for this project here: [Link to your GitHub repository]
Happy coding!
Top comments (0)