Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 11, 2020 05:07 pm GMT

Build an Instagram clone with strapi.js and svelte (PART 2)

Hey! I'm back here with part 2 of this tutorial. As promised, now, we'll be working on the frontend.

As always, the code is available on Github

Interacting with the Strapi API

I was supposed to put this in the previous part, but I forgot

We're currently using Strapi on localhost:1337. This is fine for now, but in production, this URL will change. If we use this URL on every request we make, we may have to change this to the production url a lot of times. Instead of having to change it so much, let's only change it once. In main.ts, let's pass a prop called strapiApiUrl to our svelte app.

// src/main.tsimport App from './App.svelte';const app = new App({    target: document.body,    props: {        strapiApiUrl: 'http://localhost:1337'    }});export default app;
Enter fullscreen mode Exit fullscreen mode

Now, in App.svelte, we can set this strapiApiUrl as a context so that we can access it from anywhere within our app. We can achieve this with getContext and setContext.

<!-- src/App.svelte --><script lang="ts">    // ...    import { setContext } from "svelte";    // ...    export let strapiApiUrl: string;    setContext("apiUrl", strapiApiUrl);</script><!-- ... -->
Enter fullscreen mode Exit fullscreen mode

Making API requests

We can use fetch, but I think that axios is better, so let's install it:

npm install axios
Enter fullscreen mode Exit fullscreen mode

Now, to make an API request,

