Full-Stack Sapper & SQLite with Sequelize Pt.1

インターネット上には、ExpressのデータベースとしてMongoDB/Mongooseを使用するためのチュートリアルがたくさんあります。私は関係データベースを探求することに決めました。最初に思い浮かんだのがSQLiteでした。そして驚いたことに、Sapperで動作する有益なものは何も見つかりませんでした。

このチュートリアルは2部構成で、第1部はExpressバックエンド、SQLiteデータベース、コントローラー、その他諸々に関するもので、第2部はSvelteフロントエンドをSapperに統合する方法を案内します。

これが、これら二つのミニマルでありながら強力な技術を統合した最終結果です。試してみてください。

npx degit gevera/sapper_sqlite_crud
npm i
npm run dev


最初から作成しましょう

デフォルトのSapperプロジェクトを作成し、TypeScriptに変換し、必要な依存関係をインストールします。

npx degit "sveltejs/sapper-template#rollup" sapper_sqlite
cd sapper_sqlite
node scripts/setupTypeScript.js
npm i

まず最初に依存関係を追加しましょう。デフォルトのpolkaではなくExpress、そしてSQLiteデータベースとsequelize ORMを使用します。

npm i express sqlite3 sequelize
npm remove polka @types/polka

それらの型も追加しましょう。

npm i -D @types/express @types/node @types/sqlite3 @types/sequelize

srcフォルダ内にdbフォルダを作成し、その中にsqliteDb.tsファイルを作成します。ここでsequelizeをインポートし、sequelize自身と、./src/dbフォルダのdatabase.sqliteでデータベースを初期化する非同期db関数をエクスポートします。この関数は後でサーバーファイルを変更するときに使用します。

import { Sequelize } from 'sequelize';

export const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: './src/db/database.sqlite',
});

export const db = async () => {
    try {
        await sequelize.authenticate();
        console.log('Connection to sqliteDB has been established successfully.');
    } catch (error) {
        console.error('Unable to connect to the database:', error);
    }
};

次に、User.tsファイルをModelsフォルダ内に作成して、ユーザーモデルを作ります。

import { UUIDV4, DataTypes } from "sequelize";
import { sequelize } from "../sqliteDb";

export const UserModel = sequelize.define("User", {
  id: {
    type: DataTypes.UUID,
    defaultValue: UUIDV4,
    primaryKey: true,
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true,
  },
  profession: {
    type: DataTypes.STRING,
    defaultValue: "programmer",
  },
});

ユニークなプライマリーIDを各ユーザーに生成するためにUUIDV4をインポートしていることに注意してください。これはmongooseスキーマにとても似ています。

リクエストを処理するコントローラーが必要なので、src内にcontrollersディレクトリを作成し、userControllers.tsファイルを追加しましょう。ここで私たちのサーバーとデータベースとの間の魔法が起こります。なぜか、私は個人的にmongooseよりもsequelizeの構文の方が好きです。それは直感的でクリーンだと思います。

import { UserModel } from '../db/Models/User';
import { sequelize } from '../db/sqliteDb';

export const getAllUsers = async (req, res, next) => {
    // 中略
}

export const createAUser = async (req, res, next) => {
    // 中略
}

export const getAUser = async (req, res, next) => {
    // 中略
}

export const deleteAUser = async (req, res, next) => {
    // 中略
}

export const updateAUser  = async (req, res, next) => {
    // 中略
}

最後にsrcディレクトリ内のserver.tsファイルに切り替えましょう。
polkaからexpressに変更します。db関数をインポートし実行します。また、post/putリクエストの本文を解析するためにjsonとurlencodedを有効にする必要があります。
先に作成したコントローラーを取得し、対応するルートに適用します。

import sirv from "sirv";
import express from "express";
import compression from "compression";
import * as sapper from "@sapper/server";
import { db } from "./db/sqliteDb";
import {
    getAllUsers,
    createAUser,
    getAUser,
    updateAUser,
    deleteAUser
} from './controllers/userControllers';

const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === "development";

db();

const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/api', getAllUsers);
app.post('/api', createAUser);
app.get('/api/:id', getAUser);
app.put('/api/:id', updateAUser);
app.delete('/api/:id', deleteAUser);

app
  .use(
    compression({ threshold: 0 }),
    sirv("static", { dev }),
    sapper.middleware()
  )
  .listen(PORT, () => {
    console.log("Express is up and running!");
  });

最初の実行時、dbフォルダ内にdatabase.sqliteファイルが作成されます。http://localhost:3000/apiにアクセスすると、

{
  "success": true,
  "data": []
}

と表示されます。

やりましたね!私たちのSapperアプリには、今や持続的なデータベースと完全なCRUD機能を持つREST APIがあります。

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/gevera/full-stack-sapper-sqlite-with-sequelize-pt-1-13d3