Search by

    Terakhir diperbaharui: Jul 1, 2021

    Membuat ulang Aplikasi DinoTes #1

    1. Setup Next.js

    Kita gunakan tool yang mirip dengan Create React App yaitu Create Next App untuk membuat sebuah project Next.js yang baru.

    1$ npx create-next-app dinotes
    2# or
    3$ yarn create next-app dinotes

    Gunakan perintah berikut untuk mengeksekusi aplikasi Next.js yang barus saja dibuat.

    1$ yarn run dev

    Hasilnya:

    dinotes nextjs run success

    2. Setup Tailwind CSS

    Untuk urusan styling kita gunakan CSS library yang sama dengan yang digunakan di aplikasi DinoTes sebelumnya yaitu Tailwind CSS. Tetapi tanpa menambahkan library styled-components.

    Install Tailwind CSS dan package yang dibutuhkan.

    1$ npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
    2
    3atau
    4
    5$ yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest

    Selanjutnya kita perlu membuat file yang berisi informasi konfigurasi dari tailwind.

    Jalankan perintah berikut:

    1npx tailwindcss init -p

    Hasilnya adalah dua file konfigurasi untuk Tailwind can PostCSS.

    dinotes tailwindcss init

    Update konfigurasi menjadi seperti ini agar Tailwind menghapus style yang tidak digunakan ketika berada di production environment.

    1// tailwind.config.js
    2module.exports = {
    3 purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
    4 darkMode: false,
    5 theme: {
    6 extend: {}
    7 },
    8 variants: {
    9 extend: {}
    10 },
    11 plugins: []
    12};

    Langkah terakhir import Tailwind ke dalam project.

    1// pages/_app.js
    2
    3import 'tailwindcss/tailwind.css';
    4
    5function MyApp({ Component, pageProps }) {
    6 return <Component {...pageProps} />;
    7}
    8
    9export default MyApp;

    Ganti isi dari file styles/global.css menjadi seperti ini:

    1/* ./styles/globals.css */
    2@tailwind base;
    3@tailwind components;
    4@tailwind utilities;

    Jalankan perintah yarn run dev dari terminal dan akses alamat http://localhost:3000, jika tidak ada kendala kita akan mendapatkan tampilan seperti ini:

    dinotes nextjs tailwindcss

    3. Setup Backend AWS Amplify

    Dengan menggunakan AWS Amplify kita bisa tambahkan bagian backend aplikasi dengan mudah, backend disini meliputi API, database, autentikasi dll.

    Dari root folder aplikasi DinoTes jalankan perintah berikut ini:

    1$ amplify init

    Proses konfigurasi amplify dilakukan dengan menjawab beberapa pertanyaan:

    dinotes run amplify init

    Untuk melihat backend yang sudah dibuat melalui perintah amplify init bisa dilakukan dengan cara login ke AWS Management Console dan pilih services - AWS Amplify

    dinotes select aws amplify

    dinotes amplify success created

    Perintah amplify init secara otomatis akan membuat sebuah folder dengan nama amplify di dalam folder project yang berisi file backend.

    dinotes amplify folder created

    Install Amplify Libraries

    Install beberapa library yang akan digunakan pada proses pembuatan aplikasi.

    1npm install aws-amplify @aws-amplify/ui-react
    2
    3//atau
    4
    5yarn add aws-amplify @aws-amplify/ui-react

    4. Layout

    Pada bagian ini kita akan membuat layout tampilan aplikasi DinoTes.

    Layout adalah kumpulan shared component yang muncul di setiap page. Untuk membuat layout di Next.js kita buat sebuah folder baru bernama components.

    Sehingga susunan folder menjadi seperti ini:

    dinotes folder structure 1

    Isi dari Layout adalah Header, Footer dan children component yang mewakili component lain.

    Buat sebuah file baru bernama Header.js di dalam folder components kemudian tambahkan code berikut:

    components/Header.js

    1import React, { useState } from 'react';
    2
    3const Header = () => {
    4 return (
    5 <>
    6 <div className="flex justify-between items-center border-b-2 border-gray-100 py-6 md:justify-start md:space-x-3">
    7 <img className="h-14 w-auto sm:h-16" src="/header-logo.png" alt="logo" />
    8 </div>
    9 </>
    10 );
    11};
    12
    13export default Header;

    Tempatkan file header-logo.png di dalam folder public agar bisa diakses.

    dinotes header logo public

    Buat sebuah file di dalam folder components bernama Footer.js dan tambahkan code berikut:

    components/Footer.js

    1import React from 'react';
    2
    3const Footer = () => {
    4 return (
    5 <div className="m-4 p-2">
    6 <p>
    7 <a href="https://devsaurus.com">devsaurus</a> &copy; 2021
    8 </p>
    9 </div>
    10 );
    11};
    12
    13export default footer;

    Selanjutnya buat sebuah file bernama Layout.js dan import component Header & Footer ke dalam file tersebut.

    components/Layout.js

    1import Header from './Header';
    2import Footer from './Footer';
    3
    4const Layout = ({ children }) => {
    5 return (
    6 <div className="max-w-7xl mx-auto px-4 sm:px-6">
    7 <Header />
    8 {children}
    9 <Footer />
    10 </div>
    11 );
    12};
    13
    14export default Layout;

    Ganti isi dari file index.js menjadi seperti ini:

    1import Layout from '../components/Layout';
    2
    3const Home = () => (
    4 <div className="text-center">
    5 <Layout />
    6 </div>
    7);
    8
    9export default Home;

    Hasil Akhir:

    dinotes result layout

    5. Add New Note Page

    Halaman atau page pertama yang kita buat adalah halaman untuk menambahkan note atau catatan baru.

    Step by step

    Buat sebuah file baru bernama add.js di dalam folder pages.

    Kemudian tambahkan code berikut:

    pages/add.js

    1import React from 'react';
    2
    3import Layout from '../components/Layout';
    4
    5const Add = () => {
    6 return (
    7 <Layout>
    8 <div className="flex flex-col items-center justify-center m-4">
    9 <p>This is Add Page</p>
    10 </div>
    11 </Layout>
    12 );
    13};
    14
    15export default Add;

    Karena Next.js menggunakan sistem routing berdasarkan pages maka kita bisa akses halaman ini menggunakan alamat http://localhost:3000/add.

    Setiap file yang ada di dalam folder pages dapat diakses menggunakan alamat yang sama dengan nama file.

    Buat Component

    Selanjutnya buat component utama dari halaman Add. Component utama ini berupa form yang berfungsi untuk menambahkan data note yang baru.

    Buat sebuah file bernama AddNoteForm.js di dalam folder components.

    Kita gunakan form library Formik untuk mempermudah kita dalam menghandle React Form. Library formik ini sudah pernah digunakan saat proses implementasi sistem autentikasi.

    Install Formik:

    1$ npm install formik
    2
    3// atau
    4
    5$ yarn add formik

    Selain itu kita juga akan tambahkan package fontawesome untuk icon.

    1$ npm i --save @fortawesome/fontawesome-svg-core
    2$ npm install --save @fortawesome/free-solid-svg-icons
    3$ npm install --save @fortawesome/react-fontawesome
    4
    5//atau
    6
    7$ yarn add @fortawesome/fontawesome-svg-core
    8$ yarn add @fortawesome/free-solid-svg-icons
    9$ yarn add @fortawesome/react-fontawesome

    Di dalam file AddNoteForm.js tambahkan code berikut ini:

    1import React from 'react';
    2import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
    3import { faSave } from '@fortawesome/free-solid-svg-icons';
    4import { useFormik } from 'formik';
    5
    6const AddNoteForm = () => {
    7 const formik = useFormik({
    8 initialValues: {
    9 title: '',
    10 content: ''
    11 },
    12 onSubmit: async (values) => {
    13 setIsSuccess(true);
    14 }
    15 });
    16 return (
    17 <>
    18 <form className="flex flex-col w-4/5" onSubmit={formik.handleSubmit}>
    19 <div className="flex flex-col">
    20 <input
    21 className="my-8 p-2 text-xl font-bold w-full focus:outline-none focus:ring focus:border-blue-300"
    22 type="text"
    23 name="title"
    24 placeholder="Title"
    25 onChange={formik.handleChange}
    26 value={formik.values.title}
    27 ></input>
    28 <textarea
    29 className="resize-y mb-8 p-2 focus:outline-none focus:ring focus:border-blue-300"
    30 name="content"
    31 rows="12"
    32 placeholder="Your content goes here.."
    33 onChange={formik.handleChange}
    34 value={formik.values.content}
    35 ></textarea>
    36 </div>
    37 <div className="flex justify-end">
    38 <button className="bg-purple-700 text-white text-base m-2 p-3 border rounded-md" type="submit">
    39 <FontAwesomeIcon icon={faSave} /> &nbsp; Save
    40 </button>
    41 </div>
    42 </form>
    43 </>
    44 );
    45};
    46
    47export default AddNoteForm;

    Import component AddNoteForm pada file pages/add.js.

    1import React from 'react';
    2
    3import Layout from '../components/Layout';
    4import AddNoteForm from '../components/AddNoteForm';
    5
    6const Add = () => {
    7 return (
    8 <Layout>
    9 <div className="flex flex-col items-center justify-center m-4">
    10 <AddNoteForm />
    11 </div>
    12 </Layout>
    13 );
    14};
    15
    16export default Add;

    Hasilnya:

    dinotes amplify addnoteform

    Menambah API

    Seperti yang sudah dijelaskan sebelumnya, kita bisa menggunakan AWS Amplify untuk menambah API yang sudah terkoneksi dengan database ke dalam aplikasi DinoTes.

    Jalankan perintah berikut di terminal:

    1amplify add api

    Kemudian kita perlu jawab beberapa pertanyaaan untuk konfigurasi awal

    1? Please select from one of the below mentioned services:
    2# GraphQL
    3? Provide API name:
    4# dinotes
    5? Choose the default authorization type for the API:
    6# API key
    7? Enter a description for the API key:
    8#
    9? After how many days from now the API key should expire (1-365):
    10# 7
    11? Do you want to configure advanced settings for the GraphQL API?
    12# Yes, I want to make some additional changes.
    13? Configure additional auth types?
    14# Yes
    15? Choose the additional authorization types you want to configure for the API:
    16# Amazon Cognito User Pool
    17? Do you want to use the default authentication and security configuration?
    18# Default configuration
    19? How do you want users to be able to sign in?
    20# Username
    21? Do you want to configure advanced settings?
    22# No, I am done.
    23? Enable conflict detection?
    24# No
    25? Do you have an annotated GraphQL schema?
    26# No
    27? Choose a schema template:
    28# Single object with fields (e.g., “Todo” with ID, name, description)
    29? Do you want to edit the schema now?
    30# Yes

    Secara garis besar kita akan gunakan GraphQL dengan mode autentikasi API key dan Amazon Cognito User Pool.

    Apa itu Amazon Cognito User Pool?

    Amazon Cognito User Pool adalah direktori user yang ada di dalam layanan Amazon Cognito. Dengan menggunakan user pool, user bisa login ke aplikasi DinoTes menggunakan Amazon Cognito.

    Amazon Cognito sendiri merupakan layanan milik AWS untuk membantu menghandle proses autentikasi.

    Langkah berikutnya kita perlu mengubah graphql schema yang sebelumnya tergenerate secara otomatis saat proses menambahkan api.

    Schema diubah menyesuaikan bentuk data yang ingin disimpan pada aplikasi DinoTes.

    Lokasi file: amplify/backend/api/dinotes/schema.graphql

    Dari

    1type Todo @model {
    2 id: ID!
    3 name: String!
    4 description: String
    5}

    Menjadi

    1type Note
    2 @model
    3 @auth(rules: [{ allow: owner }, { allow: public }]) {
    4 id: ID!
    5 username: String
    6 title: String!
    7 content: String
    8 createdAt: AWSDateTime
    9 updatedAt: AWSDateTime
    10}

    Yang perlu kita ketahui pada perubahan schema diatas adalah directive @auth yang berfungsi untuk mengatur mekanisme autentikasi pada GraphQL API.

    @auth(rules: [{ allow: owner }, { allow: public }] berarti yang memiliki akses ke GraphQL API adalah:

    • owner, setiap user yang sign in menggunakan Amazon Cognito User Pool
    • public, terbuka untuk publik namun harus menggunakan API key

    Untuk lebih lengkapnya kamu bisa lihat di sini.

    Deploy API

    API yang sudah ditambahkan masih bersifat lokal, kita perlu deploy API tersebut ke AWS.

    Untuk melakukannya jalankan perintah berikut:

    1amplify push
    1Current Environment: dev
    2
    3| Category | Resource name | Operation | Provider plugin |
    4| -------- | --------------------- | --------- | ----------------- |
    5| Auth | dinotese043671b | Create | awscloudformation |
    6| Api | dinotes | Create | awscloudformation |
    7? Are you sure you want to continue? Y
    8
    9# You will be walked through the following questions for GraphQL code generation
    10? Do you want to generate code for your newly created GraphQL API? Y
    11? Choose the code generation language target: javascript
    12? Enter the file name pattern of graphql queries, mutations and subscriptions: graphql/**/*.js
    13? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions? Y
    14? Enter maximum statement depth [increase from default if your schema is deeply nested]: 2

    Proses deploy API membutuhkan waktu beberapa menit.

    Setelah berhasil kita bisa lihat hasilnya di AWS Amplify Console.

    dinotes amplify console push api

    Atau melihat statusnya dengan menjalankan perintah amplify status.

    1➜ dinotes git:(main) ✗ amplify status
    2
    3Current Environment: dev
    4
    5| Category | Resource name | Operation | Provider plugin |
    6| -------- | --------------- | --------- | ----------------- |
    7| Auth | dinotese043671b | No Change | awscloudformation |
    8| Api | dinotes | No Change | awscloudformation |

    Jika kita lihat pada AWS Amplify Console, API pada AWS Amplify terdiri dari dua bagian, yaitu GraphQL API yang terhubung dengan layanan AWS AppSync serta Data Source yang merupakan kumpulan table di DynamoDB.

    dinotes amplify API

    Dengan kata lain saat ini aplikasi DinoTes menggunakan AppSync untuk keperluan API dan DynamoDB untuk database.

    Admin UI

    Kita bisa mendapatkan tampilan visual dari backend DinoTes dengan cara mengaktifkan fitur admin UI.

    dinotes setup admin ui

    dinotes setup admin ui 2

    Untuk mengakses admin UI klik button "Open Admin UI" dari AWS Amplify console.

    dinotes open admin ui

    Tampilan Admin UI

    dinotes amplify admin ui

    Dengan Admin UI kita bisa mengelola semua layanan yang terhubung dengan backend dari aplikasi DinoTes dalam tampilan mirip dashboard.

    Sebagai contoh pada menu GraphQL API kita akan temukan API yang baru saja dideploy.

    dinotes amplify admin ui graphql api

    Disini ada yang perlu diperhatikan, menu Data pada Admin UI berfungsi untuk mengelola data yang berasal dari Data Store sedangkan layanan AppSync secara default terhubung dengan DynamoDB, untuk melihat data yang tersimpan oleh AppSync kita harus melihatnya langsung di DynamoDB.

    Oleh karena itu jika kita melihat menu Data pada Admin UI kita tidak akan temukan data yang disimpan oleh API.

    Integrasi API

    Pada bagian ini kita akan update component AddNoteForm agar bisa menyimpan note baru ke dalam database.

    Sebelumnya kita perlu membuat component untuk menghandle feedback dari form.

    Buat file dengan nama Message.js di dalam folder components dan isi dengan code berikut:

    1import React from 'react';
    2
    3const Message = (props) => {
    4 const { text, type } = props;
    5
    6 return (
    7 <>
    8 {type === 'error' ? (
    9 <div className="flex flex-col items-center justify-center m-4 p-4 border-2 border-red-500 rounded">
    10 <p>{text}</p>
    11 </div>
    12 ) : (
    13 <div className="flex flex-col items-center justify-center m-4 p-4 border-2 border-green-500 rounded">
    14 <p>{text}</p>
    15 </div>
    16 )}
    17 </>
    18 );
    19};
    20
    21export default Message;

    Update file AddNoteForm.js dengan code berikut ini:

    1import React, { useState } from 'react';
    2import { API } from '@aws-amplify/api';
    3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
    4import { faSave } from '@fortawesome/free-solid-svg-icons';
    5import { useFormik } from 'formik';
    6
    7import { createNote } from '../graphql/mutations';
    8import Message from './Message';
    9
    10const InfoWrapper = (props) => {
    11 const { status } = props;
    12
    13 if (status !== null) {
    14 if (status === false) {
    15 return <Message type="error" text="Title cannot be empty" />;
    16 }
    17 return <Message type="success" text="Data successfully saved" />;
    18 }
    19 return <></>;
    20};
    21
    22const AddNoteForm = () => {
    23 const [isSuccess, setIsSuccess] = useState(null);
    24
    25 const formik = useFormik({
    26 initialValues: {
    27 title: '',
    28 content: ''
    29 },
    30 onSubmit: async (values) => {
    31 const { data } = await API.graphql({
    32 authMode: 'API_KEY',
    33 query: createNote,
    34 variables: {
    35 input: {
    36 title: values.title,
    37 content: values.content
    38 }
    39 }
    40 });
    41 console.log(data.createNote.id);
    42 setIsSuccess(true);
    43 }
    44 });
    45 return (
    46 <>
    47 <InfoWrapper status={isSuccess} />
    48 <form className="flex flex-col w-4/5" onSubmit={formik.handleSubmit}>
    49 <div className="flex flex-col">
    50 <input
    51 className="my-8 p-2 text-xl font-bold w-full focus:outline-none focus:ring focus:border-blue-300"
    52 type="text"
    53 name="title"
    54 placeholder="Title"
    55 onChange={formik.handleChange}
    56 value={formik.values.title}
    57 ></input>
    58 <textarea
    59 className="resize-y mb-8 p-2 focus:outline-none focus:ring focus:border-blue-300"
    60 name="content"
    61 rows="12"
    62 placeholder="Your content goes here.."
    63 onChange={formik.handleChange}
    64 value={formik.values.content}
    65 ></textarea>
    66 </div>
    67 <div className="flex justify-end">
    68 <button className="bg-purple-700 text-white text-base m-2 p-3 border rounded-md" type="submit">
    69 <FontAwesomeIcon icon={faSave} /> &nbsp; Save
    70 </button>
    71 </div>
    72 </form>
    73 </>
    74 );
    75};
    76
    77export default AddNoteForm;

    Kita akan sedikit bahas baris code berikut ini yang merupakan bagian penting dari proses integrasi API.

    1...
    2 const { data } = await API.graphql({
    3 authMode: 'API_KEY',
    4 query: createNote,
    5 variables: {
    6 input: {
    7 title: values.title,
    8 content: values.content
    9 }
    10 }
    11...

    API.graphql({}) merupakan method yang kita ambil dari package @aws-amplify/api yang berfungsi untuk menghandle operasi Query dan Mutation pada GraphQL API.

    Beberapa parameter yang digunakan pada method API.graphql ini:

    • authMode, jenis autentikasi yang digunakan
    • query, jenis query yang digunakan, dan diambil dari file yang ada di dalam folder graphql. Pada code diatas kita gunakan query jenis mutation dan method yang dipanggil adalah createNote
    • variables, berisi payload atau data yang akan dikirim ke GraphQL API

    Karena setiap request ke AWS server harus dievaluasi maka kita perlu menambahkan konfigurasi AWS di file index.js atau _app.js.

    pages/_app.js

    1import Amplify from '@aws-amplify/core';
    2
    3import awsconfig from "../src/aws-exports";
    4
    5import '../styles/globals.css';
    6
    7Amplify.configure({ ...awsconfig, ssr: true });
    8
    9function MyApp({ Component, pageProps }) {
    10 return <Component {...pageProps} />;
    11}
    12
    13export default MyApp;

    Penjelasan singkat:

    • aws-exports, berisi AWS credential yang digunakan agar bisa berkomunikasi dengan AWS Amplify
    • Karena kita menggunakan Next.js yang support SSR maka perlu ditambahkan opsi ssr:true

    Test API

    Pengujian atau test API dapat dilakukan dengan mengakses alamat localhost:3000/add kemudian tambahkan title dan content.

    dinotes amplify add new note

    Kemudian kita bisa cek apakah data berhasil disimpan pada Admin UI.

    dinotes check data 1

    dinotes check data 2

    dinotes check data 3