import axios from "axios";import { getContext } from "svelte";async function makeApiRequest(): Promise<WhatTheApiReturns> {    try {        const { data } = await axios.get<WhatTheApiReturns>(            getContext("apiUrl") + "/path/of/the/api"        );        return data;    } catch (err) {        console.log({ error: err });        throw new Error(            "Request failed with status: " +                err.response.status +                "
Check the console for further details." ); }}
Enter fullscreen mode Exit fullscreen mode

You can replace get with post or delete for those methods.

The post component

Let's quickly scaffold up a Post component which will show our post, like on instagram:

Alt Text

Let's see the code for this Post component:

<!-- src/components/Post.svelte --><script lang="ts">    import { getContext } from "svelte";    // typescript only    import type { Post } from "../types";    export let post: Post;</script><style>    .post {        width: 50%;        margin: 0 auto;    }    @media (max-width: 992px) {        .post {            width: 70%;        }    }    @media (max-width: 600px) {        .post {            width: 90%;        }    }</style><div class="w3-card post w3-section">    <a href="/@{post.user.username}/{post.id}"><img            src={post.image[0].provider === 'local' && getContext('apiUrl') + post.image[0].url}            alt={post.image.alternativeText || 'Post image'}            style="width: 100%" /></a>    <div class="w3-container">        <p class="w3-small w3-text-gray">            <a                href="/@{post.user.username}"                style="text-decoration: none">@{post.user.username}</a>        </p>        <p>{post.content}</p>    </div>    <footer class="w3-bar w3-border-top w3-border-light-gray">        <a            href="/@{post.user.username}/{post.id}"            class="w3-bar-item w3-button w3-text-blue"            style="width:100%">{post.comments.length}            {post.comments.length === 1 ? 'Comment' : 'Comments'}</a>    </footer></div>
Enter fullscreen mode Exit fullscreen mode

Now, if you want the types.ts file which contains the types like Post and Comment, here it is:

// WARNING: TYPESCRIPT USERS ONLY!export interface User {    id: string;    username: string;    email: string;    provider: string;    confirmed: boolean;    blocked: any;    role: number;    created_at: string;    updated_at: string;}export interface Post {    id: number;    user: User;    content: string;    image: Image;    comments: Comment[];    published_at: string;    created_at: string;    updated_at: string;}export interface Comment {    id: number;    content: number;    user: null | User;    post: number;    published_at: string;    created_at: string;    updated_at: string;}interface Image {    id: number;    name: string;    alternativeText: string;    caption: string;    width: number;    height: number;    formats: {        thumbnail: ImageMetaData;        large: ImageMetaData;        medium: ImageMetaData;        small: ImageMetaData;    };    hash: string;    ext: string;    mime: string;    size: number;    url: string;    previewUrl: null | string;    provider: string;    provider_metadata: null | any;    created_at: string;    updated_at: string;}interface ImageMetaData {    name: string;    hash: string;    ext: string;    mime: string;    width: number;    height: number;    size: number;    path: null | string;    url: number;}
Enter fullscreen mode Exit fullscreen mode

Fetching posts from the API

Let's create a /posts route. That route will serve the posts component, which fetches posts from our Strapi CMS and renders them.

<!-- src/routes/posts.svelte --><script lang="ts">    import axios from "axios";    import { getContext } from "svelte";    import Post from "../components/Post.svelte";    import type { Post as PostType } from "../types";    async function getPosts(): Promise<PostType[]> {        try {            const { data } = await axios.get<PostType[]>(                getContext("apiUrl") + "/posts"            );            return data;        } catch (err) {            console.log({ error: err });            throw new Error(                "Request failed with status: " +                    err.response.status +                    "
Check the console for further details." ); } }</script><h1 class="w3-xxxlarge w3-center">Posts</h1>{#await getPosts()} <div class="w3-center w3-section w3-xxlarge w3-spin"> <i class="fas fa-spinner" /> </div>{:then posts} <div class="w3-container w3-margin"> {#each posts as post} <Post {post} /> {/each} </div>{:catch err} <div class="w3-panel w3-pale-red w3-padding w3-leftbar w3-border-red w3-text-red"> {err} </div>{/await}
Enter fullscreen mode Exit fullscreen mode

We need to add this route to our router, so let's quickly do that by adding this line to App.svelte:

router("/posts", setupRouteParams, () => (page = Posts));
Enter fullscreen mode Exit fullscreen mode

Now, create a post on strapi following the video below.

If we head over to /posts on our frontend, we see:

Alt Text

Individual post page

When we visit @username/postid, I want to see username's post with ID postid, so let's do just that

<!-- src/routes/onePost.svelte --><script lang="ts">    import axios from "axios";    import { getContext } from "svelte";    import router from "page";    import type { Post, Comment as CommentType } from "../types";    import Comment from "../components/Comment.svelte";    export let params: { username: string; postId: string };    const apiUrl = getContext("apiUrl");    async function getPost(): Promise<Post> {        try {            const { data } = await axios.get<Post>(                apiUrl + "/posts/" + params.postId            );            if (data.user)                if (data.user.username !== params.username)                    router.redirect("/404");            return data;        } catch (err) {            if (err.response.status === 404) router.redirect("/404");            else {                console.log({ error: err });                throw new Error(                    "Request failed with status: " +                        err.response.status +                        "
Check the console for further details." ); } } } async function getComments(post: Post): Promise<CommentType[]> { try { let comments: CommentType[] = []; for (let i = 0; i < post.comments.length; i++) { const { data } = await axios.get<CommentType>( apiUrl + "/comments/" + post.comments[i].id ); comments.push(data); } return comments; } catch (err) { if (err.response) { console.log({ err }); if (err.response.status === 404) router.redirect("/404"); else { console.log({ error: err }); throw new Error( "Request failed with status: " + err.response.status + "
Check the console for further details." ); } } else throw new Error(err); } }</script><style> .post { width: 50%; margin: 0 auto; } @media (max-width: 992px) { .post { width: 70%; } } @media (max-width: 600px) { .post { width: 90%; } }</style>{#await getPost()} <div class="w3-center w3-section w3-xxlarge w3-spin"> <i class="fas fa-spinner" /> </div>{:then post} <div class="w3-card post"> <a href={post.image[0].provider === 'local' && getContext('apiUrl') + post.image[0].url}><img src={post.image[0].provider === 'local' && getContext('apiUrl') + post.image[0].url} alt={post.image.alternativeText || 'Post image'} style="width: 100%" /></a> <div class="w3-container"> <p class="w3-small w3-text-gray"> <a href="/@{post.user.username}" style="text-decoration: none">@{post.user.username}</a> </p> <p>{post.content}</p> </div> </div> <div class="w3-card post w3-margin-top"> <header class="w3-container w3-border-bottom w3-border-light-gray"> <h3>Comments</h3> </header> <div class="w3-container"> {#await getComments(post)} <div class="w3-center w3-section w3-xxlarge w3-spin"> <i class="fas fa-spinner" /> </div> {:then comments} {#each comments as comment} <Comment {comment} /> {/each} {:catch err} <div class="w3-panel w3-pale-red w3-padding w3-leftbar w3-border-red w3-text-red"> {err} </div> {/await} </div> </div>{:catch err} <div class="w3-panel w3-pale-red w3-padding w3-leftbar w3-border-red w3-text-red"> {err} </div>{/await}
Enter fullscreen mode Exit fullscreen mode

We have to get each comment separately because Strapi doesn't return detailed information of a relation inside another relation. This can be good if you don't need this data, which in most cases, you don't, but if you're in situations like this, you'll have to use this method.

Now this is what our app should look like:

Alt Text

User profile

Let's work on the user's profile page. This will be accessed through the /@username route. We want to display every post this user has made. So, create src/routes/userProfile.svelte, and put this in it:

<!-- src/routes/userProfile.svelte --><script lang="ts">    import axios from "axios";    import PostComponent from "../components/Post.svelte"import { getContext } from "svelte";import type { Post } from "../types";    export let params: {username: string}    const apiUrl:string = getContext("apiUrl")    async function getPosts(): Promise<Post[]> {        try {            const { data } = await axios.get<Post[]>(                getContext("apiUrl") + "/posts"            );            return data.map(post => {                if (post.user.username === params.username) return post            });        } catch (err) {            console.log({ error: err });            throw new Error(                "Request failed with status: " +                    err.response.status +                    "
Check the console for further details." ); } }</script><h1 class="w3-xxxlarge w3-center">Posts</h1>{#await getPosts()} <div class="w3-center w3-section w3-xxlarge w3-spin"> <i class="fas fa-spinner" /> </div>{:then posts} <div class="w3-container w3-margin"> {#each posts as post} <PostComponent {post} /> {/each} </div>{:catch err} <div class="w3-panel w3-pale-red w3-padding w3-leftbar w3-border-red w3-text-red"> {err} </div>{/await}
Enter fullscreen mode Exit fullscreen mode

And here's the user's page:

Alt Text

Conclusion

And here we are! We've seen how easy it is to integrate with Strapi's API using your frontend, but, we've only dealt with unauthenticated requests. In the next part, you'll see how to add authentication to your requests, and also create posts, comments and upload images to Strapi.

The 3rd part is out! Click here, here or here to view it!


Original Link: https://dev.to/arnu515/build-an-instagram-clone-with-strapi-js-and-svelte-part-2-44ld

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To