FGK-SYSTEMS 

戻る トップメニュー

. 【SvelteKit】
web アプリケーションを開発する為の JavaScript フレームワーク
VBやC#しか知らない人へのSvelteKit入門


オールWINDOWS環境での下記の機能を確認
・ インストール
・ WEBアプリ立上まで
 ・ ルーティング
 ・ ログインとログアウト処理
 ・ 単一画面処理
 ・ 一覧画面処理
 ・ URLのクエリ文字列処理
マイクロサービスアーキテクチャーの各サービスの開発に最適かの確認

SvelteKit の概要
SvelteKit を選んだ三つの重要な理由です。(2023年夏時点では)
 ・高い生産性がある。
 ・他のフレームワークと比べてとてもシンプルで、学習コストが低い。
 ・堅牢でハイパフォーマンスな web アプリケーションを問題なく開発できる。

SvelteKit は Svelte を使用して web アプリケーションを開発する為の JavaScript フレームワークです。 SvelteKit は SSR(サーバーサイドレンダリング)、SSG(静的サイトジェネレーション)、SPA(シングルページアプリケーション)などの様々なレンダリング方法に対応しています。

以下 Svelte について説明します。Svelte の発音はスベルト。 最大の特徴は、Svelteがコンパイラであるということです。 Svelte の最大の特徴は、コンパイル時に HTML テンプレートを DOM を直接操作する JavaScript コードに変換することです。 これにより、ファイルサイズや実行時のパフォーマンスが向上します。 Svelte は、TypeScript で書かれたコンパイラで MIT ライセンスでオープンソースとして公開されています。
参考にしたサイトへの感謝 C# 系でやってきた自分自身の Sveltekit 入門学習のため下記の2つのサイトを参考にし、 掲載されたコードを使わせて頂きました。感謝しています。 このページのコードも部分的に入門作業で使わせて頂いたコードが載せてあります。 (確認と習得作業の都合かなり修正が入ってますが)       【SvelteKit入門】初めてSvelteKitを使い始めた人のための基礎講座
      【SvelteKit入門】SvelteKit + Prismaによる掲示板アプリ作成

環境構築 前もって下記をインストールします。インストールはそれぞれにサイトを参照してください。 ・Nodejs
・SQLite
格納されるホルダはデホルトで問題ありません。ちなみに私の環境での説明です。 OS は Windows11 です。node --version は v18.17.1 SQLite version 3.43.1 Nodejs ホルダは "C:\Program Files\nodejs" に作成されています。 パスをとうしておくことを忘れないでください。( npm,npx もこのホルダーにありよく使います) また SQLite3.exe は "C:\app\SqliteDB\sqlite3.exe" に作られています。 これも、パスもとうしています。

プロジェクトをいれるホルダを作成
プロジェクトを開発するディレクトリを作成します。 できれば、Cドライブの下に新たに専用のホルダを作成してください。 私は今回は下記のホルダに作成しました。
     パス: C:\aaTest\x25sk\proj


SvelteKitのプロジェクトを作成

コマンドプロンプトまたは"bash"を開いてください。そして作成したホルダに移動(cd)してください。 コマンドを入れます。
     npm create svelte@latest [プロジェクト名]

私は今回はプロジェクト名は「board_a_t」で作成しました。 コマンドは「npm create svelte@latest board_a_t」といれています。 作成時にいろいろなにを使うかをを使うか聞かれます。今回の場合は下記を選択しています。
     ・テンプレートに「Skeleton project」を選択
     ・「TypeScript」を選択
基本的にYNの質問には「yes」で良いと思います。

「create svelte@latest」が成功すると下記が表示されます。
     Next steps:
     1: cd board_a_t
     2: npm install (or pnpm install, etc)
     3: git init && git add -A && git commit -m "Initial commit" (optional)
     4: npm run dev -- --open
プロジェクトに移動し、「npm install」をおこないます。
     cd sveltekit-first
     npm install
この後、コマンドを実行すると開発サーバが起動
     npm run dev
表示されたURLをCtrlキーを押しながらクリックすると Skeleton project の初期画面が表示されます。 今後の作業で開発中の画面等の操作や表示もこのコマンドです。
以下参考情報ですが
     npm run build でリリース版のビルド
     npm run preview でビルドしたアプリケーションをプレビュー


