Ga naar hoofdinhoud

Y-Clone (Routing) (twitter)

Download het starterproject en voer daarna npm install uit.

📂 Naam project: lab-nextjs-y-routing
🔗 Basisproject: de vereiste begincode is inbegrepen in het starterproject.

We bouwen verder op de Y-Clone applicatie die je in het vorige labo hebt gemaakt. Deze keer gaan we routing toevoegen aan de applicatie.

Pas de applicatie aan zodat je de data van de posts en de profielen uit een MongoDB database haalt. Maak hiervoor gebruik van de mongodb npm package. Je kan de database lokaal draaien of gebruik maken van een cloud oplossing zoals MongoDB Atlas.

De database seed route​

Voorzie een pagina op de route /admin/seed die de database seedt met de posts en profielen data. Je kan hiervoor de fetch calls hergebruiken die je in het vorige labo hebt gebruikt om de data op te halen. Vervolgens sla je deze data op in de database.

Je kan de volgende database.ts file gebruiken om de database connectie en de seed functie te implementeren:

import { Collection, MongoClient, Sort } from "mongodb";
import { Post, Profile } from "@/types";

const client = new MongoClient(process.env.MONGODB_URI || "mongodb://localhost:27017");

export const postsCollection: Collection<Post> = client.db("y-clone").collection<Post>("post");
export const profilesCollection: Collection<Profile> = client.db("y-clone").collection<Profile>("profiles");

const PAGE_SIZE = 5;

export const seedDatabase = async () => {
await postsCollection.deleteMany({});
await profilesCollection.deleteMany({});

const response = await fetch("https://raw.githubusercontent.com/similonap/json/refs/heads/master/y-clone/profiles.json");
if (!response.ok) {
throw new Error("Fetching profiles went wrong")
}
const profiles = await response.json() as Profile[];

await profilesCollection.insertMany(profiles);

const responsePosts = await fetch("https://raw.githubusercontent.com/similonap/json/refs/heads/master/y-clone/posts.json");
if (!responsePosts.ok) {
throw new Error("Fetching posts went wrong")
}
let posts = await responsePosts.json() as Post[];

posts = posts.map(post => ({ ...post, likes: Math.floor(Math.random() * 100) }));

await postsCollection.insertMany(posts);

const postsFromDb = await postsCollection.find().toArray();
console.log("Seeded posts:", postsFromDb);
const profilesFromDb = await profilesCollection.find().toArray();
console.log("Seeded profiles:", profilesFromDb);

return { posts: postsFromDb, profiles: profilesFromDb };
}

export const getProfileByUsername = async (username: string) => {
const profile = await profilesCollection.findOne({ username: username });

if (!profile) {
throw new Error("Profile not found");
}

return profile;
}

export const getPostsByUsername = async (username: string, sort: string = "newest", page: number = 1) => {
let sortObject: Sort = { createdOn: -1 };
if (sort === "oldest") {
sortObject = { createdOn: 1 };
} else if (sort === "most_liked") {
sortObject = { likes: -1 };
}

let posts = await postsCollection.find({ username: username }).sort(sortObject).skip((page - 1) * PAGE_SIZE).limit(PAGE_SIZE).toArray();
const profile = await getProfileByUsername(username);

posts = posts.map(post => {
return {
...post,
profile: profile
} as Post;
});

const totalPosts = await postsCollection.countDocuments({ username: username });
const pages = Math.ceil(totalPosts / PAGE_SIZE);

return { posts, pages };
}

export const getPosts = async (q: string = "", sort: string = "newest", page: number = 1) => {
let sortObject: Sort = { createdOn: -1 };
if (sort === "oldest") {
sortObject = { createdOn: 1 };
} else if (sort === "most_liked") {
sortObject = { likes: -1 };
}


let posts = await postsCollection.find({ text: new RegExp(q, "i") }).sort(sortObject).skip((page - 1) * PAGE_SIZE).limit(PAGE_SIZE).toArray();
let profiles = await profilesCollection.find().toArray();

posts = posts.map(post => {
return {
...post,
profile: profiles.find(p => p.username === post.username)
} as Post;
});

const totalPosts = await postsCollection.countDocuments({ text: new RegExp(q, "i") });
const pages = Math.ceil(totalPosts / PAGE_SIZE);


return { posts, pages };
}

Als de pagina bezocht wordt zal deze pagina de database seeden met de posts en profielen data en een bericht tonen dat de seed succesvol is uitgevoerd.

Seeded 11 posts and 6 profiles.

Voorzie een navigatie menu die op elke pagina zichtbaar is. Voeg hiervoor een MenuBar component toe die gebruik maakt van de Link component van Next.js om te navigeren tussen de verschillende pagina's.

alt text

Home pagina met paginatie en sortering​

Pas de home pagina (/) aan zodat deze de posts ophaalt uit de database in plaats van uit de API. Voeg paginatie toe zodat er telkens 5 posts per pagina getoond worden. Voeg ook sortering toe zodat de gebruiker kan kiezen om de posts te sorteren op "newest", "oldest" of "most liked". Er moet ook een zoekbalk voorzien worden zodat de gebruiker kan zoeken op tekst in de posts. Telksens de sortering, paginatie of zoekterm verandert moet dit worden weergegeven in de huidige URL als query parameters.

Bij het laden van de posts moet er een laadskelet getoond worden.

alt text alt text

Profiel pagina​

Voorzie een profiel pagina op de route /[username] waar [username] de gebruikersnaam is van het profiel dat bekeken wordt. Op deze pagina worden alle posts van de gebruiker getoond met paginatie. Hier hoeft geen zoekbalk of sortering voorzien te worden.

alt text

Oplossing downloaden​

Eerst zelf proberen!

Maak de oefening eerst zelf, ook als het moeizaam gaat. Pas daarna download je de oplossing om ze met je eigen code te vergelijken.

Als je meteen de oplossing bekijkt, leer je niets. Het zoeken en vastlopen is net het deel waar je het meeste uit leert.

Ik heb het geprobeerd, download