Skip to content

Commit 4d529f4

Browse files
committed
perf: performance optimisations
- Cloudinary getOptimisedUrl() helper with context transforms - ISR revalidate=3600 on all public pages - Parallel DB queries with Promise.all() - Selective column fetching on all list pages - Full OG and Twitter metadata on all public pages - robots.ts blocking /admin and /api from crawlers - sitemap.ts exposing all public and dynamic routes - All content images using next/image
1 parent 7c1cc16 commit 4d529f4

24 files changed

Lines changed: 974 additions & 475 deletions

File tree

AGENTS.md

Lines changed: 261 additions & 211 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 261 additions & 211 deletions
Large diffs are not rendered by default.

src/app/(public)/about/page.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
1+
import type { Metadata } from "next";
12
import Link from "next/link";
23
import Button from "@/components/ui/Button";
34

5+
export const revalidate = 3600;
6+
7+
const title = "About — CodedDevs Technology LTD";
8+
const description =
9+
"We build AI-first software products for African markets. Learn about our mission and approach.";
10+
11+
export const metadata: Metadata = {
12+
title,
13+
description,
14+
openGraph: {
15+
title,
16+
description,
17+
url: "https://codeddevs.com/about",
18+
siteName: "CodedDevs Technology LTD",
19+
type: "website",
20+
},
21+
twitter: {
22+
card: "summary_large_image",
23+
title,
24+
description,
25+
},
26+
};
27+
428
const companyFacts = [
529
{ label: "Founded", value: "March 2026" },
630
{ label: "Registered", value: "RC 9426867, Nigeria" },

src/app/(public)/blog/[slug]/page.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import PostContent, {
77
} from "@/components/blog/PostContent";
88
import Badge from "@/components/ui/Badge";
99
import { blogPosts, db } from "@/db";
10+
import { getOptimisedUrl } from "@/lib/cloudinary-url";
11+
12+
export const revalidate = 3600;
1013

1114
type UpdatePageProps = {
1215
params: {
@@ -39,8 +42,7 @@ async function getPublishedPostBySlug(slug: string) {
3942
.limit(1);
4043

4144
return post ?? null;
42-
} catch (error) {
43-
console.error("Failed to fetch update", error);
45+
} catch {
4446
return null;
4547
}
4648
}
@@ -70,13 +72,34 @@ export async function generateMetadata({
7072

7173
if (!post) {
7274
return {
73-
title: "Update \u2014 CodedDevs Updates",
75+
title: "Update CodedDevs Updates",
7476
};
7577
}
7678

79+
const title = `${post.title} — CodedDevs Updates`;
80+
const description = post.excerpt;
81+
const url = `https://codeddevs.com/blog/${post.slug}`;
82+
const images = post.cover_url
83+
? [{ url: getOptimisedUrl(post.cover_url), alt: post.title }]
84+
: undefined;
85+
7786
return {
78-
title: `${post.title} \u2014 CodedDevs Updates`,
79-
description: post.excerpt,
87+
title,
88+
description,
89+
openGraph: {
90+
title,
91+
description,
92+
url,
93+
siteName: "CodedDevs Technology LTD",
94+
type: "article",
95+
images,
96+
},
97+
twitter: {
98+
card: "summary_large_image",
99+
title,
100+
description,
101+
images: images?.map((image) => image.url),
102+
},
80103
};
81104
}
82105

@@ -113,7 +136,7 @@ export default async function UpdatePage({ params }: UpdatePageProps) {
113136
<div className="mx-auto max-w-5xl px-6">
114137
<div className="relative aspect-[16/9] overflow-hidden rounded-lg bg-[#F4F5F8]">
115138
<Image
116-
src={post.cover_url}
139+
src={getOptimisedUrl(post.cover_url)}
117140
alt={post.title}
118141
fill
119142
sizes="(min-width: 1024px) 1024px, 100vw"

src/app/(public)/blog/page.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1+
import type { Metadata } from "next";
12
import { desc, eq } from "drizzle-orm";
23
import UpdatesList, {
34
type UpdateListPost,
45
} from "@/components/blog/UpdatesList";
56
import { blogPosts, db } from "@/db";
67

7-
export const dynamic = "force-dynamic";
8+
export const revalidate = 3600;
9+
10+
const title = "Updates — CodedDevs Technology LTD";
11+
const description =
12+
"Product updates, announcements, and stories from the CodedDevs team.";
13+
14+
export const metadata: Metadata = {
15+
title,
16+
description,
17+
openGraph: {
18+
title,
19+
description,
20+
url: "https://codeddevs.com/blog",
21+
siteName: "CodedDevs Technology LTD",
22+
type: "website",
23+
},
24+
twitter: {
25+
card: "summary_large_image",
26+
title,
27+
description,
28+
},
29+
};
830

931
async function getPublishedPosts(): Promise<UpdateListPost[]> {
1032
try {
@@ -17,6 +39,7 @@ async function getPublishedPosts(): Promise<UpdateListPost[]> {
1739
author: blogPosts.author,
1840
category: blogPosts.category,
1941
published_at: blogPosts.published_at,
42+
cover_url: blogPosts.cover_url,
2043
})
2144
.from(blogPosts)
2245
.where(eq(blogPosts.is_published, true))
@@ -26,8 +49,7 @@ async function getPublishedPosts(): Promise<UpdateListPost[]> {
2649
...post,
2750
published_at: post.published_at?.toISOString() ?? null,
2851
}));
29-
} catch (error) {
30-
console.error("Failed to fetch updates", error);
52+
} catch {
3153
return [];
3254
}
3355
}

src/app/(public)/careers/page.tsx

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Metadata } from "next";
12
import Link from "next/link";
23
import { desc, eq } from "drizzle-orm";
34
import ApplicationForm from "@/components/careers/ApplicationForm";
@@ -7,7 +8,33 @@ import Card from "@/components/ui/Card";
78
import { careers, db } from "@/db";
89
import type { Career } from "@/types";
910

10-
export const dynamic = "force-dynamic";
11+
export const revalidate = 3600;
12+
13+
const title = "Careers — CodedDevs Technology LTD";
14+
const description =
15+
"Work with CodedDevs. We are a small team building software for African markets.";
16+
17+
type OpenRole = Pick<
18+
Career,
19+
"id" | "title" | "type" | "location" | "description"
20+
>;
21+
22+
export const metadata: Metadata = {
23+
title,
24+
description,
25+
openGraph: {
26+
title,
27+
description,
28+
url: "https://codeddevs.com/careers",
29+
siteName: "CodedDevs Technology LTD",
30+
type: "website",
31+
},
32+
twitter: {
33+
card: "summary_large_image",
34+
title,
35+
description,
36+
},
37+
};
1138

1239
function formatType(type: Career["type"]) {
1340
return type
@@ -19,17 +46,22 @@ function formatType(type: Career["type"]) {
1946
async function getOpenRoles() {
2047
try {
2148
return await db
22-
.select()
49+
.select({
50+
id: careers.id,
51+
title: careers.title,
52+
type: careers.type,
53+
location: careers.location,
54+
description: careers.description,
55+
})
2356
.from(careers)
2457
.where(eq(careers.is_open, true))
2558
.orderBy(desc(careers.created_at));
26-
} catch (error) {
27-
console.error("Failed to fetch open roles", error);
59+
} catch {
2860
return [];
2961
}
3062
}
3163

32-
function RoleCard({ role }: { role: Career }) {
64+
function RoleCard({ role }: { role: OpenRole }) {
3365
return (
3466
<Card>
3567
<article className="space-y-6">

src/app/(public)/contact/page.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1+
import type { Metadata } from "next";
12
import ContactForm from "@/components/contact/ContactForm";
23

4+
const title = "Contact — CodedDevs Technology LTD";
5+
const description =
6+
"Get in touch with the CodedDevs team. Partnerships, press, investment, and general enquiries.";
7+
8+
export const metadata: Metadata = {
9+
title,
10+
description,
11+
openGraph: {
12+
title,
13+
description,
14+
url: "https://codeddevs.com/contact",
15+
siteName: "CodedDevs Technology LTD",
16+
type: "website",
17+
},
18+
twitter: {
19+
card: "summary_large_image",
20+
title,
21+
description,
22+
},
23+
};
24+
325
const socialLinks = [
426
{
527
label: "GitHub",

src/app/(public)/page.tsx

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,71 @@
1+
import type { Metadata } from "next";
12
import { desc, eq } from "drizzle-orm";
23
import HeroSection from "@/components/sections/HeroSection";
34
import HackathonStrip from "@/components/sections/HackathonStrip";
45
import LatestReleasesSection from "@/components/sections/LatestReleasesSection";
56
import ProductsSection from "@/components/sections/ProductsSection";
67
import { blogPosts, db, products } from "@/db";
78

8-
export const dynamic = "force-dynamic";
9+
export const revalidate = 3600;
10+
11+
const title = "CodedDevs Technology LTD";
12+
const description =
13+
"Engineering software that works for Africa. AI-first products built for African markets from first principles.";
14+
15+
export const metadata: Metadata = {
16+
title,
17+
description,
18+
openGraph: {
19+
title,
20+
description,
21+
url: "https://codeddevs.com",
22+
siteName: "CodedDevs Technology LTD",
23+
type: "website",
24+
},
25+
twitter: {
26+
card: "summary_large_image",
27+
title,
28+
description,
29+
},
30+
};
931

1032
async function getFeaturedProducts() {
1133
try {
1234
return await db
13-
.select()
35+
.select({
36+
id: products.id,
37+
name: products.name,
38+
slug: products.slug,
39+
tagline: products.tagline,
40+
cover_url: products.cover_url,
41+
external_url: products.external_url,
42+
status: products.status,
43+
is_featured: products.is_featured,
44+
})
1445
.from(products)
1546
.where(eq(products.is_featured, true))
1647
.orderBy(products.order_index);
17-
} catch (error) {
18-
console.error("Failed to fetch featured products", error);
48+
} catch {
1949
return [];
2050
}
2151
}
2252

2353
async function getLatestPosts() {
2454
try {
2555
return await db
26-
.select()
56+
.select({
57+
id: blogPosts.id,
58+
title: blogPosts.title,
59+
slug: blogPosts.slug,
60+
excerpt: blogPosts.excerpt,
61+
category: blogPosts.category,
62+
published_at: blogPosts.published_at,
63+
})
2764
.from(blogPosts)
2865
.where(eq(blogPosts.is_published, true))
2966
.orderBy(desc(blogPosts.published_at))
3067
.limit(3);
31-
} catch (error) {
32-
console.error("Failed to fetch latest posts", error);
68+
} catch {
3369
return [];
3470
}
3571
}

src/app/(public)/products/[slug]/page.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import { eq } from "drizzle-orm";
55
import Badge from "@/components/ui/Badge";
66
import Button from "@/components/ui/Button";
77
import { db, products } from "@/db";
8+
import { getOptimisedUrl } from "@/lib/cloudinary-url";
89
import type { ProductSelect } from "@/types";
910

11+
export const revalidate = 3600;
12+
1013
type ProductPageProps = {
1114
params: {
1215
slug: string;
@@ -41,8 +44,7 @@ async function getProductBySlug(slug: string) {
4144
.limit(1);
4245

4346
return product ?? null;
44-
} catch (error) {
45-
console.error("Failed to fetch product", error);
47+
} catch {
4648
return null;
4749
}
4850
}
@@ -71,13 +73,34 @@ export async function generateMetadata({
7173

7274
if (!product) {
7375
return {
74-
title: "Product \u2014 CodedDevs",
76+
title: "Product CodedDevs",
7577
};
7678
}
7779

80+
const title = `${product.name} — CodedDevs`;
81+
const description = product.tagline;
82+
const url = `https://codeddevs.com/products/${product.slug}`;
83+
const images = product.cover_url
84+
? [{ url: getOptimisedUrl(product.cover_url), alt: product.name }]
85+
: undefined;
86+
7887
return {
79-
title: `${product.name} \u2014 CodedDevs`,
80-
description: product.tagline,
88+
title,
89+
description,
90+
openGraph: {
91+
title,
92+
description,
93+
url,
94+
siteName: "CodedDevs Technology LTD",
95+
type: "website",
96+
images,
97+
},
98+
twitter: {
99+
card: "summary_large_image",
100+
title,
101+
description,
102+
images: images?.map((image) => image.url),
103+
},
81104
};
82105
}
83106

@@ -140,7 +163,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
140163
<div className="mx-auto max-w-5xl px-6">
141164
<div className="relative aspect-[16/9] overflow-hidden rounded-lg bg-[#F4F5F8]">
142165
<Image
143-
src={product.cover_url}
166+
src={getOptimisedUrl(product.cover_url)}
144167
alt={product.name}
145168
fill
146169
sizes="(min-width: 1024px) 1024px, 100vw"

0 commit comments

Comments
 (0)