Prismaをインストールと設定

データベースは直接のSQL操作はせず、Prisma から操作します。 これにより、開発時点は Sqlite に Prisma をつないでおいて、 本番では PostgreSQL などの他のデータベースにつなぎ直すことが簡単になります。

下記のコマンドを入力し、「prisma」をインストールます。
  npm install prisma --save-dev
  
開発では sqlite を使うので、オプションを SQLite で指定しPrisma の設定ファイルを作成します。
  npx prisma init --datasource-provider sqlite
  
コマンドを実行したホルダ直下に下記が作成される
     ファイル .env
     ホルダ prisma
     ホルダ prismaのなかに設定ファイル schema.prisma

ファイル説明 .env
	DATABASE_URL="file:./dev.db"
	// データベースの場所
	// SQLite の場合上記の内容となる。(開発ではそのまま使う)
	// 外部にDBを使う場合はそのURIを書く
  
ファイル説明 schema.prisma
	generator client {
	  provider = "prisma-client-js"
	}
	datasource db {
	  provider = "sqlite"
	  url      = env("DATABASE_URL")
	}
	// 上記にデータベースへの接続情報
	// 以下にテーブル(データモデル)の情報
  
PrismaClientの作成
src下にlib/prisma.tsを作成し下記内容をいれる。
	import { PrismaClient } from "@prisma/client";
	export const db = new PrismaClient()
  
ファイル名とPrismaClientオブジェクト名は何でもよいが、後で利用するとき対応させる。
	// 省略...
	import { db } from "$lib/prisma";
	// 省略...
        const user = await db.user.findUnique({
            where: {name}
        })
	// 省略...
  
bcryptをインストール

ログインで使用するパスワードハッシュの為、「bcrypt」を使います。 bcrypt はセキュリティの高いパスワードハッシュを生成するために使われるライブラリです。 bcrypt は npm で次のコマンドでインストールできます。
    npm install --save bcrypt
  
zod のインストール

バリデーション処理を zod を利用して行います。 zod はインストールが必要になりますので npm でインストールします。
    npm install zod
  


習得用WEBアプリの作成

テーブル作成
「schema.prisma」を開きます。 今回の掲示板アプリに必要なテーブルです。
     「User」それぞれユーザのテーブル
     「Post」投稿されたバックログのスレッドのテーブル
     「Comment」バックログごとのコメントのスレッドのテーブル
「schema.prisma」に下記のようにテーブル定義します。
  // 省略...
  model User {
    id         String    @id @default(uuid())
    name       String    @unique
    password   String
    addressm   String
    authToken  String    @unique
    created_at DateTime  @default(now())
    Comment    Comment[]
  Post       Post[]
  }

  model Post {
    id         Int       @id @default(autoincrement())
    userId     String
    content    String
    created_at DateTime @default(now())
    Comment    Comment[]
  user       User      @relation(fields: [userId], references: [id])
  }

  model Comment {
    id         Int      @id @default(autoincrement())
    userId     String
    postId     Int
    content    String
    created_at DateTime @default(now())
    post       Post     @relation(fields: [postId], references: [id])
    user       User     @relation(fields: [userId], references: [id])
  }
  
SQLiteの準備が整っていればターミナルで下記のコマンドを打ちます。
     npx prisma migrate dev --name init
このコマンドは、prisma/migrationsというフォルダにSQLマイグレーションファイルを生成します。 このマイグレーションファイルでデータベースに適用します。 オプション(--name init)は任意の名前でマイグレーションファイルを作ります。


ルーティングについて
SvelteKitでは、ファイルシステムベースのルーティングが採用されています。 具体的には、「src/routes」ディレクトリに配置したフォルダとファイルに応じてページの生成や遷移が行われます。「routes」ホルダ以下のホルダがページに対応します。ダイナミックルーティングもサポートしていますが今は説明を省略します。 VBやC#しかやったことが無い人は .aspx ファイルがページに対応していたので異質に感じます。

「routes」ホルダ以下のホルダには同じ名前の「page.svelte」や「+page.server.js」のファイルが作られます。 VBやC#しかやったことが無い人は 同じ名前のファイルが各ホルダに付いているので間違えないようにしてください。 これから行う実装のほとんどが、これらのホルダやファイルを作る作業となります。

