Skip to content

Commit df2db87

Browse files
committed
implement generateSimpleMetadata for improved SEO across multiple pages
1 parent 970cf54 commit df2db87

File tree

11 files changed

+193
-56
lines changed

11 files changed

+193
-56
lines changed

app/(with-support)/about/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ import React from "react";
22
import Image from "next/image";
33
import Link from "next/link";
44
import { Button } from "@/components/ui/button";
5+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
56

6-
export const metadata = {
7-
title: "About APIs.guru",
8-
description: "Learn more about the APIs.guru project and its contributors",
9-
};
7+
export async function generateMetadata() {
8+
return generateSimpleMetadata({
9+
title: "About APIs.guru",
10+
description:
11+
"Learn more about the APIs.guru project and its contributors. Discover how we maintain the largest repository of machine-readable API specifications.",
12+
slug: "about",
13+
});
14+
}
1015

1116
const NumberRomb = () => (
1217
<svg viewBox="0 0 100 100" className="number-romb-svg">

app/(with-support)/add-api/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import React from "react";
22
import FormAddApi from "@/components/FormAddApi";
3+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
4+
5+
export async function generateMetadata() {
6+
return generateSimpleMetadata({
7+
title: "Add API",
8+
description:
9+
"Submit your API to the APIs.guru directory. We aggregate OpenAPI specifications, RAML, API Blueprint and other machine-readable API definitions.",
10+
slug: "add-api",
11+
});
12+
}
313

414
export default function AddApiPage() {
515
return (

app/(with-support)/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import React from "react";
22
import SearchClientComponent from "@/components/SearchClientComponent";
3+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
34

45
export const dynamic = "force-dynamic";
56

7+
export async function generateMetadata() {
8+
return generateSimpleMetadata({
9+
title: "APIs.guru - Wikipedia for Web APIs",
10+
description:
11+
"Wikipedia for Web APIs. Directory of REST API specs in OpenAPI 3.0 format",
12+
slug: "",
13+
});
14+
}
15+
616
export default async function Home() {
717
return (
818
<div className="container mx-auto px-4 py-4 relative">

app/api-doc/ApiDocClient.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import { RedocStandalone } from "redoc";
4+
import { useEffect } from "react";
5+
6+
export default function ApiDocClient() {
7+
useEffect(() => {
8+
const robotoFont = document.createElement("link");
9+
robotoFont.rel = "stylesheet";
10+
robotoFont.href = "//fonts.googleapis.com/css?family=Roboto:300,400,700";
11+
document.head.appendChild(robotoFont);
12+
13+
const montserratFont = document.createElement("link");
14+
montserratFont.rel = "stylesheet";
15+
montserratFont.href =
16+
"//fonts.googleapis.com/css?family=Montserrat:400,700";
17+
document.head.appendChild(montserratFont);
18+
19+
return () => {
20+
document.head.removeChild(robotoFont);
21+
document.head.removeChild(montserratFont);
22+
};
23+
}, []);
24+
25+
return (
26+
<div className="api-doc-container">
27+
<RedocStandalone
28+
specUrl="https://api.apis.guru/v2/openapi.yaml"
29+
options={{
30+
nativeScrollbars: true,
31+
32+
scrollYOffset: 56,
33+
}}
34+
/>
35+
</div>
36+
);
37+
}

app/api-doc/page.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,15 @@
1-
"use client";
2-
3-
import { RedocStandalone } from "redoc";
4-
import { useEffect } from "react";
1+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
2+
import ApiDocClient from "./ApiDocClient";
3+
4+
export async function generateMetadata() {
5+
return generateSimpleMetadata({
6+
title: "API Documentation",
7+
description:
8+
"Interactive API documentation for the APIs.guru REST API. Explore endpoints and test API calls.",
9+
slug: "api-doc",
10+
});
11+
}
512

613
export default function ApiDocPage() {
7-
useEffect(() => {
8-
const robotoFont = document.createElement("link");
9-
robotoFont.rel = "stylesheet";
10-
robotoFont.href = "//fonts.googleapis.com/css?family=Roboto:300,400,700";
11-
document.head.appendChild(robotoFont);
12-
13-
const montserratFont = document.createElement("link");
14-
montserratFont.rel = "stylesheet";
15-
montserratFont.href =
16-
"//fonts.googleapis.com/css?family=Montserrat:400,700";
17-
document.head.appendChild(montserratFont);
18-
19-
return () => {
20-
document.head.removeChild(robotoFont);
21-
document.head.removeChild(montserratFont);
22-
};
23-
}, []);
24-
25-
return (
26-
<div className="api-doc-container">
27-
<RedocStandalone
28-
specUrl="https://api.apis.guru/v2/openapi.yaml"
29-
options={{
30-
nativeScrollbars: true,
31-
32-
scrollYOffset: 56,
33-
}}
34-
/>
35-
</div>
36-
);
14+
return <ApiDocClient />;
3715
}

app/apis/[providerSlug]/[serviceSlug]/page.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,17 +150,22 @@ export async function generateMetadata(
150150
): Promise<Metadata> {
151151
const { providerSlug, serviceSlug } = await params;
152152
const api = getData(providerSlug, serviceSlug);
153+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://apis.guru";
154+
const isProduction = process.env.NEXT_PUBLIC_SITE_ENV === "production";
153155

154156
if (!api) {
155157
return {
156-
title: "API Not Found | API Directory",
158+
title: "API Not Found",
157159
description: "The requested API was not found in the directory.",
160+
alternates: {
161+
canonical: `${siteUrl}/apis/${providerSlug}/${serviceSlug}`,
162+
},
158163
};
159164
}
160165

161-
const title = `${api.info.title} | API Directory`;
162-
const description =
163-
api.cardDescriptionPlain || "Explore this API in the API Directory.";
166+
const title = `${api.info.title} API`;
167+
const description = `Documentation and specification of the ${api.info.title} API. Explore endpoints, methods, and integration options to use ${api.info.title} in your applications.`;
168+
const canonicalUrl = `${siteUrl}/apis/${providerSlug}/${serviceSlug}`;
164169

165170
return {
166171
title,
@@ -174,8 +179,9 @@ export async function generateMetadata(
174179
openGraph: {
175180
title,
176181
description,
177-
url: `/apis/${providerSlug}/${serviceSlug}`,
182+
url: canonicalUrl,
178183
type: "website",
184+
locale: "en_US",
179185
images: [
180186
{
181187
url: api.logo.url,
@@ -191,6 +197,10 @@ export async function generateMetadata(
191197
description,
192198
images: [api.logo.url],
193199
},
200+
robots: !isProduction ? "noindex, nofollow" : "index, follow",
201+
alternates: {
202+
canonical: canonicalUrl,
203+
},
194204
};
195205
}
196206

app/apis/[providerSlug]/page.tsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import Link from "next/link";
33
import list from "../../../list.json";
4+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
45

56
import ApiPage, { getData } from "./[serviceSlug]/page";
67
import SearchClientComponent from "@/components/SearchClientComponent";
@@ -71,6 +72,34 @@ export function generateStaticParams() {
7172
}));
7273
return params;
7374
}
75+
76+
export async function generateMetadata({
77+
params,
78+
}: {
79+
params: Promise<{ providerSlug: string }>;
80+
}) {
81+
const { providerSlug } = await params;
82+
const apis = getProviderApis(providerSlug);
83+
84+
const providerName = apis.length > 0 ? apis[0].providerName : providerSlug;
85+
const services = apis.filter((api) => api.serviceName);
86+
87+
const title =
88+
services.length > 0
89+
? `${providerName} APIs`
90+
: `${apis[0]?.title || providerName} API`;
91+
92+
const description =
93+
services.length > 0
94+
? `Explore ${providerName} APIs. Browse ${services.length} API${services.length > 1 ? "s" : ""} from ${providerName}.`
95+
: `Documentation and specification of the ${apis[0]?.title || providerName} API. Explore endpoints, methods, and integration options to use ${apis[0]?.title || providerName} in your applications.`;
96+
97+
return generateSimpleMetadata({
98+
title,
99+
description,
100+
slug: `apis/${providerSlug}`,
101+
});
102+
}
74103
export default async function ProviderPage({
75104
params,
76105
}: {
@@ -83,10 +112,7 @@ export default async function ProviderPage({
83112
if (services.length > 0) {
84113
return (
85114
<div className=" mx-auto px-4 relative">
86-
<SearchClientComponent
87-
88-
providerSlug={providerSlug}
89-
/>
115+
<SearchClientComponent providerSlug={providerSlug} />
90116
</div>
91117
);
92118
} else {

app/blog/[slug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function generateMetadata(props: {
2828
notFound();
2929
}
3030

31-
return generatePageMetadata({ page: post, slug: `/blog/${params.slug}` });
31+
return generatePageMetadata({ page: post, slug: `blog/${params.slug}` });
3232
}
3333

3434
export default async function PostPage(props: {

app/graphql-apis/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import ReactMarkdown from "react-markdown";
22
import remarkGfm from "remark-gfm";
33
import rehypeRaw from "rehype-raw";
4+
import { generateSimpleMetadata } from "@/sanity/lib/metadata";
5+
6+
export async function generateMetadata() {
7+
return generateSimpleMetadata({
8+
title: "Public GraphQL APIs",
9+
description:
10+
"A collective list of public GraphQL APIs. Discover and explore GraphQL endpoints from various providers.",
11+
slug: "graphql-apis",
12+
});
13+
}
414

515
// Define your markdown content here
616
const markdownContent = `# Public GraphQL APIs

app/layout.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ const roboto = Roboto({
2424
});
2525

2626
export const metadata = {
27-
title: "APIs.guru - Wikipedia for Web APIs",
28-
description: "Wikipedia for Web APIs. Directory of REST API specs",
27+
title: {
28+
default: "APIs.guru - Wikipedia for Web APIs",
29+
template: "%s | Api Directory",
30+
},
31+
description:
32+
"Wikipedia for Web APIs. Directory of REST API specs in OpenAPI 3.0 format",
2933
viewport: "width=device-width, initial-scale=1",
3034
icons: {
3135
icon: [
@@ -46,10 +50,9 @@ export const metadata = {
4650
},
4751
],
4852
},
49-
openGraph: {
50-
title: "APIs.guru - Wikipedia for Web APIs",
51-
description: "Wikipedia for Web APIs. Directory of REST API specs",
52-
},
53+
metadataBase: new URL(
54+
process.env.NEXT_PUBLIC_SITE_URL || "https://apis.guru"
55+
),
5356
};
5457

5558
export default function RootLayout({

0 commit comments

Comments
 (0)