Managing a blog with AWS Amplify Studio
Tags: javascript, aws, react, nextjs
Published: 2023-05-25 03:48:28


## Summary

This article explains how to use **`AWS Amplify`** to manage a blog by creating, updating, and deleting new posts. In contrast to the previous blog post that used **`MDX files`**, this article uses a table on **`AWS DynamoDB`** and a graphical interface to update content. The article covers how to launch **`Amplify Studio`**, create a post data model using Data Modeling, and how to pull resources. Additionally, it demonstrates how to integrate Amplify libraries with Next.js for **`server-side rendering (SSR) `**and provides code examples for fetching and rendering blog posts from the DataStore.

---

Following the series of articles about **AWS Amplify**, in this post, we will cover a way to manage our blog by adding a way to create, update, and delete new posts.

In our [**past blog**](https://dev.to/albac/building-a-markdown-blog-using-nextjs-and-tailwind-typography-41mf), we used **MDX files** to get the markdown content and use **Tailwind/Topography** prose plugins to show our markdown with tons of beautiful styles.

For this blog, instead of using **MDX files** to get our markdown content, we will use a table on **AWS DynamoDB** and a graphic interface to update our content.

---

### Prerequisites

Before proceeding with this article, make sure you have an existing project hosted using AWS Amplify. If you don't have a project yet, you can follow [this blog](https://dev.to/albac/host-a-nextjs-blog-with-aws-amplify-and-setup-automated-deployments-6nl) to learn how to host a project using Amplify.

---

### Launching Amplify Studio

To get started, we need to launch **Amplify Studio**, which is a visual interface that manages the backend resources created by Amplify.

**1.** Go to the AWS Console and navigate to Amplify services.
**2.** Select your app, and under **`backend environments`**, click on the yellow button labeled **Launch Studio**.

Once Amplify Studio is launched, you will see the homepage where you can create or manage different resources. This interface provides a more convenient way to manage data models compared to using the Amplify CLI.

![Amplify Studio Homepage](https://raw.githubusercontent.com/albac/albac.dev/v1.3/images/studio-home.png)

---

### Data Modeling

Now let's create the data model for our blog posts using Amplify Studio.

**1.** Click on the **Data** link in the left menu.
**2.** Click on **Add model** to create a new model.
**3.** Enter the model title as **Posts**.
**4.** Add the following columns: **title**, **content**, and **summary**.

![Data Modeling in Amplify Studio](https://raw.githubusercontent.com/albac/albac.dev/v1.3/images/data-modeling.png)

After defining the model, click on **Save and Deploy** to deploy the infrastructure.

Wait for a few minutes until Amplify has finished creating all the necessary resources. Once completed, you will see the configuration and instructions for pulling that configuration. Here, you can select the model, operation, and language to generate code for accessing and managing your data.

![Amplify Client Configuration](https://raw.githubusercontent.com/albac/albac.dev/v1.3/images/amplify-client-config.png)

---

### Create Initial Content

Now, on the **`Amplify Studio`**  home page go to**`Content`** on the left menu. Here you will be able to create new post and see the list of post that has been created.

![Amplify Studio Create Post](https://raw.githubusercontent.com/albac/albac.dev/v1.3/images/Create_post.png)

>> Note:  There is currently a report that the new data manager doesn't support markdown editor anymore, but there is a work around provided by amplify team until this is resolved:
[https://github.com/aws-amplify/amplify-studio/issues/815](https://github.com/aws-amplify/amplify-studio/issues/815)



And add some **`markdown`** content:

![Amplify Studio Content](https://raw.githubusercontent.com/albac/albac.dev/v1.3/images/helloworld.png)

---


### Pull resources

Once we have successfully deployed our infrastructure, you can download the configuration locally using **AWS Amplify CLI**:

```bash
$ amplify pull --appId ddxxxxxxxir --envName dev                                                               

Pre-pull status:

    Current Environment: dev

┌──────────┬────────────────┬───────────┬───────────────────┐
│ Category │ Resource name  │ Operation │ Provider plugin   │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Hosting  │ amplifyhosting │ Create    │                   │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Api      │ albacdev       │ No Change │ awscloudformation │
└──────────┴────────────────┴───────────┴───────────────────┘

⚠️ Local changes detected.
⚠️ Pulling changes from the cloud will override your local changes.
 ```

Now you are ready to start coding and use your data model storage.

---

### The Code

In our previous implementation, we used **`MDX files`** stored in a GitHub repository to display our markdown content as a web blog page. However, this approach made it challenging to manage and maintain the MDX files through a website. It required downloading, editing, and re-uploading the MDX files.

To improve the management of our blog posts, we can leverage the **`Amplify libraries`** for a more efficient workflow.

- **aws-amplify**: This is the core library of AWS Amplify, providing a JavaScript interface for interacting with AWS services, including authentication, storage, and database.
- **@aws-amplify/ui-react**: This library offers UI components and utilities for building user interfaces in React applications. It includes components for authentication, forms, and commonly used web application UI elements.

These libraries simplify the integration of AWS services into React applications, providing convenient methods and components for tasks like authentication and data management. They abstract the underlying AWS SDKs, offering a higher-level API that streamlines the development process.

To implement this solution, we can also take advantage of the **`Next.js server-side rendering (SSR)`** feature supported by the Amplify libraries.

First, make sure you have the necessary **`Amplify UI libraries`** by running the following command:

```bash
$ npm install aws-amplify @aws-amplify/ui-react
```

Next, update your `_app.js` file to configure Amplify and enable SSR support:

```javascript
import { Amplify } from 'aws-amplify';
import awsconfig from '../src/aws-exports';
Amplify.configure({
  ...awsconfig,
  ssr: true
});
```

In your `[slug.js]` file, make the following changes:

```javascript
+import { withSSRContext } from "aws-amplify";
+import { Posts } from "../../src/models";
import { format, parseISO } from "date-fns"

export async function getStaticProps(context) {
+  const { DataStore } = withSSRContext(context);
  const { params } = context;
-  const allPosts = getAllPosts();
-  const { data, content } = allPosts.find((item) => item.slug === params.slug);
+  const { slug } = params;
+  const post = await DataStore.query(Posts, slug);
+  const { data, content } = matter(post.content);
+  console.log(data, content);
  const mdxSource = await serialize(content);

  return {
    props: {
      ...data,
-      date: data.date.toISOString(),
+      date: post.createdAt,
      content: mdxSource,
    },
+    revalidate: 60 * 60,
  };
}

-export async function getStaticPaths() {
+export async function getStaticPaths(context) {
+  const { DataStore } = withSSRContext(context);
+  const posts = await DataStore.query(Posts);
+  const paths = posts.map((post) => ({ params: { slug: post.id } }));
  return {
-    paths: getAllPosts().map((post) => ({
-      params: {
-        slug: post.slug,
-      },
-    })),
-    fallback: false,
+    paths,
+    fallback: true,
  };
}
```

Let's break down the changes:

```javascript
import { withSSRContext } from "aws-amplify";
import { Posts } from "../../src/models";
```

Here, we import the `withSSRContext` function, which enables us to query our data using SSR, and the `Posts` schema from the GraphQL models.

```javascript
export async function getStaticProps(context) {
  const { DataStore } = withSSRContext(context);
  const { params } = context;
  const { slug } = params;
  const post = await DataStore.query(Posts, slug);
  const { data, content } = matter(post.content);
  const mdxSource = await serialize(content);
```

We utilize the `getStaticProps` function from Next.js, which receives the `context` object containing the `req` and `res` keys from the session.

Using `context`, we leverage `withSSRContext` from Amplify to obtain the `DataStore` function. We also extract the parameters and the `slug`, which represents the ID of our post request. With the `slug` and `DataStore.query` function, we fetch the data and assign it to the `post` variable. From `post`, we can retrieve all the relevant post details such as the content, date, and summary.

```javascript
return {
  props: {
    ...data,
    date: post.createdAt,
    content: mdxSource,
    slug: slug,
  },
  revalidate: 60 * 60,
};
```

Finally, we return the data for rendering. The `revalidate` property is used to generate the data on the server-side according to the specified schedule.
In the future we will like to use on On-Demand Incremental Static Regeneration (ISR) but at the moment is not a supported feature: https://docs.aws.amazon.com/amplify/latest/userguide/ssr-Amplify-support.html

The same modifications are made to the `getStaticPaths` function for generating the paths:

```javascript
export async function getStaticPaths(context) {
  const { DataStore } = withSSRContext(context);
  const posts = await DataStore.query(Posts);
  const paths = posts.map((post) => ({ params: { slug: post.id } }));
  return {
    paths,
    fallback: true,
  };
}
```

With these changes in place, you can render the post as before, but now utilizing the `Post` model datastore instead of static files.

```javascript
  <div className="mx-5">
    <h2 className="dark:text-white text-3xl mt-8 font-bold">
      {title}
    </h2>
    <div className="text-sm text-gray-600 dark:text-gray-200 mt-4">
      Published {format(parseISO(date), "MMMM do, uuu")}
    </div>
    <article
      className="
+        prose dark:prose-invert
+        prose-a:text-blue-600
+        prose-a:font-light
+        prose-a:italic
+        prose-pre:bg-slate-800
+        prose-img:rounded
+        xl:prose-pre:prose-md
+        lg:prose-pre:prose-sm
+        xl:prose-xl
+        lg:prose-lg
+        prose-xl
+        max-w-sm
+        sm:max-w-lg
+        md:max-w-2xl
+        lg:max-w-3xl
+        xl:max-w-5xl
+        2xl:max-w-5xl
+        lg:prose-img:max-w-5xl
+        pt-6 text-slate-600 dark:text-slate-300 font-light font-sans"
    >
      <MDXRemote {...content} />
    </article>
  </div>
```

You can find the complete code [here](https://github.com/albac/albac.dev/blob/v1.3/pages/blog/%5Bslug%5D.js).

Finally, once you update a blog on Amplify Studio, depending of the revalidation time, the content of the blog will be recreated from our database and show in our website.

---

## Conclusion

In the context of this blog, DynamoDB is used as the data store for the blog posts. Instead of using MDX files, the blog content is stored in a DynamoDB table, which is accessed and managed using Amplify Studio. This approach provides an easy way to manage blog content without having to deal with the complexities of file-based storage.

In summary, AWS Amplify is a powerful development platform that provides a wide range of services and features for building modern web and mobile applications. Amplify Studio is a visual interface that simplifies the process of managing backend resources, making it an ideal choice for developers who prefer a more visual approach. Next.js is a popular React-based framework that offers several features for building server-side rendered web applications. AWS DynamoDB is a highly available, durable, and secure NoSQL database service that provides fast and predictable performance with seamless scalability.

---

## References

- **Nadder Dabit GitHub(Popular AWS/Next.js/Web3 developer):** [https://github.com/dabit3/next.js-amplify-datastore](https://github.com/dabit3/next.js-amplify-datastore)
- **Official Amplify documentation for DataStore:**  [https://docs.amplify.aws/lib/datastore/data-access/q/platform/js/](https://docs.amplify.aws/lib/datastore/data-access/q/platform/js/)
- **AWS Amplify Next.js feature support:** [https://docs.aws.amazon.com/amplify/latest/userguide/ssr-Amplify-support.html](https://docs.aws.amazon.com/amplify/latest/userguide/ssr-Amplify-support.html)
- **Author's portfolio blog:**  [https://albac.dev/blog/67d0470b-4056-460c-9272-3b182d4ab8aa](https://albac.dev/blog/67d0470b-4056-460c-9272-3b182d4ab8aa)
- **Blog code:**  [https://github.com/albac/albac.dev/blob/v1.3/](https://github.com/albac/albac.dev/blob/v1.3/)

---