「page.svelte」はそのホルダの表示内容を持ちます。 「src/routes」ホルダ直下の「+page.svelte」はルート(`/`)のページとして扱われます。 また、「src/routes/about/+page.svelte」であれば、そのページは(`/about`)のURLでアクセスできます。

「+page.server.js」は、各ページごとにサーバーサイドで実行されるます。 このファイルでデータ取得などをし、「+page.svelte」に渡したりする処理を記述します。 このファイルで「+page.svelte」からデータを受け取る処理、データ取得や加工などをし、 「+page.svelte」に渡したりする処理を記述します。

アプリケーションのルーティングを作ります。 この SvelteKit は src/routes がデフォルトのルートです。

今までC#のページを作ってきた人は、混乱するかもしれませんが、次のように受け入れてください。
     ・このweb最初のページはsrc/routesのホルダで、「/」で表示されます。
     ・以降のページはsrc/routesのホルダの以下に作られたホルダになります。
     ・この以下に作られたホルダの名前がそのページ名になります。
     ・各ページの内容はそのホルダの中の「+page.svelte」で記述されます。
       (各ページ同じファイル名なので間違えないように)

まずページとなるホルダを作成します。
  □ src
   □ rib
   □ routes        「ホーム: デフォルトのルート」
    □ login       「ログイン画面」
    □ logout      「ログアウト画面」
    □ register    「ユーザの登録」
    □ tdetail     「バックログの詳細コメント一覧」
    □ tlist       「バックログの一覧」
  
詳細は以降の作業で説明して行きます。

共通の情報の設定 共通のCSS

共通のCSSは /src/app.html に追記で作成する。 以下、今回追記した内容です。
<style>
	.active {
		color: red;
		font-weight: 1200;
	}
	.errormes {
		color: red;
		font-weight: 1200;
	}
</style>
  
共通の情報の設定 各ページに付くヘッダーとフッターのレイアウト

ホルダ /src/routes 直下のレイアウトファイルの「+layout.svelte」で、共通のヘッダーとフッターを作ります。 このレイアウトコンポーネントには、ヘッダーやフッターなどの共通部分を記述し、 <slot>から</slot>というタグの間に各ページコンポーネント 「+page.svelte」の内容を差し込みます。

以下、今回作成した内容です。
	<!-- src/routes/+layout.svelte -->

	<script>
		import { page } from '$app/stores';
		$: pathname = $page.url.pathname;
	</script>

	<header>
	  <span class="fs10">F's Back Log Test</span>
	    <nav>
		<a href="/" class:active={pathname === '/'}>home</a>
		<a href="/login" class:active={pathname === '/login'}>login</a>
		<a href="/logout" class:active={pathname === '/logout'}>logout</a>
		<a href="/register" class:active={pathname === '/register'}>register</a>
		<a href="/tlist" class:active={pathname === '/tlist'}>BACKLOG</a>
	    </nav>
		<hr>
	</header>

	<main>
	  <slot></slot>
	</main>

	<footer>
		<br>
		<hr>
		<span class="fs10">c 2023 FGK SYSTEM  (SvelteKit test)</span>
	</footer>
 
	<style>
	    .fs10 {font-size: 10px;}
	</style>
  

エラー専用画面の表示
+error.svelte
ホルダ /src/routes 直下のレイアウトファイルの「+error.svelte」を作ります。 これはエラー表示専用のページです。

以下、今回作成した内容です。
	<script>
	  // +error.svelte

	  import { page } from '$app/stores';
	</script>
  
	<h1>{$page.error?.message}</h1>
  

ユーザの登録画面

最初に新規登録画面を作成いたします。 /register下に「+page.svelte」を作り、下記のような登録フォームを作ります。
<script lang="ts">
    // register/+page.svelte

    import type { ActionData } from "./$types"
    export let form: ActionData
</script>

<h3>Back Log ユーザー登録</h3>

