Aを構築するアプリケーションを行うと雲の硝酸


概要


Nitric クラウドアプリケーションを急速に開発し、展開するためのServerlessなフレームワークです.このガイドでは、次を使用して簡単なリストを作成する方法を示します.窒素関数によるAPIを持つJSまた、データの永続性のコレクションを使用します.もとはhere 硝酸サイト
ガイドでは次のように使います.
  • 窒素API,機能およびコレクション
  • 次.js
  • フロントウィンドスタイリングのためのTarwindCSS
  • お好みの雲
  • AWS
  • GCP
  • 紺碧
  • 必要条件

  • ノード.js
  • 次.js
  • 硝酸カリ
  • 次から始めなさい。jsアプリ


    我々は、完成品を起動し、そのように沿って従って、ちょうどクローンNitric to-do Githubのプロジェクトオフ
    git clone https://github.com/nitrictech/nitric-todo.git
    
    NPMや糸で依存関係をインストールします.
    cd nitric-todo
    yarn install
    
    次に、選択したエディタでプロジェクトを開きます.
    code .
    

    プロジェクト構造


    プロジェクトは2つの主要な領域に分割されます.

  • これは窒素APIが格納されている場所です

  • Web -これはあなたの次です.アプリケーションを格納
  • タイピング


    我々のタスクとAPIの要求/応答のいくつかのタイピングを追加します.
    // todo-api/types.ts
    
    /* Base Types */
    export interface Task {
      id: string;
      createdAt: number;
      name: string;
      complete: boolean;
      description?: string;
      dueDate?: number;
    }
    
    export interface TaskList {
      id: string;
      createdAt: number;
      name: string;
      tasks: Task[];
    }
    
    /* Task List */
    export type Filters = Partial<Task>;
    
    export type TaskListResponse = TaskList;
    
    export type TaskListRequest = Omit<TaskList, 'id' | 'tasks'>;
    
    export type TaskListPostRequest = Omit<TaskList, 'id' | 'complete'>;
    
    /* Task Post */
    export type TaskPostRequest = Omit<Task, 'id'>;
    
    /* Task Update */
    export type TaskPatchRequest = { completed: boolean };
    

    資源


    アプリケーションは、コード内のリソースを定義してビルドすると、任意のルートでこれを書くことができます.js or .ts ファイルが、組織のために我々は一緒に入れをお勧めします.では、新しいAPIをサポートするために必要なリソースを定義することから始めましょうresources ディレクトリ.
    // todo-api/resources/apis.ts
    
    import { api } from '@nitric/sdk';
    
    export const taskListApi = api('taskList');
    
    その後、我々のタスクリストを格納するために私たちのコレクションを作成したい.我々は、代わりにサブコレクションとしてそれらを格納できるようにタスクを省略します.これは将来的に個々のタスクを問い合わせるのを容易にします.
    // todo-api/resources/collections.ts
    
    import { collections } from '@nitric/sdk';
    import { TaskList } from 'types';
    
    type TaskCollection = Omit<TaskList, 'tasks'>;
    
    export const taskListCol = collection<TaskCollection>('taskLists');
    

    路線


    APIルートを設定し始めると、これらを空にするまで空の関数として残ります.
    // todo-api/functions/tasks.ts
    
    import { taskListApi } from '../resources/apis.ts';
    
    taskListApi.get("/:listid/:id", async (ctx) => {});      // Get task with [id]
    taskListApi.get("/:listid", async (ctx) => {);           // Get task list with [id]
    taskListApi.get("/", async (ctx) => {});                 // Get all task lists
    taskListApi.post("/:listid", async (ctx) => {});         // Post new task for task list
    taskListApi.post("/", async (ctx) => {});                // Post new task list
    taskListApi.patch("/:listid/:id", async (ctx) => {});    // Update task
    taskListApi.delete("/:listid", async (ctx) => {});       // Delete task list
    taskListApi.delete("/:listid/:id", async (ctx) => {});   // Delete task
    
    それから、我々は前のコレクションを得ることができて、この機能の範囲内で使用のためにそれに許可を適用することができます.
    // todo-api/functions/tasks.ts
    
    import { taskListCol } from '../resources/collections.ts';
    
    const taskLists = taskListCol.for('reading', 'writing', 'deleting');
    
    今我々はコレクションを持って、我々はタスクやタスクのリストを追加を開始することができます.私たちは私たちのコレクションを使用して、私たちのタスクのリストを格納するために、各タスクのリスト上のサブコレクション私たちのタスクを格納します.
    我々は、最初の新しいタスクのリストを使用してPOST / 終点
    // todo-api/functions/tasks.ts
    
    taskListApi.post('/', async (ctx) => {
      const { name, tasks } = ctx.req.json() as TaskListPostRequest;
    
      try {
        if (!name) {
          ctx.res.body = 'A new task list requires a name';
          ctx.res.status = 400;
          return;
        }
    
        const id = uuid.generate();
    
        await taskLists.doc(id).set({
          id,
          name,
          createdAt: new Date().getTime(),
        });
    
        // add any tasks if supplied
        if (tasks) {
          for (const task of tasks) {
            const taskId = uuid.generate();
            await taskLists
              .doc(id)
              .collection<Task>('tasks')
              .doc(taskId)
              .set({
                ...task,
                complete: false,
                createdAt: new Date().getTime(),
              });
          }
        }
    
        ctx.res.body = 'Successfully added task list!';
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to add task list';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    使用POST /:listid タスクリストのサブコレクションに新しいドキュメントを追加することで、タスクリストに新しいタスクを置くことができます.
    まず、タスクリストIDを受け取り、listid -> tasks サブコレクション.
    // todo-api/functions/tasks.ts
    
    taskListApi.post('/:listid', async (ctx) => {
      const { listid } = ctx.req.params;
      const task = ctx.req.json() as TaskPostRequest;
    
      try {
        if (!listid) {
          ctx.res.body = 'A task list id is required';
          ctx.res.status = 400;
          return;
        }
    
        if (!task || !task.name) {
          ctx.res.body = 'A task with a name is required';
          ctx.res.status = 400;
          return;
        }
    
        const taskId = uuid.generate();
    
        await taskLists
          .doc(listid)
          .collection<Omit<Task, 'id'>>('tasks')
          .doc(taskId)
          .set({
            ...task,
            complete: false,
            createdAt: new Date().getTime(),
          });
    
        ctx.res.body = 'Successfully added task!';
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to add task list';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    GET / すべてのタスクのリストとそのタスクを返します.
    // todo-api/functions/tasks.ts
    
    import { sortByCreatedAt } from "../common/utils";
    ...
    taskListApi.get("/", async (ctx) => {
      try {
        const taskList = await taskLists.query().fetch();
    
        const taskListsWithTasks = await Promise.all(
          taskList.documents.map(async (doc) => {
            const { documents: tasks } = await taskLists
              .doc(doc.id)
              .collection<Task>("tasks")
              .query()
              .fetch();
    
            return {
              id: doc.id,
              ...doc.content,
              tasks: tasks
                .map(({ id, content }) => ({ id, ...content }))
                .sort(sortByCreatedAt),
            };
          })
        );
    
        ctx.res.json(taskListsWithTasks.sort(sortByCreatedAt));
      } catch (err) {
        console.log(err);
        ctx.res.body = "Failed to retrieve taskList list";
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    // todo-api/common/utils.ts
    
    import { Task } from 'types';
    
    type CreatedAtData = Pick<Task, 'createdAt'>;
    
    export const sortByCreatedAt = (a: CreatedAtData, b: CreatedAtData) => {
      return a.createdAt < b.createdAt ? 1 : -1;
    };
    
    GET /:listid エンドポイントは、単一のリストを取得することができますし、タスクのクエリにフィルタを適用します.
    // todo-api/functions/tasks.ts
    
    // Get all tasks from a task list, with filters
    taskListApi.get('/:listid', async (ctx) => {
      const { listid } = ctx.req.params;
      const filters = ctx.req.query as Filters;
    
      try {
        const taskListRef = taskLists.doc(listid);
        let query = taskListRef.collection<Task>('tasks').query();
    
        // Apply filters to query before executing query;
        Object.entries(filters).forEach(([k, v]) => {
          switch (k) {
            case 'complete': {
              query = query.where(k, '==', v === 'true');
              break;
            }
            case 'dueDate': {
              query = query.where(k, '>=', v);
              break;
            }
            default: {
              query = query.where(k, 'startsWith', v as string);
              break;
            }
          }
        });
    
        const taskList = await taskListRef.get();
        const tasks = await query.fetch();
    
        ctx.res.json({
          ...taskList,
          tasks: tasks.documents
            .map((doc) => ({ id: doc.id, ...doc.content }))
            .sort(sortByCreatedAt),
        });
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to retrieve tasks';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    中でGET /:listid/:id エンドポイントは、特定のリストから特定のタスクを取得し始めることができます.
    // todo-api/functions/tasks.ts
    
    taskListApi.get('/:listid/:id', async (ctx) => {
      const { listid, id } = ctx.req.params;
    
      try {
        // Get our task list with id [listId]
        const taskListRef = taskListCol.doc(listid);
        // Get all tasks from the collection with id [id]
        const task = await taskListRef.collection<Task>('tasks').doc(id).get();
    
        ctx.res.json(task);
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to retrieve tasks';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    内部PATCH :listid/:id パッチルートは、タスクが完了したかどうかを更新するロジックを書くことができます.
    // todo-api/functions/tasks.ts
    
    taskListApi.patch('/:listid/:id', async (ctx) => {
      const { listid: listId, id } = ctx.req.params;
      const { completed } = ctx.req.json() as ToggleRequest;
    
      try {
        const taskListRef = taskLists.doc(listId);
        const taskRef = taskListRef.collection<Task>('tasks').doc(id);
        const originalTask = await taskRef.get();
    
        await taskListRef
          .collection<Task>('tasks')
          .doc(id)
          .set({
            ...originalTask,
            complete: completed,
          });
    
        ctx.res.body = 'Successfully updated task';
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to retrieve tasks';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    削除ルートDELETE /:listid/:id and DELETE /:id 関連するドキュメントを取得し、コレクションから削除します.
    // todo-api/functions/tasks.ts
    
    taskListApi.delete('/:listid/:id', async (ctx) => {
      const { listid: listId, id } = ctx.req.params;
    
      try {
        const taskListRef = taskLists.doc(listId);
        await taskListRef.collection('tasks').doc(id).delete();
        ctx.res.body = 'Successfully deleted task';
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to delete task';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    
    taskListApi.delete('/:id', async (ctx) => {
      const { id } = ctx.req.params;
    
      try {
        await taskLists.doc(id).delete();
        ctx.res.body = 'Successfully deleted task list';
      } catch (err) {
        console.log(err);
        ctx.res.body = 'Failed to delete task list';
        ctx.res.status = 400;
      }
    
      return ctx;
    });
    

    APIプロキシの設定


    あなたの作成.env ファイル名の変更.env.example ファイル
    mv web/.env.example web/.env
    
    内部next.config.js 次のプロキシ間でプロキシに定義された書き換えを行う必要があります.JS APIルートとあなたの硝酸アピール.それはAPI_BASE_URL に定義されている変数.env ファイル.
    // web/next.config.js
    
    module.exports = {
      reactStrictMode: true,
      api: {
        bodyParser: {
          bodyParser: false, // Disallow body parsing, consume as stream
        },
      },
      // To avoid any CORs issues use Next.js as a proxy for Nitric API
      // We are working on it :)
      async rewrites() {
        return [
          {
            source: '/apis/:path*',
            destination: `${process.env.API_BASE_URL}/apis/:path*`, // Proxy to Backend
          },
        ];
      },
    };
    

    ローカルに動く


    ローカルAPIをテストするには、次のようにします.
    cd todo-api
    nitric run
    
    我々は次を起動することができます.JSフロントエンド
    cd ../web
    yarn dev
    

    展開


    窒素APIの配備


    あなたの資格情報と他のどのクラウド特有の設定を設定してください.



  • 展開コマンドを実行します.

    Warning: Publishing services to the cloud may incur costs.


    nitric stack up -s todo
    
    展開が完了すると、関連クラウドコンソールに移動し、APIを参照して対話することができます.

    When you're done, you can destroy the stack with nitric down -s todo


    次を展開します。jsアプリ


    次の配備ボタンのいずれかを選択し、API_BASE_URL 展開されたAPI URLでこの設定プロセス中に変数を設定します.

    展開する



    Netlifyに配備する


    注意:Netlify.toml このリポジトリのファイルには、API_BASE_URL 初期配置のプロパティ.