NewtとSvelteKitを利用してブログを作成する

最終更新日:

Table of contents

このチュートリアルでは、Newtと SvelteKit を利用して、ブログを作成する手順を紹介します。
具体的には、Newtで管理しているコンテンツの一覧ページと詳細ページを作る手順を紹介します。

記事内で使用している主なソフトウェアのバージョン

  • SvelteKit(@sveltejs/kit): 2.5.1
  • newt-client-js(newt-cliet-js): 3.3.0
SvelteKit v2を利用する場合、Node 18.13 以上が必要となります。ご注意ください。

概要

SvelteKitでプロジェクトを作成し、Newtのコンテンツ情報を取得できるようにします。
コンテンツの一覧ページ(パス: /)と詳細ページ(パス: /articles/:slug 。slugがarticle-1の場合は /articles/article-1)を作成し、ローカル環境で表示を行うまでを説明します。
また、ここではビルド時にHTMLを生成する、静的サイト生成(Static Site Generation)の方法を紹介します。

1. SvelteKitのセットアップ

はじめに、SvelteKitのセットアップを行います。create-svelte を利用することで、簡単にSvelteKitのプロジェクトを作成できます。
以下のコマンドを入力します。

npm create svelte@latest

コマンドを入力すると、以下の質問を聞かれるので、お好きな設定を選びましょう。

  • どこにプロジェクトを作成するか(ここでは ./sveltekit-blog としました)
  • どのappテンプレートを利用するか(ここでは Skeleton project を選択)
  • TypeScriptの型チェックを追加するか(ここでは Yes, using TypeScript syntax を選択)
  • 追加オプションの選択(ここでは Add ESLint for code lintingAdd Prettier for code formatting を選択)

以下のように表示されます。

$  npm create svelte@latest
Need to install the following packages:
create-svelte@6.0.9
Ok to proceed? (y)

create-svelte version 6.0.9

┌  Welcome to SvelteKit!
◇  Where should we create your project?
│  ./sveltekit-blog
◇  Which Svelte app template?
│  Skeleton project
◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax
◇  Select additional options (use arrow keys/space bar)
│  Add ESLint for code linting, Add Prettier for code formatting
└  Your project is ready!

✔ Typescript
  Inside Svelte components, use <script lang="ts">

✔ ESLint
  https://github.com/sveltejs/eslint-plugin-svelte

✔ Prettier
  https://prettier.io/docs/en/options.html
  https://github.com/sveltejs/prettier-plugin-svelte#options

作成したプロジェクトに移動して、開発サーバーを立ち上げます。

$ cd sveltekit-blog
$ yarn install
$ yarn dev

http://localhost:5173/ にアクセスして、以下のような画面が表示されることを確認します。
sveltekit-blog1.png

2. Newtのセットアップ

次にNewtにコンテンツとAPIトークンを用意し、コンテンツの取得を行うための準備を行います。

2-1. Appを追加する

「Appを追加」をクリックして「テンプレートから追加」を選択します。

Appを追加する

表示されるテンプレートの中から「Blog」を選択して、「このテンプレートを追加」をクリックします。
Appテンプレート

テンプレートが追加されると、「投稿データ」「タグデータ」「著者データ」が追加されます。

quick-start03.jpg

2-2. スペースUID・App UID・モデルUIDを確認する

スペースUIDは「スペース設定」から確認できます。

quick-start0402.jpg
quick-start0503.jpg

上記の例だと、スペースUIDは sample-for-docs となります。
この値は3-1で環境変数として定義します。

また「Blog」テンプレートを追加した場合、App UIDは blog、「投稿データ」モデルUIDは article となります。
これらの値は、4-3や5-1で投稿情報を取得する際に利用します。

2-3. Newt CDN API Tokenを作成する

続いて、APIリクエストに必要なトークンを発行します。
スペース設定 > APIキー のページからNewt CDN API Tokenを作成します。

quick-start06.jpg

名前と取得対象を決めて「作成」を押します。

quick-start07.jpg

ここで作成したトークンの値は3-1で環境変数として定義します。

3. リクエストの準備

Newtの SDK を利用することで、NewtのAPIをより簡単に利用できます。
ここではSDKを利用して、NewtのAPIクライアントを作成します。

3-1. 環境変数の設定

SvelteKitの環境変数は、プロジェクトディレクトリの .env ファイルから読み込めます。

ここでは、.env ファイルを作成し、2-2で確認したスペースUID、2-3で作成したトークンの値を定義します。以下を、実際の値で置き換えて定義してください。

.env
1NEWT_SPACE_UID=sample-for-docs
2NEWT_CDN_API_TOKEN=xxxxxxxxxxxxxxx

上記のように定義しておくと、以下のような形で利用できます。

import { NEWT_SPACE_UID, NEWT_CDN_API_TOKEN } from '$env/static/private'

ここで使われている $env/static/private ですが、この static は、これらの環境変数がビルド時に解決され、静的に置き換えられることを示しています。
クライアントサイドに公開しても安全な環境変数の場合、$env/static/public も利用できます。

またビルド時ではなく、実行時に環境変数の値を読みこむ必要がある場合、$env/dynamic/private$env/dynamic/public も利用できます。

3-2. 型情報の生成

3-1で定義した環境変数ですが、このままでは型情報が生成されておらず、以下のようなエラーが出てしまいます。

Module '"$env/static/private"' has no exported member 'NEWT_SPACE_UID'.

型情報を生成するためには、以下のどちらかを行う必要があります。

  • package.jsonの scripts で定義されている dev コマンドの実行(vite dev の実行)
  • package.jsonの scripts で定義されている check コマンドの実行(svelte-kit sync の実行)

環境変数以外にも、このあと $lib の型などを生成する必要があるので、ここでは dev コマンドを実行したまま、以下の作業を進めていきます。

npm run dev
# or
yarn dev

3-3. newt-client-jsのインストール

次に newt-client-js をインストールします。

npm install newt-client-js
# or
yarn add newt-client-js

3-4. APIクライアントの作成

CDN APIを利用するためのクライアントを作成します。ここではサーバーサイドでのみ読み込めれば良いので、src/lib/server ディレクトリを作成し、その中に newt.ts というファイルを作成します。
※ サーバー専用のモジュールについて、詳細はSvelteKitの Server-only modules のドキュメントをご確認ください。

spaceUidtoken のところには3-1で設定した環境変数を入力します。
ここではCDN APIを利用するので、apiType には cdn を指定しましょう。

src/lib/server/newt.ts
1import { createClient } from 'newt-client-js'
2import { NEWT_SPACE_UID, NEWT_CDN_API_TOKEN } from '$env/static/private'
3
4export const newtClient = createClient({
5  spaceUid: NEWT_SPACE_UID,
6  token: NEWT_CDN_API_TOKEN,
7  apiType: 'cdn'
8})

これで、NewtにAPIリクエストを送るための準備ができました。

4. 一覧ページの作成

4-1. 言語の設定をする

src/app.html にある lang 属性を修正します。ここでは日本語 ja を指定します。

src/app.html
<!DOCTYPE html>
  <html lang="en">
  <html lang="ja">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width" />
    %sveltekit.head%
  </head>
  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>

4-2. 投稿の型を定義する

はじめに、投稿の型 Article を定義しておきます。
このチュートリアルでは、_idtitleslugbody のみを使うため、以下のように定義しておきます。

src/lib/server/newt.ts
import { createClient } from 'newt-client-js'
import { NEWT_SPACE_UID, NEWT_CDN_API_TOKEN } from '$env/static/private'

export interface Article {
  _id: string
  title: string
  slug: string
  body: string
}

export const newtClient = createClient({
  spaceUid: NEWT_SPACE_UID,
  token: NEWT_CDN_API_TOKEN,
  apiType: 'cdn'
})

4-3. 投稿一覧を取得する

