← Cloudflare D1 / d1 / tutorials
Создание API для комментариев
В этом руководстве вы будете использовать D1 и Hono ↗ чтобы создать JSON API, который сохраняет и получает комментарии для блога. Вы создадите базу данных D1, определите схему и настроите GET и POST конечные точки, которые читают из базы данных и записывают в неё.
Предварительные требования
- Зарегистрируйтесь для получения Аккаунт Cloudflare ↗.
- Установка
Node.js↗.
менеджер версий Node.js
Используйте менеджер версий Node, например Volta ↗ или nvm ↗ чтобы избежать проблем с правами доступа и переключать версии Node.js. Wrangler, о котором пойдёт речь далее в этом руководстве, требует версию Node 16.17.0 или более поздней версии.
1. Создайте новый проект Worker
-
Создайте новый проект с именем
d1-comments-apiвыполнив:npm create cloudflare@latest -- d1-comments-apiДля настройки выберите следующие параметры:
- Для С чего вы хотите начать?, выберите
Hello World example. - Для Какой шаблон вы хотите использовать?, выберите
Worker only. - Для Какой язык вы хотите использовать?, выберите
TypeScript. - Для Хотите использовать git для контроля версий?, выберите
Yes. - Для Хотите развернуть приложение?, выберите
No(мы внесём некоторые изменения перед развёртыванием).
- Для С чего вы хотите начать?, выберите
-
Перейдите в каталог проекта:
cd d1-comments-api
2. Установите Hono
Установка Hono ↗, легковесный веб-фреймворк для создания API на Workers:
npm i hono3. Создайте базу данных
-
Создайте новую базу данных D1 с помощью Wrangler:
npx wrangler@latest d1 create d1-comments-api -
При появлении запроса
Would you like Wrangler to add it on your behalf?, выберитеYes. Это автоматически добавляетDBпривязку в конфигурационный файл Wrangler.Убедитесь, что конфигурационный файл Wrangler содержит
d1_databasesпривязку и полную конфигурацию проекта:{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "d1-comments-api", "main": "src/index.ts", // Set this to today's date "compatibility_date": "2026-08-28", "d1_databases": [ { "binding": "DB", "database_name": "d1-comments-api", "database_id": "<YOUR_DATABASE_ID>" } ] }name = "d1-comments-api" main = "src/index.ts" # Set this to today's date compatibility_date = "2026-08-28" [[d1_databases]] binding = "DB" # available in your Worker on env.DB database_name = "d1-comments-api" database_id = "<YOUR_DATABASE_ID>"Замените
<YOUR_DATABASE_ID>с идентификатором, выведеннымwrangler d1 createкоманда.
Bindings позволяют вашим Workers обращаться к ресурсам, таким как базы данных D1, KV namespaces и бакеты R2, используя имя переменной в коде. Ваша база данных D1 доступна в Worker на env.DB.
4. Создайте схему и заполните базу данных начальными данными
-
Создайте
schemas/schema.sqlфайл со следующим содержимым:DROP TABLE IF EXISTS comments; CREATE TABLE IF NOT EXISTS comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, author TEXT NOT NULL, body TEXT NOT NULL, post_slug TEXT NOT NULL ); CREATE INDEX idx_comments_post_slug ON comments (post_slug); -- Optionally, uncomment the below query to insert seed data -- INSERT INTO comments (author, body, post_slug) VALUES ('Kristian', 'Great post!', 'hello-world'); -
Сначала примените схему к локальной базе данных:
npx wrangler d1 execute d1-comments-api --local --file schemas/schema.sql -
Убедитесь, что таблица создана локально:
npx wrangler d1 execute d1-comments-api --local --command "SELECT name FROM sqlite_schema WHERE type = 'table'"┌──────────┐ │ name │ ├──────────┤ │ comments │ └──────────┘ -
Когда схема вас устроит, примените её к удалённой (продакшен) базе данных:
npx wrangler d1 execute d1-comments-api --remote --file schemas/schema.sql
5. Инициализируйте приложение Hono
Замените содержимое src/index.ts с помощью следующего кода. Это настраивает приложение Hono с типизированным Bindings интерфейс, чтобы env.DB корректно типизируется как D1Database:
import { Hono } from "hono";
const app = new Hono();
app.get("/api/posts/:slug/comments", async (c) => {
// Do something and return an HTTP response
// Optionally, do something with c.req.param("slug")
});
app.post("/api/posts/:slug/comments", async (c) => {
// Do something and return an HTTP response
// Optionally, do something with c.req.param("slug")
});
export default app;import { Hono } from "hono";
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get("/api/posts/:slug/comments", async (c) => {
// Do something and return an HTTP response
// Optionally, do something with c.req.param("slug")
});
app.post("/api/posts/:slug/comments", async (c) => {
// Do something and return an HTTP response
// Optionally, do something with c.req.param("slug")
});
export default app;6. Запросите комментарии
Добавьте логику для GET конечная точка для получения комментариев к определённой записи. Она использует D1 Workers Binding API чтобы подготовить и выполнить параметризованный запрос:
app.get("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { results } = await c.env.DB.prepare(
"SELECT * FROM comments WHERE post_slug = ?",
)
.bind(slug)
.run();
return c.json(results);
});app.get("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { results } = await c.env.DB.prepare(
"SELECT * FROM comments WHERE post_slug = ?",
)
.bind(slug)
.run();
return c.json(results);
});Код использует prepare чтобы создать параметризованный запрос, bind чтобы безопасно передать значение slug (это предотвращает SQL-инъекции), а run чтобы выполнить запрос.
7. Вставьте комментарии
Добавьте POST конечная точка для создания новых комментариев. Она проверяет тело запроса перед вставкой строки:
app.post("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { author, body } = await c.req.json();
if (!author) return c.text("Missing author value for new comment", 400);
if (!body) return c.text("Missing body value for new comment", 400);
const { success } = await c.env.DB.prepare(
"INSERT INTO comments (author, body, post_slug) VALUES (?, ?, ?)",
)
.bind(author, body, slug)
.run();
if (success) {
c.status(201);
return c.text("Created");
} else {
c.status(500);
return c.text("Something went wrong");
}
});app.post("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { author, body } = await c.req.json<{
author: string;
body: string;
}>();
if (!author) return c.text("Missing author value for new comment", 400);
if (!body) return c.text("Missing body value for new comment", 400);
const { success } = await c.env.DB.prepare(
"INSERT INTO comments (author, body, post_slug) VALUES (?, ?, ?)",
)
.bind(author, body, slug)
.run();
if (success) {
c.status(201);
return c.text("Created");
} else {
c.status(500);
return c.text("Something went wrong");
}
});8. (Необязательно) Добавьте поддержку CORS
Если вы планируете обращаться к этому API из фронтенд-приложения с другого источника, добавьте CORS middleware. Импортируйте cors модуль из Hono и добавьте его перед своими маршрутами:
import { Hono } from "hono";
import { cors } from "hono/cors";
const app = new Hono();
app.use("/api/*", cors());import { Hono } from "hono";
import { cors } from "hono/cors";
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use("/api/*", cors());При отправке запросов к /api/*, Hono будет автоматически создавать и добавлять заголовки CORS к ответам вашего API.
9. Разверните приложение
-
Войдите в свою учётную запись Cloudflare (если ещё не сделали этого):
npx wrangler whoamiЕсли вход не выполнен, Wrangler предложит войти.
-
Разверните ваш Worker:
npx wrangler deploy -
Протестируйте API, вставив и затем получив комментарий:
# Replace <YOUR_SUBDOMAIN> with your workers.dev subdomain curl -X POST https://d1-comments-api.<YOUR_SUBDOMAIN>.workers.dev/api/posts/hello-world/comments \ -H "Content-Type: application/json" \ -d '{"author": "Kristian", "body": "Great post!"}'Createdcurl https://d1-comments-api.<YOUR_SUBDOMAIN>.workers.dev/api/posts/hello-world/comments[ { "id": 1, "author": "Kristian", "body": "Great post!", "post_slug": "hello-world" } ]
Полный пример
Полный src/index.ts со всеми маршрутами и поддержкой CORS:
import { Hono } from "hono";
import { cors } from "hono/cors";
const app = new Hono();
app.use("/api/*", cors());
app.get("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { results } = await c.env.DB.prepare(
"SELECT * FROM comments WHERE post_slug = ?",
)
.bind(slug)
.run();
return c.json(results);
});
app.post("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { author, body } = await c.req.json();
if (!author) return c.text("Missing author value for new comment", 400);
if (!body) return c.text("Missing body value for new comment", 400);
const { success } = await c.env.DB.prepare(
"INSERT INTO comments (author, body, post_slug) VALUES (?, ?, ?)",
)
.bind(author, body, slug)
.run();
if (success) {
c.status(201);
return c.text("Created");
} else {
c.status(500);
return c.text("Something went wrong");
}
});
export default app;import { Hono } from "hono";
import { cors } from "hono/cors";
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use("/api/*", cors());
app.get("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { results } = await c.env.DB.prepare(
"SELECT * FROM comments WHERE post_slug = ?",
)
.bind(slug)
.run();
return c.json(results);
});
app.post("/api/posts/:slug/comments", async (c) => {
const { slug } = c.req.param();
const { author, body } = await c.req.json<{
author: string;
body: string;
}>();
if (!author) return c.text("Missing author value for new comment", 400);
if (!body) return c.text("Missing body value for new comment", 400);
const { success } = await c.env.DB.prepare(
"INSERT INTO comments (author, body, post_slug) VALUES (?, ?, ?)",
)
.bind(author, body, slug)
.run();
if (success) {
c.status(201);
return c.text("Created");
} else {
c.status(500);
return c.text("Something went wrong");
}
});
export default app;Следующие шаги
- См. D1 Workers Binding API полный список доступных методов.
- Подробнее о локальная разработка D1 для тестирования базы данных без развёртывания.
- Изучите проекты сообщества, созданные на основе D1.