sellIntegration
account_tree

How front-end, back-end and DB connect

A browser, a Worker, and a database. Three small pieces, wired together with bindings. Once you see how they fit, the whole Integrations section clicks.

3Layers to wire
1Command to deploy
0Servers to manage
330+Cities serving it
insights

What are we wiring together?

Almost every web app is made of three jobs: showing things to a person, running logic and rules, and remembering data. On Cloudflare those three jobs map to three pieces — the browser (front-end), a Worker (your back-end API), and a database (D1, KV, R2, or Durable Objects). This guide gives you the mental map for how they connect.

Front-end means the part that runs inside the user's browser — the HTML, CSS and JavaScript they actually see and click. Back-end means code that runs on a server (here, a Worker) which the user never sees directly; it does the thinking and talks to the database. A database is just where data is stored so it survives after the request ends.

restaurant

Think of it like a restaurant

The browser is the dining room where the customer sits and orders. The Worker is the kitchen that receives orders and decides what to do. The database is the pantry where ingredients are stored. The customer never walks into the pantry — they ask the kitchen, and the kitchen fetches from the pantry.

schemaBig picture: how a full-stack app fits together

load page

fetch /api

query

data

JSON

User

Browser (front-end)

Static assets: HTML/CSS/JS

Worker (back-end API)

Database: D1/KV/R2

Notice two different arrows leaving the browser. One loads the static files that make up the page (the look). The other is an API call that asks the Worker for fresh data (the content). Keeping these separate is the heart of the whole model.

account_tree

The three layers, and who does what

Each layer has one clear job. Mixing the jobs up is the most common beginner mistake (for example, putting your database password in front-end code, where anyone can read it). Keep the responsibilities separated like this:

devices

Front-end — the browser

Renders the UI and reacts to clicks. It owns nothing secret. It asks the back-end for data using fetch() and shows the result.

dns

Back-end — the Worker

Receives requests, applies your rules and security checks, holds the secrets, and is the only layer allowed to touch the database.

database

Data — the database

Remembers data between requests. On Cloudflare this can be D1 (SQL), KV (key-value), R2 (files), or Durable Objects (live state).

lock

Why the split matters

Anything in the browser is public — users can open dev tools and read it. Secrets and database access must stay in the Worker, never the front-end.

schemaThree layers as a stack

fetch

read / write

JSON

Front-end: Browser (HTML/CSS/JS)

Back-end: Worker (API)

Data: D1 / KV / R2 / Durable Objects

bolt

One Worker can be both

