How to Create a Blog with SvelteKit and Strapi

This page summarizes the projects mentioned and recommended in the original post on dev.to

Our great sponsors
  • SurveyJS - Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • WorkOS - The modern identity platform for B2B SaaS
  • svelte-adders

    Easily add integrations and other functionality to Svelte apps

  • We'll use SvelteAdd to add TailwindCSS to our application quickly. Run the below command to add TailwindCSS to our project.

  • Svelte-strapi-tutorial

  • Title Description Content

    Submit
    label { @apply font-bold block mb-1; } input { @apply bg-white w-full border border-gray-500 rounded outline-none py-2 px-4; } textarea { @apply bg-white w-full border border-gray-500 rounded outline-none py-2 px-4 resize-y; } .submit { @apply bg-blue-500 text-white border-transparent rounded px-4 py-2; }
    Enter fullscreen mode Exit fullscreen mode

    `

    Don't try this out yet, since there's currently no way to determine the Author of the PostPost. We need to code that in Strapi explicitly.

    Let's create custom controllers for the Post content type. Here, we'll make it so that the Author of a post will be the currently logged-in User.

    Edit api/post/controllers/post.js in the Strapi project.

    `

    'use strict';
    /**
     *  post controller
     */
    const { createCoreController } = require('@strapi/strapi').factories;
    const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    /**
     * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
     * to customize this controller
     */
    module.exports = {
        async create(ctx) {
            let entity;
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                data.author = ctx.state.user.id;
                entity = await strapi.services.post.create(data, { files });
            } else {
                ctx.request.body.author = ctx.state.user.id;
                entity = await strapi.services.post.create(ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async update(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't update this entry`);
            }
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                entity = await strapi.services.post.update({ id }, data, {
                    files,
                });
            } else {
                entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async delete(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't delete this entry`);
            }
            await strapi.services.post.delete({ id });
            return { ok: true };
        },
    };
    module.exports = createCoreController('api::post.post');
    
    Enter fullscreen mode Exit fullscreen mode

    `

    If you get confused, checkout the Strapi Documentation

    Install strapi-utils with the following command.
    bash
    npm install strapi-utils

    And now, you should be able to create and update posts all from one route. Let's make the update process easier. Change src/routes/blog/[id].svelte to the code below:

    `js


    import type { Load } from '@sveltejs/kit';
    export const load: Load = async ({ params, fetch }) => {

            // Now, we'll fetch the blog post from Strapi
            const res = await fetch(`http://localhost:1337/api/posts/${params.id}?populate=*`);
            // A 404 status means "NOT FOUND"
            if (res.status === 404) {
                // We can create a custom error and return it.
                // SvelteKit will automatically show us an error page that we'll learn to customise later on.
                const error = new Error(`The post with ID ${params.id} was not found`);
                return { status: 404, error };
            } else {
                const response = await res.json();
                return { props: { post: response.data.attributes } };
            }
        };
    
    
            import type { Post } from '$lib/types';
            import { onMount } from 'svelte';
            import { goto } from '$app/navigation';
            import user from '$lib/user';
        export let post: Post;
        let content = post.content;
        onMount(async () => {
            // Install the marked package first!
            // Run this command: npm i marked
            // We're using this style of importing because "marked" uses require, which won't work when we import it with SvelteKit.
            // Check the "How do I use a client-side only library" in the FAQ: https://kit.svelte.dev/faq
            const marked = (await import('marked')).default;
            content = marked(post.content);
        });
        async function deletePost() {
                // TODO
            }
    
    

    {post.title}

    By: {post.author.data.attributes.username}

    {#if $user && post.author.id === $user.id}

    goto('/new?edit=' + post.id)}>Update post Delete post

    {/if}
    {@html content}

    `

    Now, when the Author visits their PostPost, they'll see two buttons to Update and Delete the PostPost, respectively.

    Don't try this out yet, since there's currently no way to determine the Author of the PostPost. We need to code that in Strapi explicitly.
    Let's create custom controllers for the Post content type. Here, we'll make it so that the Author of a post will be the currently logged-in User.
    Edit api/post/controllers/post.js in the Strapi project.

    `

        "use strict";
    
        const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    
        /**
         * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
         * to customize this controller
         */
    
        module.exports = {
          async create(ctx) {
            let entity;
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              data.author = ctx.state.user.id;
              entity = await strapi.services.post.create(data, { files });
            } else {
              ctx.request.body.author = ctx.state.user.id;
              entity = await strapi.services.post.create(ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async update(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't update this entry`);
            }
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              entity = await strapi.services.post.update({ id }, data, {
                files,
              });
            } else {
              entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
        };
    

    `

    If you get confused, checkout the Strapi Documentation

    And now, you should be able to create and update posts all from one route. Let's make the update process easier. Change src/routes/blog/[slug].svelte to the code below:

    `

    
        
            import type { Load } from '@sveltejs/kit';
    
            export const load: Load = async ({ page: { params }, fetch }) => {
                // The params object will contain all of the parameters in the route.
                const { slug } = params;
    
                // Now, we'll fetch the blog post from Strapi
                const res = await fetch('http://localhost:1337/posts/' + slug);
    
                // A 404 status means "NOT FOUND"
                if (res.status === 404) {
                    // We can create a custom error and return it.
                    // SvelteKit will automatically show us an error page that we'll learn to customise later on.
                    const error = new Error(`The post with ID ${slug} was not found`);
                    return { status: 404, error };
                } else {
                    const data = await res.json();
                    return { props: { post: data } };
                }
            };
        
    
        
            import type { Post } from '$lib/types';
            import { onMount } from 'svelte';
            import { goto } from '$app/navigation';
            import user from '$lib/user';
    
            export let post: Post;
            let content = post.content;
    
            onMount(async () => {
                // Install the marked package first!
                // Run this command: npm i marked
    
                // We're using this style of importing because "marked" uses require, which won't work when we import it with SvelteKit.
                // Check the "How do I use a client-side only library" in the FAQ: https://kit.svelte.dev/faq
                const marked = (await import('marked')).default;
                content = marked(post.content);
            });
    
            async function deletePost() {
                // TODO
            }
        
    
        

    {post.title}

    By: {post.author.username}

    {#if $user && post.author.id === $user.id}

    goto('/new?edit=' + post.id)}>Update post Delete post

    {/if}
    {@html content}

    `

    Now, when the Author visits their PostPost, they'll see two buttons to Update and Delete the PostPost, respectively.

    Deleting Posts

    Let's add functionality to the Delete Post button. Edit the deletePost() function in the file we just modified (src/routes/blog/[id].svelte) and change it to this:

    `

        async function deletePost() {
            if (!localStorage.getItem('token')) {
                goto('/login');
                return;
            }
            const res = await fetch('http://localhost:1337/api/posts/' + post.id, {
                method: 'DELETE',
                headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
            });
            if (res.ok) {
                goto('/');
            } else {
                const data: { message: { messages: { message: string }[] }[] } = await res.json();
                if (data?.message?.[0]?.messages?.[0]?.message) {
                    alert(data.message[0].messages[0].message);
                }
            }
        }
    

    `
    Now, obviously, we don't want anybody to delete a post by someone else. Let's add another method in api/post/controllers/post.js in our Strapi App.

    This is how your code should look now:

    `

    'use strict';
    /**
     *  post controller
     */
    const { createCoreController } = require('@strapi/strapi').factories;
    const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    /**
     * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
     * to customize this controller
     */
    module.exports = {
        async create(ctx) {
            let entity;
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                data.author = ctx.state.user.id;
                entity = await strapi.services.post.create(data, { files });
            } else {
                ctx.request.body.author = ctx.state.user.id;
                entity = await strapi.services.post.create(ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async update(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't update this entry`);
            }
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                entity = await strapi.services.post.update({ id }, data, {
                    files,
                });
            } else {
                entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async delete(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't delete this entry`);
            }
            await strapi.services.post.delete({ id });
            return { ok: true };
        },
    };
    module.exports = createCoreController('api::post.post');
    

    `

    And now, the author should be able to delete posts.

    Custom Error Page

    You may have noticed that the 404 page looks terrible. It has almost no styling. With SvelteKit, we're allowed to create a custom error page. So we need to name this file __error.svelte and place it in src/routes.

    `

    
    
        import type { ErrorLoad } from '@sveltejs/kit';
        export type { ErrorLoad } from '@sveltejs/kit';
        export const load: ErrorLoad = ({ error, status }) => {
            return { props: { error, status } };
        };
    
    
        export let error: Error;
        export let status: number;
    
    

    {status}

    {error.message}

    `

    Here's how our error page will look like when you search for a blog post with wrong id.

    The Error Page

    Much better right?

    Now, obviously, we don't want anybody to delete a post by someone else. Let's add another method in
    api/post/controllers/post.js in our Strapi App.
    This is how your code should look now:

    `
    // api/post/controllers/post.js
    "use strict";

        const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    
        /**
         * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
         * to customize this controller
         */
    
        module.exports = {
          async create(ctx) {
            let entity;
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              data.author = ctx.state.user.id;
              entity = await strapi.services.post.create(data, { files });
            } else {
              ctx.request.body.author = ctx.state.user.id;
              entity = await strapi.services.post.create(ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async update(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't update this entry`);
            }
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              entity = await strapi.services.post.update({ id }, data, {
                files,
              });
            } else {
              entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async delete(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't delete this entry`);
            }
    
            await strapi.services.post.delete({ id });
    
            return { ok: true };
          },
        };
    

    `

    And now, the author should be able to delete posts.

    Conclusion & Resources

    And there you have it! Your blog website is made with SvelteKit and Strapi. If you got stuck anywhere, be sure to check the SvelteKit Docs, the Strapi Docs, and the source code on Github.

  • SurveyJS

    Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App. With SurveyJS form UI libraries, you can build and style forms in a fully-integrated drag & drop form builder, render them in your JS app, and store form submission data in any backend, inc. PHP, ASP.NET Core, and Node.js.

    SurveyJS logo
  • Tailwind CSS

    A utility-first CSS framework for rapid UI development.

  • TailwindCSS is a straightforward way to style your apps, and it's clear to add them to SvelteKit.

  • Svelte

    Cybernetically enhanced web apps

  • Javascript and Svelte knowledge. (Svelte has a great tutorial)

  • Strapi

    πŸš€ Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable and developer-first.

  • Strapi is a headless CMS coded in Javascript. A headless CMS has no frontend, only an admin panel, so it is ideal for developers. In other words, a Headless CMS is an API that serves up your content to be consumed by a frontend.

  • node

    Node.js JavaScript runtime βœ¨πŸ’πŸš€βœ¨

  • NodeJS and NPM installed on your machine.

  • SvelteKit

    web development, streamlined (by sveltejs)

  • Title Description Content

    Submit
    label { @apply font-bold block mb-1; } input { @apply bg-white w-full border border-gray-500 rounded outline-none py-2 px-4; } textarea { @apply bg-white w-full border border-gray-500 rounded outline-none py-2 px-4 resize-y; } .submit { @apply bg-blue-500 text-white border-transparent rounded px-4 py-2; }
    Enter fullscreen mode Exit fullscreen mode

    `

    Don't try this out yet, since there's currently no way to determine the Author of the PostPost. We need to code that in Strapi explicitly.

    Let's create custom controllers for the Post content type. Here, we'll make it so that the Author of a post will be the currently logged-in User.

    Edit api/post/controllers/post.js in the Strapi project.

    `

    'use strict';
    /**
     *  post controller
     */
    const { createCoreController } = require('@strapi/strapi').factories;
    const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    /**
     * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
     * to customize this controller
     */
    module.exports = {
        async create(ctx) {
            let entity;
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                data.author = ctx.state.user.id;
                entity = await strapi.services.post.create(data, { files });
            } else {
                ctx.request.body.author = ctx.state.user.id;
                entity = await strapi.services.post.create(ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async update(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't update this entry`);
            }
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                entity = await strapi.services.post.update({ id }, data, {
                    files,
                });
            } else {
                entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async delete(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't delete this entry`);
            }
            await strapi.services.post.delete({ id });
            return { ok: true };
        },
    };
    module.exports = createCoreController('api::post.post');
    
    Enter fullscreen mode Exit fullscreen mode

    `

    If you get confused, checkout the Strapi Documentation

    Install strapi-utils with the following command.
    bash
    npm install strapi-utils

    And now, you should be able to create and update posts all from one route. Let's make the update process easier. Change src/routes/blog/[id].svelte to the code below:

    `js


    import type { Load } from '@sveltejs/kit';
    export const load: Load = async ({ params, fetch }) => {

            // Now, we'll fetch the blog post from Strapi
            const res = await fetch(`http://localhost:1337/api/posts/${params.id}?populate=*`);
            // A 404 status means "NOT FOUND"
            if (res.status === 404) {
                // We can create a custom error and return it.
                // SvelteKit will automatically show us an error page that we'll learn to customise later on.
                const error = new Error(`The post with ID ${params.id} was not found`);
                return { status: 404, error };
            } else {
                const response = await res.json();
                return { props: { post: response.data.attributes } };
            }
        };
    
    
            import type { Post } from '$lib/types';
            import { onMount } from 'svelte';
            import { goto } from '$app/navigation';
            import user from '$lib/user';
        export let post: Post;
        let content = post.content;
        onMount(async () => {
            // Install the marked package first!
            // Run this command: npm i marked
            // We're using this style of importing because "marked" uses require, which won't work when we import it with SvelteKit.
            // Check the "How do I use a client-side only library" in the FAQ: https://kit.svelte.dev/faq
            const marked = (await import('marked')).default;
            content = marked(post.content);
        });
        async function deletePost() {
                // TODO
            }
    
    

    {post.title}

    By: {post.author.data.attributes.username}

    {#if $user && post.author.id === $user.id}

    goto('/new?edit=' + post.id)}>Update post Delete post

    {/if}
    {@html content}

    `

    Now, when the Author visits their PostPost, they'll see two buttons to Update and Delete the PostPost, respectively.

    Don't try this out yet, since there's currently no way to determine the Author of the PostPost. We need to code that in Strapi explicitly.
    Let's create custom controllers for the Post content type. Here, we'll make it so that the Author of a post will be the currently logged-in User.
    Edit api/post/controllers/post.js in the Strapi project.

    `

        "use strict";
    
        const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    
        /**
         * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
         * to customize this controller
         */
    
        module.exports = {
          async create(ctx) {
            let entity;
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              data.author = ctx.state.user.id;
              entity = await strapi.services.post.create(data, { files });
            } else {
              ctx.request.body.author = ctx.state.user.id;
              entity = await strapi.services.post.create(ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async update(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't update this entry`);
            }
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              entity = await strapi.services.post.update({ id }, data, {
                files,
              });
            } else {
              entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
        };
    

    `

    If you get confused, checkout the Strapi Documentation

    And now, you should be able to create and update posts all from one route. Let's make the update process easier. Change src/routes/blog/[slug].svelte to the code below:

    `

    
        
            import type { Load } from '@sveltejs/kit';
    
            export const load: Load = async ({ page: { params }, fetch }) => {
                // The params object will contain all of the parameters in the route.
                const { slug } = params;
    
                // Now, we'll fetch the blog post from Strapi
                const res = await fetch('http://localhost:1337/posts/' + slug);
    
                // A 404 status means "NOT FOUND"
                if (res.status === 404) {
                    // We can create a custom error and return it.
                    // SvelteKit will automatically show us an error page that we'll learn to customise later on.
                    const error = new Error(`The post with ID ${slug} was not found`);
                    return { status: 404, error };
                } else {
                    const data = await res.json();
                    return { props: { post: data } };
                }
            };
        
    
        
            import type { Post } from '$lib/types';
            import { onMount } from 'svelte';
            import { goto } from '$app/navigation';
            import user from '$lib/user';
    
            export let post: Post;
            let content = post.content;
    
            onMount(async () => {
                // Install the marked package first!
                // Run this command: npm i marked
    
                // We're using this style of importing because "marked" uses require, which won't work when we import it with SvelteKit.
                // Check the "How do I use a client-side only library" in the FAQ: https://kit.svelte.dev/faq
                const marked = (await import('marked')).default;
                content = marked(post.content);
            });
    
            async function deletePost() {
                // TODO
            }
        
    
        

    {post.title}

    By: {post.author.username}

    {#if $user && post.author.id === $user.id}

    goto('/new?edit=' + post.id)}>Update post Delete post

    {/if}
    {@html content}

    `

    Now, when the Author visits their PostPost, they'll see two buttons to Update and Delete the PostPost, respectively.

    Deleting Posts

    Let's add functionality to the Delete Post button. Edit the deletePost() function in the file we just modified (src/routes/blog/[id].svelte) and change it to this:

    `

        async function deletePost() {
            if (!localStorage.getItem('token')) {
                goto('/login');
                return;
            }
            const res = await fetch('http://localhost:1337/api/posts/' + post.id, {
                method: 'DELETE',
                headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
            });
            if (res.ok) {
                goto('/');
            } else {
                const data: { message: { messages: { message: string }[] }[] } = await res.json();
                if (data?.message?.[0]?.messages?.[0]?.message) {
                    alert(data.message[0].messages[0].message);
                }
            }
        }
    

    `
    Now, obviously, we don't want anybody to delete a post by someone else. Let's add another method in api/post/controllers/post.js in our Strapi App.

    This is how your code should look now:

    `

    'use strict';
    /**
     *  post controller
     */
    const { createCoreController } = require('@strapi/strapi').factories;
    const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    /**
     * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
     * to customize this controller
     */
    module.exports = {
        async create(ctx) {
            let entity;
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                data.author = ctx.state.user.id;
                entity = await strapi.services.post.create(data, { files });
            } else {
                ctx.request.body.author = ctx.state.user.id;
                entity = await strapi.services.post.create(ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async update(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't update this entry`);
            }
            if (ctx.is("multipart")) {
                const { data, files } = parseMultipartData(ctx);
                entity = await strapi.services.post.update({ id }, data, {
                    files,
                });
            } else {
                entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
            return sanitizeEntity(entity, { model: strapi.models.post });
        },
        async delete(ctx) {
            const { id } = ctx.params;
            let entity;
            const [article] = await strapi.services.post.find({
                id: ctx.params.id,
                "author.id": ctx.state.user.id,
            });
            if (!article) {
                return ctx.unauthorized(`You can't delete this entry`);
            }
            await strapi.services.post.delete({ id });
            return { ok: true };
        },
    };
    module.exports = createCoreController('api::post.post');
    

    `

    And now, the author should be able to delete posts.

    Custom Error Page

    You may have noticed that the 404 page looks terrible. It has almost no styling. With SvelteKit, we're allowed to create a custom error page. So we need to name this file __error.svelte and place it in src/routes.

    `

    
    
        import type { ErrorLoad } from '@sveltejs/kit';
        export type { ErrorLoad } from '@sveltejs/kit';
        export const load: ErrorLoad = ({ error, status }) => {
            return { props: { error, status } };
        };
    
    
        export let error: Error;
        export let status: number;
    
    

    {status}

    {error.message}

    `

    Here's how our error page will look like when you search for a blog post with wrong id.

    The Error Page

    Much better right?

    Now, obviously, we don't want anybody to delete a post by someone else. Let's add another method in
    api/post/controllers/post.js in our Strapi App.
    This is how your code should look now:

    `
    // api/post/controllers/post.js
    "use strict";

        const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
    
        /**
         * Read the documentation (https://docs.strapi.io/developer-docs/latest/development/backend-customization.html#core-controllers)
         * to customize this controller
         */
    
        module.exports = {
          async create(ctx) {
            let entity;
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              data.author = ctx.state.user.id;
              entity = await strapi.services.post.create(data, { files });
            } else {
              ctx.request.body.author = ctx.state.user.id;
              entity = await strapi.services.post.create(ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async update(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't update this entry`);
            }
    
            if (ctx.is("multipart")) {
              const { data, files } = parseMultipartData(ctx);
              entity = await strapi.services.post.update({ id }, data, {
                files,
              });
            } else {
              entity = await strapi.services.post.update({ id }, ctx.request.body);
            }
    
            return sanitizeEntity(entity, { model: strapi.models.post });
          },
    
          async delete(ctx) {
            const { id } = ctx.params;
    
            let entity;
    
            const [article] = await strapi.services.post.find({
              id: ctx.params.id,
              "author.id": ctx.state.user.id,
            });
    
            if (!article) {
              return ctx.unauthorized(`You can't delete this entry`);
            }
    
            await strapi.services.post.delete({ id });
    
            return { ok: true };
          },
        };
    

    `

    And now, the author should be able to delete posts.

    Conclusion & Resources

    And there you have it! Your blog website is made with SvelteKit and Strapi. If you got stuck anywhere, be sure to check the SvelteKit Docs, the Strapi Docs, and the source code on Github.

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

    InfluxDB logo
  • Visual Studio Code

    Visual Studio Code

  • Launch the app inside your favourite editor, for example, VSCode. Now, we can start the app with the below two commands:

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts