【Astro/SSG】Astroでのjson表示してアプリから読み込み!

Astro / TailWind

https://umi.grtlab.com/
umiカメラというフィルムカメラ風撮影ができるアプリです!無料なので使ってください。

さくらのレンタルサーバーに置いています。

お知らせをjsonで配信しようかなと

ts ? astro ?

まず、jsonとして出力するには src/pages/api/test.json.ts
を作ると
/api/test.jsonにアクセスできるようになります

import type { APIRoute } from 'astro';

export function getStaticPaths() {
  return AvailableLocales.map(lang => ({
    params: { lang },
  }));
}

const allNews = [
  { 
    id: 1, 
    priority: 0, // 100: warning, 1: new 0: normal
    content: {
      "en": { "title": "Thank you for your continued support in 2026!" },
      "ja": { "title": "2026年もよろしくお願いいたします!" },
      "de": { "title": "Vielen Dank für Ihre Unterstützung im Jahr 2026!" }
    },
    uri: "/",
    start: "2026-01-01T00:00:00+09:00",
    end: "2026-01-07T23:59:59+09:00"
  },
  { 
    id: 2, 
    priority: 0, // 100: warning, 0: normal
    content: {
      "en": {  title: "How to Switch Filters in umi camera: Quickly Explore 100+ Photo Filters"},
      "ja": {  title: "umi cameraのフィルター切り替え方法:100種類以上のフォトフィルターを使いこなす"}
    },
    uri: "/how-to-use/switching-filters/",
    start: "2026-01-01T00:00:00+09:00",
    end: "2026-12-07T23:59:59+09:00"
  }
];

export const GET: APIRoute = ({ params }) => {
  const { lang } = params;
  const now = new Date();

  const filteredNews = allNews
    .filter(item => {
      const isWithin = now >= new Date(item.start) && now <= new Date(item.end);
      const hasLang = item.content[lang as keyof typeof item.content];
      return isWithin && hasLang;
    })
    .map(item => ({
      id: item.id,
      priority: item.priority,
      title: (item.content[lang as keyof typeof item.content] as any).title,
      uri: item.uri,
      start: item.start,
      end: item.end
    }))
    .sort((a, b) => b.priority - a.priority);

  return new Response(JSON.stringify(filteredNews), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  });
};

こんな感じでexport const GET: APIRoute = ({ params }) => {の中で
return new Responseで出力できました。

test.jsonで使いたい場合はこんな感じ。

ちなみに /api/v1/ja/test/で日本語を表示するように作っています

slashを固定すると面倒?

test.jsonではなく /test/ とuriをしたいので api/test/index.htmlを出力して
api以下は api/test/のヘッダーをjsonで返すようにhtaccessやサーバー設定に書きました。
この場合は

api/test/index.astroとastroで出力します。
こちらは、普通に return new Responseを直接出力でindex.htmlが生成されます。

return new Response(JSON.stringify(filteredNews), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
});
---

htaccess

ForceType application/json
AddDefaultCharset UTF-8

RewriteEngine On
# 本番環境: ヘッダー必須 403 (Forbidden)
RewriteCond %{HTTP:X-App-Token} !^token$
RewriteRule ^ - [F,L]

/api/v1/.htaccess v1以下を
ForceTypeでjsonで認識させています

バージョンキーとして ヘッダーのトークンをチェック(ただのお知らせなのでパスワードではありません。

iPhoneアプリからの読み込みは簡単で

newsアイテムの取得ですがこんな感じで

 guard let url = URL(string: BASE_URL + "/api/v1/\(langkey)/news/") else { return nil }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 10.0 // タイムアウト
        request.setValue(token, forHTTPHeaderField: "X-App-Token")

        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            // 成功時の処理
            // 2. HTTPステータスコードのチェック
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                debugPrint("サーバーエラーまたは認証失敗")
                return nil
            }
            
            let decoder = JSONDecoder()
            // 1. まず全件デコード
            let allItems = try decoder.decode([NewsItem].self, from: data)

こんな感じで、newsitemに設定しました◎

お気軽にコメントください!

スパム対応のためコメント認証に数日かかることがありますが、お気軽にコメントいただけると嬉しいです^^

コメント

タイトルとURLをコピーしました