On Cloudflare a single Worker often serves the static front-end files AND runs the back-end API. They live in one project but stay logically separate: static files on most paths, API logic on paths like /api/*.

swap_vert

Follow one request, end to end

The fastest way to understand a full-stack app is to trace a single click. Imagine a page with a "Load todos" button. Here is exactly what happens, step by step, when the user presses it.

schemaClick → fetch → Worker → DB → JSON back
DatabaseWorkerBrowserDatabaseWorkerBrowseruser clicks the buttonfetch GET /api/todosSELECT id, title FROM todosrowsJSON responserender the list on screen

What each step means

  • Click: the browser's JavaScript reacts to the button press and calls fetch().
  • fetch GET /api/todos: an HTTP request leaves the browser and is routed to your Worker.
  • SELECT ...: the Worker asks the database for the rows it needs.
  • rows: the database hands the matching data back to the Worker.
  • JSON response: the Worker turns that data into JSON (a simple text format) and replies.
  • render: the browser receives the JSON and updates the page — no full reload needed.
data_object

Why JSON?

JSON (JavaScript Object Notation) is a lightweight text format that both the Worker and the browser understand natively. The back-end sends data, not HTML, so the front-end is free to display it however it likes.

cable

Bindings: how a Worker reaches storage

A Worker doesn't connect to a database with a password and a connection string the way old servers did. Instead you declare a binding — a named, secure connection that Cloudflare wires up for you. In your code the binding appears as a property on the env object, like env.DB.

schemaOne Worker, many bindings

wires up

env.DB

env.CACHE

env.BUCKET

env.ROOM

wrangler.toml bindings

Worker (back-end)

D1 (SQL database)

KV (key-value store)

R2 (file storage)

Durable Objects (live state)

table_rows

D1 → env.DB

A SQL database for structured, related data like users and orders. Query it with SELECT / INSERT / UPDATE.

key

KV → env.CACHE

A key-value store for simple lookups read very often, like settings or cached pages. Super fast reads worldwide.

folder

R2 → env.BUCKET

Object storage for large files — images, videos, PDFs, backups. Like a folder in the cloud, with no egress fees.

hub

Durable Objects → env.ROOM

A single, consistent place to keep live state — perfect for chat rooms, game lobbies, or counters that many users share.

settings

The name is yours to choose

DB, CACHE, BUCKET and ROOM are just names you pick in wrangler.toml. Whatever you write as the binding name is exactly what appears on env in your code. Pick clear names and you'll never get lost.

construction

Wire it up yourself

Here is the smallest possible version of all three layers working together: a front-end that calls fetch, a Worker that returns JSON from D1, the wrangler.toml that binds them, and the SQL that creates the data. Copy these into one project and you have a real full-stack app.

  1. 1. The front-end (runs in the browser)

    Plain JavaScript that calls your API on click and paints the result. No framework needed to understand the idea.

    html
    <!-- public/index.html -->
    <button id="load">Load todos</button>
    <ul id="list"></ul>
    
    <script>
      async function loadTodos() {
        // Ask the Worker (back-end) for data
        const res = await fetch("/api/todos");
        const todos = await res.json();
        // Paint the result into the page
        document.querySelector("#list").innerHTML =
          todos.map((t) => `<li>${t.title}</li>`).join("");
      }
      document.querySelector("#load").addEventListener("click", loadTodos);
    </script>
  2. 2. The back-end Worker (returns JSON)

    It serves static files for normal paths, and for /api/todos it reads D1 via env.DB and replies with JSON. Notice the user's request never touches the database directly — only the Worker does.

    js
    // src/index.js
    export default {
      async fetch(request, env, ctx) {
        const url = new URL(request.url);
        if (url.pathname === "/api/todos") {
          // env.DB is the D1 binding from wrangler.toml
          const { results } = await env.DB
            .prepare("SELECT id, title FROM todos")
            .all();
          return Response.json(results);
        }
        // Everything else is served as a static asset
        return env.ASSETS.fetch(request);
      },
    };
  3. 3. The wrangler.toml (wires the binding)

    This config file tells Cloudflare your Worker's entry file, where the static files live, and which database to expose as env.DB.

    toml
    name = "my-fullstack-app"
    main = "src/index.js"
    compatibility_date = "2025-01-01"
    
    # Serve the front-end from ./public, exposed as env.ASSETS
    [assets]
    directory = "./public"
    binding = "ASSETS"
    
    # Bind a D1 database; reach it in code as env.DB
    [[d1_databases]]
    binding = "DB"
    database_name = "my-app-db"
    database_id = "<paste-your-database-id>"
  4. 4. The data layer (create the table)

    Run this SQL once to create the todos table and add a couple of rows, so your /api/todos endpoint has something to return.

    sql
    CREATE TABLE IF NOT EXISTS todos (
      id INTEGER PRIMARY KEY,
      title TEXT NOT NULL
    );
    
    INSERT INTO todos (title) VALUES
      ('Learn the full-stack model'),
      ('Wire a Worker to D1');
  5. 5. Run it, then deploy

    wrangler dev starts everything locally at http://localhost:8787. When it looks good, wrangler deploy puts all three layers live on Cloudflare's global network in one shot.

    bash
    npx wrangler d1 execute my-app-db --local --file=./schema.sql
    npx wrangler dev
    npx wrangler deploy
north_east

Next step: full CRUD

This guide is read-only (it only loads data). To also create, update and delete rows from the browser, follow the companion guide "Front-end ↔ Worker ↔ D1: full CRUD".

school

Key terms in one place

language

Front-end

Code that runs in the user's browser: the HTML, CSS and JavaScript they see and interact with.

dns

Back-end

Code that runs on a server — here a Worker — handling logic, security, and database access out of the user's sight.

http

API

Application Programming Interface — the set of URLs (like /api/todos) the front-end calls to ask the back-end for things.

sync

fetch()

The built-in browser function for sending an HTTP request to a URL and reading the response — how the front-end talks to the API.

cable

Binding

A named, secure connection from a Worker to another service (D1, KV, R2, Durable Objects), accessed via the env object.

tune

wrangler.toml

The config file where you name your Worker and declare its bindings. Wrangler reads it when you run or deploy.

data_object

JSON

A lightweight text format for data. The back-end usually replies in JSON so the front-end can easily read it.

storage

Database

Where data is stored so it survives between requests. On Cloudflare: D1, KV, R2, or Durable Objects.

tips_and_updates

Common pitfalls & tips

vpn_key

Never put secrets in the front-end

API keys, database credentials and tokens belong in the Worker only. Anything shipped to the browser can be read by anyone using dev tools. Keep secrets server-side with Wrangler secrets (wrangler secret put).

Things beginners trip over

  • CORS errors: if your front-end and API are on the same Worker, you usually avoid them entirely — serving both from one origin is the simplest setup.
  • Forgetting await: fetch() and D1 queries are asynchronous; always await them or you'll get a Promise instead of data.
  • Returning HTML when you meant JSON: use Response.json(data) so the front-end can call res.json() cleanly.
  • Binding name mismatch: the name in wrangler.toml must match what you read on env (env.DB needs binding = "DB").
  • Editing local vs remote DB: --local touches your machine's copy; drop it (or use --remote) to change the deployed database.
savings

It's cheap to start

Workers' free plan gives 100,000 requests a day and D1 includes a generous free tier — plenty to build and learn a full-stack app before paying anything. Check the docs for current limits before you launch.