DEV Community

Cover image for From Zero to Deployed: A Step-by-Step Guide to Building a Full-Stack App with React Router and Cloudflare
Atman
Atman

Posted on

From Zero to Deployed: A Step-by-Step Guide to Building a Full-Stack App with React Router and Cloudflare

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:

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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": "***************"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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>"
Enter fullscreen mode Exit fullscreen mode
  • CLOUDFLARE_D1_ID: The database_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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Deploy to Production

Deploy your application to Cloudflare Pages.

npm run deploy
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'],    
  },
}));
Enter fullscreen mode Exit fullscreen mode
{
  "compilerOptions": {
    // ...
    "types": [
      // ...
      "vitest/globals"
    ],
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

shadcn/ui for Components

Initialize shadcn/ui to easily add beautiful and accessible components.

npx shadcn-ui@latest init
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)