<form method="post" action="?/register">
    <span class="fs14">名前:</span>
    <span class="fs10">(半角英数字とドット)</span><br>
    <input name="name" type="text" /><br>
    <span class="fs14">パスワード:</span>
    <span class="fs10">(半角英数字と*)</span><br> 
    <input name="password" type="password" /><br>
    <span class="fs14">メールアドレス:</span><br> 
    <input name="addressm" type="text" /><br><br>
    <button>登録する</button>
    {#if form?.message}
        <p class="errm">{form.message}</p>
    {/if}
</form>

<style>
    .fs14 {font-size: 14px;}
    .fs10 {font-size: 10px;}
    .errm {
        padding: 0.5em;
        font-weight: 900;
        color: white;
        background-color: red;
    }
</style>
  
サーバサイドのロジック「+page.server.ts」を作成します。 この処理はよくある単一画面用のサーバです。 受け取ったユーザ、パスワード、メールアドレスをデータベースへ登録します。 このユーザの登録処理で、zodのバリデーションを使います。 またパスワードのハッシュ処理をしています。 実際の下記コードを見て使用方法を理解してください。 ◎バリデーション スキーマの部分
      コード zod のインポートからスキーマ作成の部分
◎バリデーション データでの関数使用部分
      コード blschema.parse(formData)
◎バリデーション エラー処理部分
      コード try catch のエラー処理の z.ZodError
◎ハッシュ処理部分
      コード password : await bcrypt.hash(password, 10) を参照
// register/+page.server.ts
import { fail, redirect } from "@sveltejs/kit";
import type { Actions } from "./$types";
import bcrypt from 'bcrypt'
import { db } from "$lib/prisma";
import * as z from "zod";

const reged1 = RegExp( '^[A-Z0-9.]{4,}$' );
const reged2 = RegExp( '^[a-zA-Z0-9*]{4,}$' );
const regem1 = "半角(英大文,字数字,ドット)4桁以上が user入力です。 ";
const regem2 = "半角(英,数字と*)で4桁以上が password入力です。 ";
const regem3 = "入力はmailaddress様式で入力してください。 ";
const blschema = z.object({
    name: z.string().regex(reged1, regem1),
    password: z.string().regex(reged2, regem2),
    addressm: z.string().email(regem3),
})
let name: string;
let password: string;
let addressm: string;

export const actions: Actions = {
    register : async ({request}) => {
        const formData = Object.fromEntries(await request.formData());
        try {
            const data = await blschema.parse(formData)
            name = data.name;
            password = data.password;
            addressm = data.addressm;
            const user = await db.user.findUnique({
                where: {name}
            })
            if (user) {
                return fail(400, { message: "既に存在するユーザーです。" })
            }
            await db.user.create({
                data: {
                    name,
                    password : await bcrypt.hash(password, 10),
                    addressm,
                    authToken: crypto.randomUUID(),
                },
            })
    
            throw redirect(303, '/login')

        }catch(err){
            
            if (err instanceof z.ZodError) {    
                let mesj = JSON.parse(err.message);
                let mesdisp: string = mesj[0].message;
                if(mesj[1]) mesdisp = mesdisp + mesj[1].message;
                if(mesj[2]) mesdisp = mesdisp + mesj[2].message;
                return fail(400, { message: mesdisp });                
             } else {
                console.log('register調査7 mesj:' + JSON.stringify(err));
                let mesx: string = 'その他:' + JSON.stringify(err);
                return fail(400, mesx);    
            }

        }
    }
}
  

ホーム画面
ユーザの入力画面ができたので、ユーザの一覧をホーム画面に表示しようと思います。 ホルダ /src/routes の直下の +page.svelte と +page.server.ts 下記のように 作ります。
<script lang="ts">
    // routes/+page.svelte

    import type { PageServerLoad } from "./$types";
    export let data: PageServerLoad;
</script>

<main>
    <h2>ホーム</h2>
    <p><span class="fs20">ユーザ一 一覧</span></p>
{#each data.threadUser as thread }
    <p>
        <span class="fs20">{thread.name}</span>
        <span class="fs10">{thread.addressm}</span>
    </p>    
{/each}
</main>

<style>
    .fs20 {font-size: 20px;}
    .fs10 {font-size: 10px;}
</style>
  
// routes/+page.server.ts

import { db } from "$lib/prisma";
import type { PageServerLoad } from "./$types";

export const load: PageServerLoad = async () => {
    const threadUser = await db.user.findMany({
        select: {
            name: true,
            addressm: true
        },
        
    });
    if(!threadUser) throw fail(404, {message: "存在しないスレッドです。"})
    return {threadUser}
}
  
ここまでのテスト ここまでの作業で一部の画面は使えるようになっています。下記のテスト作業をしてください
 ・ユーザを登録する(ユーザ、パスワード、メールアドレス)
    ・エラーのないデータ数件(今後のテストで使うので覚えやすいユーザとパスワード)
    ・エラーのある(ユーザ、パスワード、メール)
 ・ホームでユーザの一覧表示

以下は画面の参考情報です。実際の表示内容は上記作業で入力した内容が表示されます。



ログイン画面
ログイン画面を作ります。
ユーザとパスワードが登録されている場合、cookieでセッションを登録します。 ホルダ /src/routes 下に「+page.svelte」を作成し、ログインフォームを作ります。
以下作成内容です。
<script lang="ts">
    // login/+page.svelte

    import type { ActionData } from "./$types";
    export let form: ActionData;
</script>

<h2>ログイン画面</h2>
{#if form?.message}
    <p class="error">{form.message}</p>
{/if}
<form method="POST" action="?/login">
    <span class="fs10">名前:</span><br>
    <input name="name" type="text" /><br>
    <span class="fs10">パスワード:</span><br> 
    <input name="password" type="password" /><br><br>
    <button>ログイン</button>
</form>
 
<style>
    .fs10 {font-size: 10px;}
</style>
  
ログインのサーバ処理を作ります。
以下作成内容です
// login/+page.server.ts

import { db } from "$lib/prisma";
import { type Actions, fail, redirect} from "@sveltejs/kit";
import bcrypt from "bcrypt";

export const actions: Actions = {
    login: async ({request, cookies}) => {
        const data = await request.formData();
        const name = data.get("name");
        const password = data.get("password");
    
        if (typeof name !== "string" || typeof password !== "string" || !name || !password) {
            return fail(400, { message: "名前とパスワードを入力してください" })
        }
        const user = await db.user.findUnique({
           where: {name}
        })

        if (!user) {
            return fail(400, { message: "名前またはパスワードを間違えています" });
        }

        const correctPassword = await bcrypt.compare(password, user.password)

        if (!correctPassword) {
            return fail(400, { message: "名前またはパスワードを間違えています"})
        }

        const authenticatedUser = await db.user.update({
            where: { name },
            data: {
                authToken: crypto.randomUUID()
            }
        })

        cookies.set('session', authenticatedUser.authToken, {
            path: '/',
            maxAge: 3600,
        })

        throw redirect(303, '/tlist')
    }
}
  
ここまでのテストをします。

ログアウト機能
ログアウトするというのはcookie情報を消すということです。それだけです。 ログアウトの表示画面を作成します。 src/routes下にlogoutフォルダとlogoutフォルダ下に「+page.svelte」を作成してください。
<script lang="ts">
    // logout/+page.svelte

    import type { Actions } from "@sveltejs/kit";
    export let form : Actions
</script>

<h3>Back Log ログアウト</h3>

{#if form?.message}
{form.message}
{/if}
<form method="POST" action="?/logout">
    <label for="content">
        ログアウトします(キャンセル:なにかの文字を記入する)
    </label>
    <input name="content" type="text" /><br>
    <button type="submit">確認</button>
</form> 
  
src/routes下にlogoutフォルダとlogoutフォルダ下に「+page.server.ts」を作成してください。 「+page.server.ts」内で行うことはcookieを消して、ログイン画面にリダイレクトさせるだけです。
// logout/+page.server.ts

import {  redirect, type Actions } from "@sveltejs/kit";

export const actions: Actions = {
    logout : async ({request, cookies}) => {
        const data = await request.formData();
        const content = data.get("content");

        if (typeof content !== "string" || !content) {
            console.log ("ログアウトを完了")
            cookies.delete('session');
        }else{
            console.log ("ログアウトをキャンセル")
            throw redirect(303, "/")
        }
    }
} satisfies Actions;
  

ホルダ /src/lib
バックログの一覧画面のスレッド部分の作成
この説明は「src/lib」を使うときの説明です。(使わなくてもできますが) バックログのスレッドを一覧できる画面を実装します。 それぞれのスレッドをコンポーネントを使って表示します。 src/libに「Thread.svelte」を作成してください。 大事なポイントはIDとContentを変数で宣言し、それをHTML内で使用していることです。 また詳細画面へ飛ぶためのIDを組み込んだ「a href=」を入れています。 重要な修正です。 参考サイトの入門では「a href=」の使い方は「 URL/{変数} 」の書き方でした。 うまく動きませんでした。 それでWindowsのクエリ文字列に修正し「 URL?変数={データ} 」で対応しています。 このhrefの行は「a href="./tdetail?id={id}"詳細を見る」で記述されます。 さらにとび先のページ tdetail もクエリ文字列を読む方法に修正しています。
<script lang="ts">
<script lang="ts">
    // src/lib/Thread.svelte

    export let id : number;
    export let content: string;
</script>

<div class="thread">
    <div class="thread-content">
      <h4>
        {id} ,
        {content} ,
        <a href="./tdetail?id={id}">詳細を見る</a>
      </h4>
      
    </div>
</div>

<style> 
.thread {
  display: flex;
  flex-direction: row;
  width: 90%;
  margin: 2px auto;
  box-shadow: 0 10px 10px rgba(0,0,0,0.9), 0 6px 6px rgba(0,0,0,0.13);
}
.thread-content {
  width: 90%;
  margin-left: 10px;
}
.thread-content a {
  display: inline-block;
  font-size: 14px;
  color: blue;
  text-decoration: none;
}
</style>
  


バックログの一覧画面
ホルダsrc/routes下にホルダtlistを作り、「+page.svelte」のファイルを作成
<script lang="ts">
    // tlist/+page.svelte
    import type { Actions } from "@sveltejs/kit";
    import type { PageServerLoad } from "./$types";
    import Thread from "$lib/Thread.svelte";
    export let form : Actions
    export let data: PageServerLoad;
</script>

<main>
    <h3>Back Log 一覧</h3>

    {#if form?.message}
        {form.message}
    {/if}
    <form method="POST" action="?/post">
        <label>内容:
        <input name="content" type="text" />
        </label>
        <button>投稿する</button>
    </form>

    {#each data.threads as thread }
       <Thread id={thread.id} content={thread.content} />
    {/each}
</main>
  
Threadコンポーネントは他と同じようにインポートするだけで使えるようになります。 PageData型はサーバーサイドで生成されたデータを、クライアント側で使用するためのものです。 なので、dataにはサーバー側からスレッド一覧のデータが送られてくることを想定しています。 HTMLには「投稿する」ボタンの部分のフォームを作成。 さらに、each文でスレッドコンポーネントを並べていきます。 スレッドの表示項目のidにthread.idを、contentにthread.contentを渡しています。

ホルダtlistに、「+page.server.ts」のファイルを作成。 サーバでのスレッドのload処理と、「投稿する」ボタンのActions処理を記述します。
// tlist/+page.server.ts

import { db } from "$lib/prisma";
import type { PageServerLoad } from "./$types";
import { fail, redirect, type Actions } from "@sveltejs/kit";

// DBのPOSTのデータ全権のスレッドデータを作成
export const load: PageServerLoad = async () => {

    const threads = await db.post.findMany({
        orderBy:{ id: 'desc' },
    });
    return {threads}
}

export const actions: Actions = {
    post : async ({request, locals}) => {
        const data = await request.formData()
        const content = data.get("content");

        console.log ("tlist調査 content:" + content)

        if (typeof content !== "string" || !content) {
            return fail(400, { message: "内容は必須入力です。"})
        }

        if (!locals.user) return fail(400, {message: "登録されていないユーザーです。"})

        await db.post.create({
            data: {
                userId: locals.user.id,
                content
            }
        })

        throw redirect(303, "")
    }
}
  

BackLog詳細コメント一覧画面
詳細コメント一覧画面では、コメントを追加することができるコメント一覧画面を作成します。 「+page.svelte」「+page.server.ts」を作成します。
サーバ処理の主な内容は
    ・load関数でバックログデータ(タイトル・作成者・作成日時)とコメント一覧データ(内容・作成者・作成日時)の取得
    ・追加コメントのフォーム処理
    ・削除ボタンのフォーム処理
ここで注意点が二つあります。
(1)は、URLのクエリ文字列の読み方です。 Windows的には[a href="./tdetail?id={id}">詳細を見る]で渡されます。 したがってURLの後ろの部分からデータ読みます。しかし再表示では読まないようにします。
(2)は、再表示です。「throw redirect(302, "")」で行います。 追加や削除が更新された同じログデータのコメント一覧を表示できます。
// detail/+page.server.ts

import { db } from "$lib/prisma";
import { fail, type Actions, redirect } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types";
import type { PageServerLoad } from "./$types"

let url: LayoutServerLoad;
let wpId: number = 0;
let index: number = 0;
let keyd: string = "";
let surl: string = "";

export const load: PageServerLoad = async ({ url }) => {

    console.log("detail調査2 url:" + url);
    surl = String(url);
    index = surl.indexOf("=");
    console.log("detail調査5 index:" + index);

    if(index < 0){
        //
    }else{
        keyd = surl.substring(index +1);
        wpId = Number(keyd);
    }
    console.log("detail調査6 wpId:" + wpId );

    const threadDetail = await db.post.findUnique({
        where: {
            id : Number(wpId) 
        },
        include: {
            Comment : {
                orderBy: { id : 'desc'},
                select: {
                    id: true,
                    content: true,
                    created_at: true,
                    user: {
                        select:{
                            name: true
                        }
                    }
                }
            },
            user: {
                select: {
                    name: true
                }
            }
        },
    });

    if(!threadDetail) throw fail(404, {message: "存在しないスレッドです。"})

    return { threadDetail }
}

export const actions: Actions = {
    addcomm: async ({request, locals}) => {
        const data = await request.formData();
        const comment = data.get("comment");
        if (typeof comment != "string" || !comment) {
            return fail(400, { message: "コメントは必須入力です。" })
        }
        
        await db.comment.create({
            data: {
                userId: locals.user.id,
                postId: Number(wpId),
                content: comment
            }
        })
        
        throw redirect(302, "");
    },

    delcomm: async ({ request }) => {
		const data = await request.formData();
		const idcomm = Number(data.get('idcomm'));
		await db.comment.delete({
			where: {
				id: idcomm
			}
		})
        
        throw redirect(302, "");
    }

} satisfies Actions;
  
画面処理の主な内容は
    ・追加コメントのアクションの作成
    ・バックログデータを表示
    ・コメント一覧のデータ(内容・作成者・作成日時・削除ボタン)の表示
<script lang="ts">
    // src/routes/tdetail/+page.svelte
        
    import type { PageServerLoad } from "./$types";
    import type { Actions } from "@sveltejs/kit";
    
    export let data: PageServerLoad;
    // export let form : Actions;
   
    const threadAuthor : string = data.threadDetail.user.name;
    const threadContent : string = data.threadDetail.content;
    const threadCreatedAt: Date = data.threadDetail.created_at;
    const comments : comment[] = data.threadDetail.Comment;

    type comment = {
        user: {
            name: string
        }
        content: string
        created_at: Date
    };

    let aurl: string = "";

</script>

<span>
    <span class="fs10"> 項目: </span>
    <span class="fs30"> {threadContent} </span>
    <span class="fs08"> 作成者: </span>
    <span class="fs10"> {threadAuthor} </span>
    <span class="fs08"> 作成日時: </span>
    <span class="fs10"> {threadCreatedAt.toLocaleString()} </span>
</span>
<h3>Back Log 詳細 コメント</h3>

<form method="POST" action="?/addcomm">
    <button type="submit">追加書込</button>
    <input name="comment" type="text" class="txt_wide1"/>
</form>

{#if comments.length}
    {#each comments as comment}
        <form method="POST" action="?/delcomm">
            <span class="fs16">{comment.content}</span>
            <span class="fs08">({comment.user.name})</span>
            <span class="fs08">:{comment.created_at.toLocaleString()}</span>
            <input type="hidden" name="idcomm" value={comment.id} />
            <button>X</button>
        </form>      
    {/each}
{:else}
   <p>コメントはありません</p>
{/if}

<style>
    .fs30 {font-size: 30px;}
    .fs20 {font-size: 16px;}
    .fs10 {font-size: 10px;}
    .fs08 {font-size: 8px;}
    .txt_wide1{
        display: inline-block;
        width: 80%;
        padding: 0.5em;
        border: 1px solid #999;
        box-sizing: border-box;
        background: #f2f2f2;
        margin: 0.5em 0;
    }
</style>
  
最終テスト ここまでの作業で全部の画面は使えるようになっています。下記のテスト作業をしてください
 ・ログインを登録する(成功するとバックログ一覧へ遷移)
 ・バックログ一覧作業
    ・バックログの追加(追加内容更新で再表示)
    ・詳細を見る(バックログ詳細コメントへ遷移)

以下は画面の参考情報です。実際の表示内容は上記作業で入力した内容が表示されます。
|
ここまでの作業で全部の画面は使えるようになっています。下記のテスト作業をしてください
 ・バックログ詳細コメント作業
    ・コメントの追加(追加内容更新で再表示)
    ・コメントの削除(削除更新で再表示)
 ・(メニューバー)ログアウトをする(ログイン画面へ遷移)

以下は画面の参考情報です。実際の表示内容は上記作業で入力した内容が表示されます。
まとめ

作業をしてみてどう感じましたか? たぶんC#に比べて設計から実装の工数は1/2以下でしょうか。 いやもっと早いたぶん、C#に比べて工数は1/4以下でしょう。 もしC#ならユーザ数やユーザ環境、開発工数を考えてクラサバのするかWEBにするかをまず悩んだと思いますが。 低工数で開発ができる sveltekit なら問題なさそうです。

次に開発用のドキュメントです。データベース用のER図は必要ですが、 sveltekit で基本的な手順で作るならなら実装用の資料は、 画面ごとのプログラム仕様書(またはユースケースフロー + 画面図)だけで十分。 もしC#のWPFの実装用の資料ならインターフェースやクラスの継承等がわかる画面ごとのクラス図が必要ですが、 sveltekit では不要でしょう。 また prisma を使うことでデータベース側の開発を分離する必要は無くなります。 分離しなければそれに関わる資料が一切不要になります。調整する工数時間も不要になります。

マイクロサービスアーキテクチャーを採用し、各サービスの開発をするツールに、 現状では最適なものが sveltekit だと思います。 ドキュメントも下記の7種類を作れば sveltekit の開発は問題ないと考えます。

1. 現状調査と改善案の資料
2. 企画書:トヨタのA3企画書相当
3. マイクロサービス分割と各サービスの開発移行日程
4. 開発環境資料(開発環境、メンバーと担当、開発と移行手順、本番環境)
    (以下は各サービスごとに)
5. ユースケース図 (* 1)(* 2)
6. ER図
7. サービス内の各画面の説明書とテスト計画書 (* 3)(* 4)

(* 1) ユースケース図の各ユースケースごとにフロー文章があること
(* 2) 画面の設計が固まったら画面に合わせてユースケースを調整すること
(* 3) バッチ処理の場合も専用の操作画面があること
(* 4) 説明書とテスト計画は必ず対で作成すること

以上のドキュメントが誰でも参照でき、その仕事の担当者が作成したり利用できる資料となります。 各資料は都度修正していいのですが、修正歴 やバージョン管理が必須です。

以下資料についての注意事項ですが。 共用する資料として作ってはいけないものがあります。 WBSと問題点連絡書です。
「WBS」は毎日の計画と進捗ですが、全員が集まって細部の計画や進捗をすることは、 メンバーの開発工数を減らし残業を増やすデメリットです。 それぞれのリーダー格の人が管理作業に使うのは問題ないのですが。共通の資料ではありません。
「問題点連絡書」は未解決問題点として解決するまでは 関係するメンバーに共有される必要がありますが、 解決したらすぐに正規の資料を更新し、問題点連絡書には解決済とします。 問題点連絡書に解決策等を書いてしまうとこれが正規資料となり混乱が始まります。
注意すべきことは、一時的に発生する問題や毎日変化する状態の資料 (メールやチャットの報告、指示、問合せ-回答、問題点連絡書)などを、 開発の為の共有する正規資料としないことです。 開発に関して決定したことは必ず共有の正規資料を更新することです。

.

S 23/09/29 - U 23/11/04 

戻る トップメニュー 


Copyright 2023 FGK-SYSTEM. All Rights Reserved.