DEV Community

Cover image for How to Build a Job Nina with Sapper, Strapi and Netlify
Strapi for Strapi

Posted on • Originally published at strapi.io

How to Build a Job Nina with Sapper, Strapi and Netlify

Author: Opuama Lucky

This article will show you how to build a job search platform using Sapper with Strapi as database and then deploy it to Netlify.

Job Nina is a platform where jobs are created; the admin/employer can add any field job that matches the company's requirements. Generally speaking, job nina is a job search platform.
In this tutorial, I'll walk you through the steps of creating a job nina with sapper and strapi as the backend then deploy to netlify.

Prerequisites

To complete this tutorial , you need to have:

  • Node.js and NPM installed locally. Visit article from Teamtreehouse for instructions on installing Node.js and NPM on your local machine
  • Basic understanding of sveltejs/sapper.
  • Basic understanding of strapi. click to know more
  • Git and GitHub
  • Netlify account

What is Sapper?

Sapper is a web application framework with a beautiful development experience and flexible filesystem-based routing.

Sapper, unlike single-page apps, does not compromise SEO, progressive enhancement, or the initial load experience. However, unlike traditional server-rendered apps, navigation is instantaneous for that app-like feel. It is a svelte-powered application that creates bigger apps with a smaller footprint.

What is Strapi?

Strapi is an open-source, Node. js-based, headless CMS that saves developers time while allowing them to use their favorite tools and frameworks. Strapi also enables content editors to streamline content delivery (text, images, video, etc.) across any device. Visit strapi documentation page for more details.

Project Setup

Let's begin setting up our project.

Strapi Installation

Before we begin the installation process, make sure that Node.js and NPM are fully installed on your system.

  • Use the following command to start a new project.
       npx create-strapi-app@latest my-project --quickstart
Enter fullscreen mode Exit fullscreen mode
  • Change my-project to the preferred name of your project. Select n when asked for a configuration template.
  • Wait for the complete installation. You will be redirected to http://localhost:1337/admin, where you will be prompted to register your details. Strapi Welcome Page
  • Add your details and click LET’S START to access the Strapi dashboard. The Strapi Dashboard

Building the Project

In this phase, start by creating a new collection type Jobs with the following content and field types as shown below:

  • Name
  • Salary
  • Experience

Content Architecture Screenshot

Also, create another collection type "Job_type" with content "Name".

Content Type Screenshot

Then, add a relation for the two collection types. In the collection type Job_type field Name, click on add new entry and select the require field for the relation.

Content Type Screenshot

Next, head over to Settings, navigate to Roles, and click on Public. Scroll down to Permissions, click on Jobs and Job_type, then click on "select all" to allow all activities for the application and save.

Installing Sapper

Sapper installation is very easy; copy and paste the following command on your terminal/cmd.

     npx degit "sveltejs/sapper-template#rollup" my-app
    # or: npx degit "sveltejs/sapper-template#webpack" my-app
    cd my-app
    npm install
    npm run dev
Enter fullscreen mode Exit fullscreen mode

The above command install the project's dependencies, scaffold a new project in the my-app directory, and launch a server. Now that you are done with the installation process, start building the project.

Developing the Frontend

After installing all the necessary packages for developing our front end, we should run the command npm run dev to begin working on the front end. After that, a display showing the local address will appear on your cmd screen: http://localhost:3000.

This local address can be copied and pasted into your browser to bring up the Sapper page. Next, open the sapper folder in your code editor, navigate to src/component/nav.svelte; copy and paste the following code.

    <script>
      export let segment;
      const handleClick = () => {
        console.log(segment);
      };
    </script>
    <nav>
      <div>
        <h1 on:click={handleClick}>JOB FORAGE</h1>
        <ul>
          <li><a class:current={segment === undefined} href=".">Home</a></li>
          <li><a class:current={segment === "about"} href="about">About</a></li>
          <li>
            <a class:current={segment === "contact"} href="contact">Contact</a>
          </li>
          <li><a class:current={segment === "Jobs"} href="Jobs">Jobs</a></li>
        </ul>
      </div>
    </nav>
    <style> 
      nav {
        background: #fff3ef;
      }
      nav div {
        max-width: 960px;
        padding: 0 10px;
        margin: 0 auto;
        display: grid;
        grid-template-columns: 1fr 1fr;
        text-align: center;
      }
      h1 {
        margin-top: 20px;
        font-size: 30px;
        font-weight: bold;
        /* display:flex; */
        text-align: left;
      }
      ul {
        padding: 0;
        margin: 0;
        text-align: right;
      }
      li {
        display: inline-block;
      }
      a {
        text-decoration: none;
        padding: 1em 0.5em;
        display: block;
      }
      a.current {
        border-bottom: 3px solid #ff3e00;
      }
    </style>
