Build a Staff Directory Application·续
To display a list of employees, invoke the findAllEmployees function from your db.ts file and call that within the routes/index.tsx file. The createRoute() function present in the file serves as a helper function for defining routes that handle different HTTP methods like GET, POST, PUT, or DELETE.
import { css } from 'hono/css'
import { createRoute } from 'honox/factory'
import Counter from '../islands/counter'
const className = css`
font-family: sans-serif;
`
export default createRoute((c) => {
const name = c.req.query('name') ?? 'Hono'
return c.render(
<div class={className}>
<h1>Hello, {name}!</h1>
<Counter />
</div>,
{ title: name }
)
})
The existing code within the file includes a placeholder that uses the Counter component. You should replace this section with the following code block:
import { createRoute } from 'honox/factory'
import type { FC } from 'hono/jsx'
import type { Employee } from '../db'
import { findAllEmployees, findAllDepartments, findAllLocations } from '../db'
const EmployeeCard: FC<{ employee: Employee }> = ({ employee }) => {
const { employee_id, name, image_url, department_name, location_name } = employee;
return (
<div className="max-w-sm bg-white border border-gray-200 rounded-lg shadow-md">
<a href={`/employee/${employee_id}`}>
<img className="bg-indigo-600 p-4 rounded-t-lg" src={image_url} alt={name} />
//...
</a>
</div>
);
};
export const GET = createRoute(async (c) => {
const employees = await findAllEmployees(c.env.DB)
const locations = await findAllLocations(c.env.DB)
const departments = await findAllDepartments(c.env.DB)
return c.render(
<section className="flex-grow">
<h1 className="mb-4 text-3xl font-extrabold text-gray-900 dark:text-white md:text-5xl lg:text-6xl mt-12">
<span className="text-transparent bg-clip-text bg-gradient-to-r to-blue-600 from-sky-400">{`Directory `}</span>
</h1>
//...
</section>
<section className="flex flex-wrap -mx-4">
{employees.map((employee) => (
<div className="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
<EmployeeCard employee={employee} />
</div>
))}
</section>
</section>
)
})
The code snippet demonstrates how to import the findAllEmployees, findAllLocations, and findAllDepartments functions from the db.ts file, and how to use the binding c.env.DB to invoke these functions. With these, you can retrieve and display the fetched data on the page.
Add an employee
Use the export POST route to create a new employee through the /admin page:
import { createRoute } from "honox/factory";
import type { Employee } from "../../db";
import { getFormDataValue, getFormDataNumber } from "../../utils/formData";
import { createEmployee } from "../../db";
export const POST = createRoute(async (c) => {
try {
const formData = await c.req.formData();
const imageFile = formData.get("image_file");
let imageUrl = "";
// TODO: process image url with R2
const employeeData: Employee = {
employee_id: getFormDataValue(formData, "employee_id"),
name: getFormDataValue(formData, "name"),
position: getFormDataValue(formData, "position"),
image_url: imageUrl,
join_date: getFormDataValue(formData, "join_date"),
department_id: getFormDataNumber(formData, "department_id"),
location_id: getFormDataNumber(formData, "location_id"),
location_name: "",
department_name: "",
};
await createEmployee(c.env.DB, employeeData);
return c.redirect("/", 303);
} catch (error) {
return new Response("Error processing your request", { status: 500 });
}
});
Store images in R2
During the process of creating a new employee, the image uploaded can be stored in an R2 bucket prior to being added to the database.
To store an image in an R2 bucket:
- Create an R2 bucket.
- Upload the image to this bucket.
- Obtain a public URL for the image from the bucket. This URL is then saved in your database, linking to the image stored in the R2 bucket.
Use the wrangler r2 bucket create command to create a bucket:
wrangler r2 bucket create employee-avatars
Once the bucket is created, add the R2 bucket binding to your Wrangler file:
{
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "employee-avatars"
}
]
}
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "employee-avatars"
Pass the R2 binding to the global.d.ts file:
declare module "hono" {
interface Env {
Variables: {};
Bindings: {
DB: D1Database;
MY_BUCKET: R2Bucket;
};
}
}
To store the uploaded image in the R2 bucket, you can use the put() method provided by R2. This method allows you to upload the image file to your bucket:
if (imageFile instanceof File) {
const key = `${new Date().getTime()}-${imageFile.name}`;
const fileBuffer = await imageFile.arrayBuffer();
await c.env.MY_BUCKET.put(key, fileBuffer, {
httpMetadata: {
contentType: imageFile.type || "application/octet-stream",
},
});
console.log(`File uploaded successfully: ${key}`);
imageUrl = `https://pub-8d936184779047cc96686a631f318fce.r2.dev/${key}`;
}
Refer to GitHub ↗︎ for the full codebase.
7. Deploy your HonoX application
With your application ready for deployment, you can use Wrangler to build and deploy your project to the Cloudflare Network. Ensure you are logged in to your Cloudflare account by running the wrangler whoami command. If you are not logged in, Wrangler will prompt you to login by creating an API key that you can use to make authenticated requests automatically from your computer.
After successful login, confirm that your Wrangler file is configured similarly to the code block below:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "staff-directory",
// Set this to today's date
"compatibility_date": "2026-10-27",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "employee-avatars"
}
],
"d1_databases": [
{
"binding": "DB",
"database_name": "staff-directory",
"database_id": "f495af5f-dd71-4554-9974-97bdda7137b3"
}
]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "staff-directory"
# Set this to today's date
compatibility_date = "2026-10-27"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "employee-avatars"
[[d1_databases]]
binding = "DB"
database_name = "staff-directory"
database_id = "f495af5f-dd71-4554-9974-97bdda7137b3"
Run wrangler deploy to deploy your project to Cloudflare. After deployment you can test your application is working by accessing the deployed URL provided for you. Your browser should display your application with the base frontend you created. If you do not have any data populated in your database, go to the /admin page to add a new employee, and this should return a new employee in your home page.
Conclusion
In this tutorial, you built a staff directory application where users can view all employees within an organization. Refer to the Staff directory repository ↗︎ for the full source code.

Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/d1/tutorials/build-a-staff-directory-app/#page","headline":"Build a Staff Directory Application","description":"Build a staff directory using D1. Users access employee info; admins add new employees within the app.","url":"https://developers.cloudflare.com/d1/tutorials/build-a-staff-directory-app/","inLanguage":"en","image":"https://developers.cloudflare.com/d1/tutorials/build-a-staff-directory-app/og.png?v=d02fe3347ccbc31c","dateModified":"2026-02-02","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["Hono","TypeScript","SQL"]}
description: This tutorial shows how to create an API that allows you to securely run queries against a D1 database. The API can be used to customize access controls and/or limit what tables can be queried. title: Build an API to access D1 using a proxy Worker image: https://developers.cloudflare.com/d1/tutorials/build-an-api-to-access-d1/og.png?v=f46d53b0420bb46b
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/d1/llms.txt
Use this file to discover all available pages before exploring further.
