sveltekitでは,routeディレクトリ内の.json.tsファイルでサーバーでの処理を記述でき,そこからjsonレスポンスを取得することができます.getメソッドの定義ではjsonに変換できるオブジェクトならなんでもbodyとして返すことができます.
今回は,その返り値にinterfaceを定義したオブジェクトを利用するときに少しハマったのでメモしておきます.
以下のように[slug].json.ts
でマークダウンファイルを読み込み,ページのメタ情報とhtmlの文字列を返すgetメソッドを定義したいとします.
[slug].json.ts
import type { RequestHandler } from '@sveltejs/kit';
import fs from "fs"
import {mdToHtml} from '$lib/convert_markdown';
import type {HtmlWithMeta} from '$lib/convert_markdown';
const ARTICLES_DIR_PATH = "contents/articles"; // 記事のあるディレクトリのパス
export const get: RequestHandler = async ({params}) => {
const md_str = fs.readFileSync(ARTICLES_DIR_PATH + "/" + params.slug + ".md").toString();
const html_with_meta: HtmlWithMeta = mdToHtml(md_str);
return {body: html_with_meta}
}
ここで,mdToHtml
はマークダウンの文字列をHtmlWithMeta
インターフェースのオブジェクトに変換する関数です.インターフェースは以下のように定義されているとします.
convert_markdown.ts
/** メタ情報を含んだhtmlの情報 */
interface HtmlWithMeta {
content: string, // htmlの文字列
meta_property: MetaProperty, // 記事のメタ情報
use_katex: boolean, // 数式を使うかどうか
use_code: boolean // コードを使うかどうか
}
/** ページのメタ情報 */
interface MetaProperty {
title: string, // ページのタイトル
page_path: string, // ページの相対パス
page_desc: string, // ページの概要
og_image_url: string // OGP 画像のurl・パス
}
しかし上のget関数はコンパイルが通りません.これはHtmlWithMeta
インターフェースが公式ドキュメントで定義されている型に変換できないためだと思われます.そこで以下のように一度JSONを通すことでコンパイルが通るようになります.
[slug].json.ts
return {body: JSON.parse(JSON.stringify(html_with_meta))}
スプレッド構文を利用することもできますがHtmlWithMeta
のようにインターフェースがネストしている場合は,以下のようにネストしている部分を後からスプレッド構文で置き換える必要があります.
[slug].json.ts
return {
body:{
...html_with_meta,
meta_property:{...html_with_meta.meta_property},
}
}
クライアント側の[slug].svelte
では通常と同じように利用できます.
[slug].svelte
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit'
import type {HtmlWithMeta} from "$lib/convert_markdown";
const ARTICLE_URL = "/articles"
export const load: Load = async({page, fetch}) => {
const html_with_meta_res = await fetch(ARTICLE_URL + "/" + page.params.slug + ".json");
if (html_with_meta_res.ok) {
let html_with_meta: HtmlWithMeta = await html_with_meta_res.json();
return {props: {html_with_meta}}
}
};
</script>
<script lang="ts">
export let html_with_meta: HtmlWithMeta;
</script>