Skip to main content

RedwoodJS on Cloudflare Example

This example demonstrates deploying a RedwoodJS full-stack application to Cloudflare with D1 database integration.

Features

  • RedwoodJS: Full-stack React framework
  • D1 Database: SQLite database at the edge
  • Drizzle Migrations: Type-safe database schema
  • Local Development: Integrated dev server

Project Setup

1
Install Dependencies
2
npm install alchemy
npm install @redwoodjs/core
3
Create alchemy.run.ts
4
Create infrastructure with D1 database:
5
import alchemy from "alchemy";
import { D1Database, Redwood } from "alchemy/cloudflare";

const app = await alchemy("cloudflare-redwood");

const database = await D1Database("database", {
  name: `${app.name}-${app.stage}-db`,
  migrationsDir: "drizzle",
  adopt: true,
});

export const website = await Redwood("website", {
  name: `${app.name}-${app.stage}-website`,
  adopt: true,
  bindings: {
    DB: database,
  },
  dev: {
    command: "vite dev --port 5004",
  },
});

console.log({
  url: website.url,
});

await app.finalize();
6
Create Database Schema
7
Create drizzle/schema.ts:
8
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

export const posts = sqliteTable("posts", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  title: text("title").notNull(),
  body: text("body").notNull(),
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
});
9
Create Migration
10
Create drizzle/0000_init.sql:
11
CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  created_at INTEGER NOT NULL
);
12
Use Database in Redwood
13
Create an API service api/src/services/posts/posts.ts:
14
import { db } from 'src/lib/db';
import { posts } from '../../../drizzle/schema';

export const posts = async () => {
  return await db.select().from(posts);
};

export const post = async ({ id }: { id: number }) => {
  return await db.select().from(posts).where(eq(posts.id, id)).get();
};

export const createPost = async (input: {
  title: string;
  body: string;
}) => {
  return await db.insert(posts).values({
    ...input,
    createdAt: new Date(),
  }).returning().get();
};
15
Configure Database Connection
16
Create api/src/lib/db.ts:
17
import { drizzle } from 'drizzle-orm/d1';
import { context } from '@redwoodjs/graphql-server';

export const db = drizzle(context.env.DB);
18
Create GraphQL Schema
19
Create api/src/graphql/posts.sdl.ts:
20
export const schema = gql`
  type Post {
    id: Int!
    title: String!
    body: String!
    createdAt: DateTime!
  }

  type Query {
    posts: [Post!]! @skipAuth
    post(id: Int!): Post @skipAuth
  }

  type Mutation {
    createPost(title: String!, body: String!): Post! @skipAuth
  }
`;
21
Deploy
22
Deploy to Cloudflare:
23
npm exec tsx alchemy.run.ts
24
For local development:
25
npm exec tsx alchemy.run.ts --local

Key Features Explained

D1 Database with Migrations

D1 supports SQL migrations from a directory:
const database = await D1Database("database", {
  name: `${app.name}-${app.stage}-db`,
  migrationsDir: "drizzle",
  adopt: true,
});

Database Bindings

The database is bound to your Redwood application:
bindings: {
  DB: database,
}

Local Development

Integrated with Redwood’s dev server:
dev: {
  command: "vite dev --port 5004",
}

Database Operations

Query Data

import { db } from 'src/lib/db';
import { posts } from '../drizzle/schema';

const allPosts = await db.select().from(posts);

Insert Data

const newPost = await db.insert(posts).values({
  title: "Hello World",
  body: "My first post",
  createdAt: new Date(),
}).returning().get();

Update Data

import { eq } from 'drizzle-orm';

await db.update(posts)
  .set({ title: "Updated Title" })
  .where(eq(posts.id, 1));

Source Code

View the complete source code: examples/cloudflare-redwood

Build docs developers (and LLMs) love