Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/3-web-servers/14-project/_samples/ai-todo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
generated/
.env
68 changes: 68 additions & 0 deletions docs/3-web-servers/14-project/_samples/ai-todo/main.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import express from "express";
import { PrismaClient } from "./generated/prisma/index.js";

const app = express();
const client = new PrismaClient();

app.use(express.json());
app.use(express.static("./public"));

app.get("/todos", async (request, response) => {
const todos = await client.todo.findMany({ orderBy: { id: "asc" } });
response.json(todos);
});

app.post("/todos", async (request, response) => {
const todo = await client.todo.create({
data: {
title: request.body.title,
dueAt: new Date(request.body.dueAt),
},
});
response.json(todo);
});

app.delete("/todos/:id", async (request, response) => {
await client.todo.delete({
where: { id: parseInt(request.params.id) },
});
response.json({ success: true });
});

app.post("/todos/ai", async (request, response) => {
const systemPrompt = `
ユーザーの入力から、ToDoのタイトルと期限を抽出してください。
1行目にタイトル、2行目に期限(ISO8601形式、タイムゾーンは東京)を出力してください。
現在日時: ${new Date().toISOString()}

現在日時: 2026-01-20T12:00:00+09:00
入力: 明日の10時に会議
出力: 会議
2026-01-21T10:00:00+09:00
`;

const result = await fetch("https://openrouter.ai/api/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "openrouter/free",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: request.body.instruction },
],
}),
});
const data = await result.json();
const content = data.choices[0].message.content;
const lines = content.split("\n");
const todo = await client.todo.create({
data: { title: lines[0], dueAt: new Date(lines[1]) },
});
response.json(todo);
});

app.listen(3000);
Loading
Loading