Enter fullscreen mode Exit fullscreen mode

Click on src/component/footer.svelte, and include the code below.

    <footer>copyright 2022 by Opuama Lucky</footer>
    <style>
      footer {
        color: #bbb;
        text-align: center;
        padding: 20px;
        max-width: 400px;
        margin: 40px auto 0;
        border-top: 1px solid #f2f2f2;
      }
    </style>
Enter fullscreen mode Exit fullscreen mode

The code above should display both the nav bar and the footer in your browser.

For the homepage, navigate to src/routes/index.svelte; copy and paste the codes below.

     <svelte:head>
      <title>Begining sapper</title>
    </svelte:head>
    <div>
      <img src="jet.jpg" alt="" />
      <h1>Job Nina</h1>
      <p>find your next assignment right here</p>
      <a href="Jobs" class="btn">View Job</a>
    </div>
    <style>
      h1,
      p {
        text-align: center;
        margin: 0 auto;
      }
      h1 {
        font-size: 2em;
        text-transform: uppercase;
        font-weight: 700;
        margin: 0 0 0 0.5em 0;
      }
      img {
        width: 200px;
        margin: 0 auto;
        display: block;
      }
      a {
        margin: 40px;
      }
      div {
        margin-top: 60px;
        text-align: center;
      }
      @media (min-width: 480px) {
        h1 {
          font-size: 4em;
        }
      }
    </style> 
Enter fullscreen mode Exit fullscreen mode

Unfortunately, you will not see anything on your browser because we have not yet decided how our project should look. To fix this, navigate to src/routes/ layout.svelte and enter the following code. The code below assists us in arranging our project.

    <script>
      import Nav from "../components/Nav.svelte";
      import Footer from "../components/Footer.svelte";
      export let segment;
    </script>
    <Nav {segment} />
    <main>
      <slot />
    </main>
    <Footer />
    <style>
      main {
        position: relative;
        max-width: 960px;
        padding: 60px 10px;
        margin: 0 auto;
      }
    </style> 
Enter fullscreen mode Exit fullscreen mode

Connecting Strapi

Before we begin, we must install axios in order to send asynchronous HTTP requests easily. Click here to know more about axios.

Run the following command on cmd /terminal

    npm install axios
    or
    yarn add axios
Enter fullscreen mode Exit fullscreen mode

