はじめに
この記事では、Next.jsとGoを使用してアプリケーションを構築する方法を紹介します。具体的には、Goをバックエンドとして使用し、Next.jsをフロントエンドとして使用して、記事一覧ページを実装します。
なお、こちらは以下の続きになります。
バックエンドの実装
まず、Goを使用してバックエンドを構築します。以下のコードは、記事データを取得するためのエンドポイントを提供するGoのサーバーです。
package main
import (
"net/http"
"context"
"log"
"os"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/jijimama/newspaper-app/ent"
_ "github.com/lib/pq"
)
type Article struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
Content string `json:"content"`
Newspaper string `json:"newspaper"`
ColumnName string `json:"column_name"`
}
func main() {
router := gin.Default()
// .envファイルを読み込む
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}
// 環境変数から取得
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")
dbUser := os.Getenv("DB_USER")
dbName := os.Getenv("DB_NAME")
dbPassword := os.Getenv("DB_PASSWORD")
// 接続文字列を作成
dsn := "host=" + dbHost + " port=" + dbPort + " user=" + dbUser + " dbname=" + dbName + " password=" + dbPassword + " sslmode=disable"
//PostgreSQLに接続
client, err := ent.Open("postgres", dsn)
if err != nil {
log.Fatalf("failed opening connection to postgres: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
router.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, World!",
})
})
router.GET("/articles", func(c *gin.Context) {
articles, err := getArticles(client)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed querying articles"})
return
}
c.JSON(http.StatusOK, articles)
})
router.Run(":8080")
}
func getArticles(client *ent.Client) ([]Article, error) {
articles, err := client.Article.Query().WithNewspaper().All(context.Background())
if err != nil {
return nil, err
}
result := make([]Article, len(articles))
for i, a := range articles {
result[i] = Article{
Year: a.Year,
Month: a.Month,
Day: a.Day,
Content: a.Content,
Newspaper: a.Edges.Newspaper.Name,
ColumnName: a.Edges.Newspaper.ColumnName,
}
}
return result, nil
}
補足説明
type Article struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
Content string `json:"content"`
Newspaper string `json:"newspaper"`
ColumnName string `json:"column_name"`
}
Article構造体は、記事の情報を表現するために使用されるデータ構造です。この構造体には、以下のフィールドが含まれています:
- Year (int): 記事が発行された年を表します。
- Month (int): 記事が発行された月を表します。
- Day (int): 記事が発行された日を表します。
- Content (string): 記事の内容を表します。
- Newspaper (string): 記事が掲載された新聞の名前を表します。
- ColumnName (string): 記事が掲載されたコラムの名前を表します。
各フィールドには、JSONタグが付けられており、これにより構造体がJSON形式に変換される際のキー名が指定されています。例えば、YearフィールドはJSON形式に変換されると"year"というキーになります。
この構造体は、データベースから取得した記事データをAPIレスポンスとして返す際に使用されます。
router.GET("/articles", func(c *gin.Context) {...})
の解説
この部分は、Ginフレームワークを使用して定義されたHTTP GETエンドポイントです。/articlesパスに対するリクエストを処理します。
1. エンドポイントの定義:
router.GET("/articles", func(c *gin.Context) {...})
これは、/articles
パスに対するGETリクエストを処理するハンドラ関数を定義しています。
2. 記事の取得:
articles, err := getArticles(client)
getArticles
関数を呼び出して、データベースから記事のリストを取得します。client
はデータベース接続を管理するためのクライアントです。
3. エラーハンドリング:
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed querying articles"})
return
}
getArticles
関数がエラーを返した場合、HTTPステータス500 (Internal Server Error) とエラーメッセージをJSON形式でクライアントに返します。
4. 成功時のレスポンス:
c.JSON(http.StatusOK, articles)
記事のリストをJSON形式でHTTPステータス200 (OK) と共にクライアントに返します。
func getArticles(client *ent.Client) ([]Article, error)
の解説
この関数は、データベースから記事のリストを取得し、Article構造体のスライスとして返します。
1. 記事のクエリ:
articles, err := client.Article.Query().WithNewspaper().All(context.Background())
client.Article.Query()
を使用して、記事をクエリします。WithNewspaper()
は、記事と関連する新聞の情報も一緒に取得するためのメソッドです。All(context.Background())
は、すべての記事を取得します。
2. エラーハンドリング:
if err != nil {
return nil, err
}
クエリが失敗した場合、エラーを返します。
3. 結果の変換:
result := make([]Article, len(articles))
for i, a := range articles {
result[i] = Article{
Year: a.Year,
Month: a.Month,
Day: a.Day,
Content: a.Content,
Newspaper: a.Edges.Newspaper.Name,
ColumnName: a.Edges.Newspaper.ColumnName,
}
}
データベースから取得した記事データをArticle
構造体のスライスに変換します。a.Edges.Newspaper.Name
とa.Edges.Newspaper.ColumnName
は、関連する新聞の名前とコラム名を取得します。
結果の返却:
return result, nil
変換された記事のリストを返します。エラーがない場合、nil
をエラーとして返します。
動作確認
http://localhost:8080/articles
にアクセスします。
以下のように取得できれば、OKです!
フロントエンドの実装
次に、Reactを使用してフロントエンドを構築します。以下のコードは、記事データを表示するためのReactコンポーネントです。
import React from 'react';
type Article = {
id: number;
year: number;
month: number;
day: number;
content: string;
newspaper: string;
column_name: string;
};
async function fetchArticles(): Promise<Article[]> {
const res = await fetch('http://backend:8080/articles');
if (!res.ok) {
throw new Error('Failed to fetch articles');
}
return res.json();
}
const ArticlesPage = async () => {
const articles = await fetchArticles();
return (
<div className="container mx-auto p-4">
<h1 className="text-center text-2xl font-bold mb-4">コラム一覧</h1>
<ul className="space-y-4">
{articles.map((article) => (
<li key={article.id} className="bg-gray-800 p-6 rounded-lg shadow-md">
<h2 className="text-lg font-semibold text-gray-300 mb-2">{article.content}</h2>
<p className="text-gray-400">{article.year}/{article.month}/{article.day} {article.newspaper} / {article.column_name}</p>
</li>
))}
</ul>
</div>
);
};
export default ArticlesPage;
補足説明
type Article = {
id: number;
year: number;
month: number;
day: number;
content: string;
newspaper: string;
column_name: string;
};
- Article 型は、記事のデータ構造を定義しています。
- 各フィールドは記事の属性を表し、型が指定されています。
async function fetchArticles(): Promise<Article[]> {
const res = await fetch('http://backend:8080/articles');
if (!res.ok) {
throw new Error('Failed to fetch articles');
}
return res.json();
}
-
fetchArticles
関数は、バックエンドから記事のデータを非同期に取得します。 -
fetch
関数を使用して、指定されたURLからデータを取得します。 - レスポンスが正常
(res.ok)
でない場合、エラーをスローします。 - 正常な場合、レスポンスをJSON形式に変換して返します。
const ArticlesPage = async () => {
const articles = await fetchArticles();
return (
<div className="container mx-auto p-4">
<h1 className="text-center text-2xl font-bold mb-4">コラム一覧</h1>
<ul className="space-y-4">
{articles.map((article) => (
<li key={article.id} className="bg-gray-800 p-6 rounded-lg shadow-md">
<h2 className="text-lg font-semibold text-gray-300 mb-2">{article.content}</h2>
<p className="text-gray-400">{article.year}/{article.month}/{article.day} {article.newspaper} / {article.column_name}</p>
</li>
))}
</ul>
</div>
);
};
-
ArticlesPage
は非同期関数として定義されたReactコンポーネントです。 -
fetchArticles
関数を呼び出して記事のデータを取得します。 - 取得した記事データを使用して、記事の一覧を表示します。
動作確認
http://localhost:3000/articles
にアクセスします。
以下のように表示されればOKです!
まとめ
この記事では、GoとNext.jsを使用してアプリケーションを構築する方法を紹介しました。バックエンドではGinフレームワークを使用してAPIを構築し、フロントエンドではReactを使用してデータを表示しました。これにより、効率的にデータを取得し、ユーザーに表示することができます。
ぜひ、この記事を参考にして、あなた自身のフルスタックアプリケーションを構築してみてください。