SvelteKitはファイルシステムベースのルーティングを採用しており、src/routes 配下のディレクトリによって、ルートが定義されます。
例えば、以下のようにルートが作成されます。

  • src/routes/blog/blog

※ ルーティングの詳細については、SvelteKitの ルーティング のドキュメントをご確認ください。

ここではトップページ(パス: /)で投稿一覧を表示したいので、src/routes のディレクトリ内にファイルを作成します。
ルートディレクトリの中には、1つ以上のルートファイルを格納します。ルートファイルには + という接頭辞が付いているので、それで見分けることができます。

SvelteKitのページでは、レンダリングの前にデータを読み込む場合、+page.ts または +page.server.tsload 関数をエクスポートします。ここでは load 関数をサーバー上でのみ実行したいので +page.server.ts を作成します。
データの取得について、詳細はSvelteKitの Loading data のドキュメントをご確認ください。

以下のように記載します。

src/routes/+page.server.ts
1import { newtClient } from '$lib/server/newt'
2import type { Article } from '$lib/server/newt'
3import type { PageServerLoad } from './$types'
4
5export const load: PageServerLoad = async () => {
6  const { items: articles } = await newtClient.getContents<Article>({
7    appUid: 'blog',
8    modelUid: 'article',
9    query: {
10      select: ['_id', 'title', 'slug', 'body']
11    }
12  })
13  return {
14    articles
15  }
16}

Cannot find module '$lib/server/newt' or its corresponding type declarations. のエラーが発生した場合は、3-2でやったように、再び dev コマンドまたは check コマンドを実行してください。

npm run dev
# or
yarn dev

load 関数では、投稿一覧を取得するために、SDKが提供している getContents メソッドを利用します。getContentsはNewtのコンテンツ一覧を取得するためのメソッドです。getContentsのパラメータに Article の型を渡すことで、返却される items の型として Article[] が指定されます。
また、selectパラメータを利用して、取得するフィールドを _idtitleslugbody のみに制限します。

4-4. 投稿一覧を表示する

次に、投稿一覧を表示します。
src/routes/+page.svelte ファイルを修正します。
+page.svelte では load 関数の戻り値を data プロパティを介して使用できます。
ここでは4-3で取得した articles をもとに、eachブロック を利用して、投稿一覧を表示します。

タイトルとディスクリプションは titleとmeta に記載があるように <svelte:head> の内側に記載します。

src/routes/+page.svelte
1<script lang="ts">
2  import type { PageData } from './$types'
3  export let data: PageData
4</script>
5
6<svelte:head>
7  <title>Newt・SvelteKitブログ</title>
8  <meta name="description" content="NewtとSvelteKitを利用したブログです" />
9</svelte:head>
10
11<main>
12  <ul>
13    {#each data.articles as article (article._id)}
14      <li>
15        <a href={`articles/${article.slug}`}>{article.title}</a>
16      </li>
17    {/each}
18  </ul>
19</main>

ここでは詳細ページへのリンクとして、HTML標準の <a> 要素を利用しています。
詳細については、SvelteKitの Link options をご確認ください。

http://localhost:5173/ にアクセスして、以下のように投稿一覧が表示されれば成功です。
sveltekit-blog2.png

5. 詳細ページの作成

5-1. 投稿詳細を取得する

SvelteKitでは src/routes/blog/[slug] のようにしてディレクトリ名に角括弧を使うことで動的なルートを作成します。

※ ルーティングの詳細については、SvelteKitの ルーティング のドキュメントをご確認ください。

ここでは、/articles/:slug/articles/article-1 など)のパスで投稿の詳細を表示したいので、src/routes/articles/[slug] のディレクトリを作成します。
+page.server.ts を作成し、load 関数を定義します。

src/routes/articles/[slug]/+page.server.ts
1import { newtClient } from '$lib/server/newt'
2import type { Article } from '$lib/server/newt'
3import type { PageServerLoad } from './$types'
4
5export const load: PageServerLoad = async ({ params }) => {
6  const article = await newtClient.getFirstContent<Article>({
7    appUid: 'blog',
8    modelUid: 'article',
9    query: {
10      slug: params.slug,
11      select: ['_id', 'title', 'slug', 'body']
12    }
13  })
14  return {
15    article
16  }
17}