Navigate to the routes folder and create a folder Jobs. Create a file index.svelte with the following codes (src/routes/Jobs/index.svelte).

    <script>
      import { onMount } from "svelte";
      import axios from "axios";
      let jobs = [];
      let error = null;

      const getJobs = () => {
        axios.get("http://localhost:1337/api/Jobs").then((res) => {
          jobs = res.data.data;
          console.log(jobs\["id"\]["attributes"]["Name"]);
          return { jobs };
        });
      };
      onMount(getJobs);
    </script>
    <style>
      ul{
        padding:0px;
      }
      li{
        list-style-type:none;
        padding: 10px;

      }
      ol li {
        font-weight: bold;
        font-size: 24px;
      }
        .btn{
        display:block;
        padding:15px;
        border:1px solid #6b3fca;
        border-radius:8px;
        background-color: #6b3fca;
        margin:10px auto;
        text-decoration:none;
      }
       a:hover{
        background-color:#9f43e5;
      }
      div{
        text-align:center;
      }
       ol li ul{
        display:none;
    }
     ol li:hover ul{
        display: block;
    }
    </style>
    <main>
    <div>
      <h1>All Current Jobs</h1>
      <hr />
      </div>
      {#await jobs}
        loading...
      {:then jobs}
        <ol>
          {#each jobs as jobs}
            <li > {jobs\["attributes"\]["Name"]}
              <ul>
                <li>Salary:  ${jobs\["attributes"\]["Salary"]}</li>
                <li>Job Requirement:  {jobs\["attributes"\]["Experience"]}</li>
              </ul>
            </li>
          {/each}
        </ol>
      {:catch error}
        {error}
      {/await}
      <div>
        <a href="/Jobs/create" class="btn" >Add a Job </a>
      </div>
    </main>
Enter fullscreen mode Exit fullscreen mode

In the above code, we imported axios and onMount life cycle, which provides a way to run a function when the component is loaded to the DOM. It can be used to initialize values and call API to load. Following that, apply a get request to fetch data from our Strapi backend.

Next, navigate to the routes folder again and create a folder Jobs. Create a file create.svelte with the following code (src/routes/Jobs/create.svelte).

    <style>
    h2{
        text-align: center;
    }
    form{
        max-width: 360px;
        margin:40px auto;
        text-align:center;
    }
    input, textarea{
    display:block;
    width:100%;
    padding:10px;
    font-family:arial;
    margin:10px auto;
    border:1px solid #eee;
    border-radius:8px;
    }
    </style>


    <script>
        import { onMount } from 'svelte';
        import axios from 'axios'

        let allJob_type = [];
        let title = "";
        let salary = "";
        let Jobtype = [];
        let details = "";
        let error = null;


        async function handleSubmit() {
            try {
                const response = await axios.post("http://localhost:1337/api/Jobs", {
                   data: { Name: title,
                    Salary: salary,
                    Experience: details,
                    Job_types: Jobtype,
                   },
                });
                Jobtype=response.data.data

            } catch(e) {
                error = e
            }
        }
        onMount(async () => {
        try {
            const response = await axios.get('http://localhost:1337/api/job-types');
            allJob_type = response.data.data
            console.log(allJob_type);
        } catch(e) {
            error = e
        }
    });

        </script>

        {#if error !== null}
          {error}
        {:else}
           <h2>Add a Job </h2>
           <form>
            <input type ="text" placeholder="Job Title" bind:value={title} required >
            <input type ="number" placeholder="Amount $" bind:value={salary}  required >
            <textarea type ="text" placeholder="Job Requirement" bind:value={details}  required></textarea> 
             <br />
            Select Job_type
            <br />
            {#each allJob_type as jobtype}
                <label>{jobtype\["attributes"\]["name"]}</label>
              <input type="checkbox" bind:group={Jobtype} value={jobtype} />
            {/each} 
            <input type="submit" value="Submit" on:click={handleSubmit} />
        </form>

        {/if}
Enter fullscreen mode Exit fullscreen mode

In the above code, we declare all of the variables required, and then we define a function handleSubmit to post/insert data to the API and a get request to retrieve the data.

Here is our Home page.
Home page

Here, Job/index.svelt is showing all available jobs. Click on "Add a Job".

Available Jobs

You will be directed to the Jobs/create page after clicking the Add a Job button. Fill out the form and submit it.

Form

After submitting the form, your information will appear as shown in the image below.

Form result

Go to Strapi database and refresh.

Updated data

Push to GitHub and Deploy to Netlify

In this phase, we will push our Sapper project to GitHub. If you are new to GitHub, follow this article and you will be fine. Click here to sign up as well.

After successfully pushing to GitHub, a similar image should appear on your git repo.

push confirmation

Let us now deploy to Netlify. If you are new, sign up and register. Once logged in, click on create new site and select import from an existing project. Choose GitHub, search for the GitHub repo, and continue. Update your build command to npm run export then deploy.

netlify deploy

Testing the Project

Before testing the project, ensure that Strapi app is still running on your system. To run Strapi app,

    npm run develop   
Enter fullscreen mode Exit fullscreen mode

Still on Netlify, click on open production deploy.

after deploy

screenshot

Conclusion

Congratulations, we have reached the end of this tutorial. If you followed the steps in this tutorial, your project should be working fine.

We were able to create collection types and establish a relationship between them. We fetched and submitted data to our Strapi backend, pushed to github and also deployed to Netlify. You can add more features to your project as well.

Here is the link to the GitHub repository for this tutorial.

Top comments (1)

Collapse
 
j2l profile image
Philippe Manzano

Sapper was deprecated quite some times ago. Maybe you should update?