Skip to content

Commit

Permalink
Merge pull request #126 from Bear29ers/feature/#122_get_data_from_ins…
Browse files Browse the repository at this point in the history
…tagram

Feature/#122 Get data from instagram(Instagramの情報を取得する)
  • Loading branch information
Bear29ers authored Jun 30, 2024
2 parents a43636c + c89aa7d commit 6a22c99
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ yarn-error.log*
# local env files
.env*.local

# docker-compose
docker-compose.yml

# vercel
.vercel

Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml → docker-compose-tmp.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '3.9'
services:
app:
container_name: app
Expand All @@ -9,6 +8,9 @@ services:
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
- GRAPH_API_BASE_URL=https://graph.facebook.com/
- GRAPH_API_INSTAGRAM_ID=dummy
- GRAPH_API_ACCESS_TOKEN=dummy
volumes:
- .:/app
- /app/node_modules
Expand Down
22 changes: 22 additions & 0 deletions src/app/api/instagram/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NextResponse } from 'next/server';

import fetchJson from '@/libs/fetchJson';

import type { Media } from '@/types/media';

export const GET = async () => {
const baseUrl = process.env.GRAPH_API_BASE_URL;
const instagramId = process.env.GRAPH_API_INSTAGRAM_ID;
const accessToken = process.env.GRAPH_API_ACCESS_TOKEN;
const queries = 'media{caption,children{media_url},media_url,permalink,like_count,timestamp,username}';
const url = `${baseUrl}/${instagramId}?access_token=${accessToken}&fields=${queries}`;

try {
const data = await fetchJson<Media>(url);
return NextResponse.json(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error fetching Instagram media data: ', error);
return NextResponse.json({ error: 'Failed to fetch Instagram data' }, { status: 500 });
}
};
45 changes: 45 additions & 0 deletions src/app/gallery/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
'use client';

import { useEffect, useState } from 'react';

import AnimatedText from '@/components/common/AnimatedText/AnimatedText';
import Footer from '@/components/layout/Footer/Footer';

import fetchMedia from '@/libs/fetchMedia';

import type { Media } from '@/types/media';

import type { NextPage } from 'next';

const Gallery: NextPage = () => {
const [mediaData, setMediaData] = useState<Media | null>(null);

const loadMediaData = () => {
fetchMedia()
.then((data: Media) => {
setMediaData(data);
})
.catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
});
};

useEffect(() => {
loadMediaData();
}, []);

return (
<div className="flex w-full flex-col items-center px-2.5 text-white xs:px-5 lg:px-0">
<div className="my-24">
<AnimatedText text="Gallery" classes="text-[48px] xs:text-[60px] xsm:text-[80px]" />
</div>
<div>
<h1>Instagram Gallery</h1>
{!mediaData || !mediaData.media.data.length ? (
<div>No data available</div>
) : (
<div>
{mediaData.media.data.map((post, index) => (
<div key={index}>
<img src={post.mediaUrl} alt={post.caption} />
<p>{post.caption}</p>
<p>Likes: {post.likeCount}</p>
<p>Posted by: {post.username}</p>
<a href={post.permalink} target="_blank" rel="noopener noreferrer">
View on Instagram
</a>
</div>
))}
</div>
)}
</div>
{/* Footer */}
<Footer />
</div>
Expand Down
10 changes: 10 additions & 0 deletions src/libs/fetchJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// execute fetch api
const fetchJson = async <T>(url: string): Promise<T> => {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
return (await res.json()) as T;
};

export default fetchJson;
16 changes: 16 additions & 0 deletions src/libs/fetchMedia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { convertToCamelCase } from '@/utils/conversion/convertToCamelCase';

import type { Media } from '@/types/media';

const fetchMedia = async (): Promise<Media> => {
const res = await fetch('/api/instagram');

if (!res.ok) {
throw new Error('Failed to fetch media data');
}

const data = (await res.json()) as Media;
return convertToCamelCase(data);
};

export default fetchMedia;
25 changes: 25 additions & 0 deletions src/types/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface ChildrenData {
mediaUrl: string;
id: string;
}

interface Children {
data: ChildrenData[];
}

interface MediaData {
caption: string;
children: Children;
mediaUrl: string;
permalink: string;
likeCount: number;
timestamp: string;
username: string;
id: string;
}

export interface Media {
media: {
data: MediaData[];
};
}
40 changes: 40 additions & 0 deletions src/utils/conversion/convertToCamelCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable no-param-reassign */
/* eslint-disable no-use-before-define */

// convert snake case to camel case (string)
const convertToCamelCaseStr = (str: string): string => {
return str
.split('_')
.map((word, index) => {
if (index === 0) {
return word.toLowerCase();
}
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
})
.join('');
};

// convert snake case to camel case (array of objects)
const convertToCamelCaseArrayOfObjects = <T extends object>(array: T[]): T[] => {
const arrayType = toString.call(array).slice(8, -1).toLowerCase();

if (arrayType !== 'array') return [];
return array.map((obj) => convertToCamelCase(obj));
};

// convert snake to camel case (object)
export const convertToCamelCase = <T extends object>(obj: T): T => {
const result = {} as T;
Object.entries(obj).forEach(([key, val]) => {
const valType = toString.call(val).slice(8, -1).toLowerCase();

if (valType === 'object') {
val = convertToCamelCase(val as Record<string, unknown>);
} else if (valType === 'array') {
val = convertToCamelCaseArrayOfObjects(val as Record<string, unknown>[]);
}
(result as Record<string, unknown>)[convertToCamelCaseStr(key)] = val;
});

return result;
};
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noPropertyAccessFromIndexSignature": false,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
Expand Down

0 comments on commit 6a22c99

Please sign in to comment.