投稿詳細を取得するメソッドとして、SDKが提供している getFirstContent を利用しています。このメソッドはクエリに該当するコンテンツのうち、最初の1件を返却するメソッドです。指定したスラッグのコンテンツを取得したい場合は、このメソッドを利用します。

5-2. 投稿詳細を表示する

5-1で取得した article を利用して、投稿詳細を表示します。

src/routes/articles/[slug]/+page.svelte
1<script lang="ts">
2  import type { PageData } from './$types'
3  export let data: PageData
4</script>
5
6<svelte:head>
7  <title>{data.article?.title}</title>
8  <meta name="description" content="投稿詳細ページです" />
9</svelte:head>
10
11<main>
12  <h1>{data.article?.title}</h1>
13  <div>{@html data.article?.body}</div>
14</main>

※ bodyの表示で利用されている @html はXSSの危険性があるため、利用には注意が必要です。ここでは、Newtで管理している投稿情報を表示するものであり、不特定多数のユーザーが入力できるものを表示するわけではないため、安全なものとして利用しています。

これで、投稿詳細についての設定も完了です。
http://localhost:5173/articles/article-3 にアクセスして、以下のように投稿詳細が表示されれば成功です。
sveltekit-blog3.png

6. アダプターの設定

静的サイト生成(Static Site Generation)するためのアダプター設定を行います。
はじめに @sveltejs/adapter-static をインストールします。

npm install -D @sveltejs/adapter-static
# or
yarn add -D @sveltejs/adapter-static

次に、svelte.config.jsadapter を以下のように設定します。
各オプションの詳細はSvelteKitの Static site generation のドキュメントをご確認ください。

svelte.config.js
import adapter from '@sveltejs/adapter-auto'
import adapter from '@sveltejs/adapter-static'
import { vitePreprocess } from '@sveltejs/kit/vite'

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Consult https://kit.svelte.dev/docs/integrations#preprocessors
  // for more information about preprocessors
  preprocess: vitePreprocess(),

  kit: {
    // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
    // If your environment is not supported or you settled on a specific environment, switch out the adapter.
    // See https://kit.svelte.dev/docs/adapters for more information about adapters.
    adapter: adapter()
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: null,
      precompress: false,
      strict: true
    })
  }
}

export default config

また、デプロイ先がVercelの場合は、ゼロコンフィグサポート があります。
以下のように adapter のオプションを省略すると、adapter-static が最適な設定を提供できるようになります。

svelte.config.js
1import adapter from '@sveltejs/adapter-static'
2import { vitePreprocess } from '@sveltejs/kit/vite'
3
4/** @type {import('@sveltejs/kit').Config} */
5const config = {
6  // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7  // for more information about preprocessors
8  preprocess: vitePreprocess(),
9
10  kit: {
11    // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12    // If your environment is not supported or you settled on a specific environment, switch out the adapter.
13    // See https://kit.svelte.dev/docs/adapters for more information about adapters.
14    adapter: adapter()
15  }
16}
17
18export default config

最後に prerender オプションを最上位のレイアウトに追加します。
src/routes/+layout.ts を作成し、以下の内容を記載します。

src/routes/+layout.ts
1export const prerender = true

これでサイト全体を、静的なファイルのコレクションとしてプリレンダリングできるようになりました。

次のステップ

このチュートリアルを行うことで、SvelteKitのプロジェクトを作成し、開発環境でコンテンツの取得・表示を行う方法を学習しました。
更に深く学習したい方は、以下のチュートリアルもおすすめです。

ホスティングを行いたい方

問い合わせフォームを作成したい方

その他にも様々なチュートリアルを用意しているので、ぜひ チュートリアル のページもご確認ください。

NewtMade in Newt