Alex The Entreprenerd

Freebies
Slides
posts

Installing Strapi

First, let’s update strapi to 3.2.x.

updatingstrapi.png

We can update strapi by opening the package.json file and updating all the strapi dependencies from 3.1.1 to 3.2.5 by using find and replace.

Then re-install strapi with npm i

Then re-build the admin with npm run build

And start again with npm run develop.

Finally, we’re using Strapi Version 3.2.5 which fixes bugs for most browsers and adds the Draft and Publish System which we’ll use for Products.

updated_strapi.png

Let’s add Products to the API by selecting ‘Content-Types Builder’ and create a new collection type called product.

builder.png

newtype_name.png

Next, let’s add fields that we are going to fill later on.

The title is going to be a string.

media_field.png

Set image as a media type.

Make sure to switch the type to single when setting the media. It’s really important to avoid confusion later. Double-check that image is single media!

text_field.png

Meta_description and meta_title are fields that we’ll use for SEO purposes later on. Set long text as their type.

number_field.png

Set the price as a decimal so it always has 2 decimals. Don't forget to add a slug that converts the title of our product into a slug version. We're going to use the slug for the URL of the page for that particular product.

And that’s it for the product.

all_fields.png

Now that our content type is ready. Let's add some products to strapi.

add_products.png

Add The Complete Strapi Course at 12,99

creating_product.png

Note the fact that after you save, you still have to publish it. This gives you more freedom in managing content with Strapi. Click on Publish and you’re good to go. I’ll add a second product so that we can work with a list of products.

product_entries.png

Once you have 2 products, you’re good to go.

Let’s test the API out. First, we need to open up the endpoint by going in Settings -> Users Permissions and set products to be available to the public. Otherwise, we are going to get a 403 (forbidden) error when making GET requests.

settings.png

roles.png

permissions.png

To keep it simple, you can visit localhost:1337/products to see all the products. You can just save this file as products.json so that we have a couple of fake data we can use while we develop the NextJs Pages and Components.

Always focus on one problem at a time, this way when something goes wrong, you won’t have to backtrack too much.

Installing NextJs

Then run it with

Add the product.json file in the root folder

nextjs_root_folder.png

As you keep working with NextJs you’ll get more familiar with it, however, when your first open your NextJs project you'll notice that there aren't that many files and folders. We'll be adding more as we build this project, but it is quite easy to navigate. You have pages where you’ll put page components public for your static assets Styles for CSS modules

We’ll be adding more folders as we develop, but at the end of the day, the nextJS Quickstart is as if you start with a Create React App project while already being in the src folder.

Let’s keep going by importing the products fake data in the home page so we can create the All Products Page.

index_page_before.png

Let's start by deleting everything inside of our index page except the Head. It should look something like this when you're done!

Also, delete everything in the CSS module for this page as we won’t need it

home_css_file.png

For a start let’s import the JSON file that you saved previously and loop over the records and show titles on our page. Add a few lines of code to our index page.

If you open your index page on your browser you should have something like this.

titles_webpage.png

Next, let’s add the basic product information like the image, title, and price. We are also going to add styles to our products - mostly images so they don't overflow and look decent.

The image that we get from strapi is just an URL to the actual image. Some products might now have an image, for them, we need to display a default image which we have locally in our NextJs product. For this logic, let's create a helper function which we can re-use everywhere.

This provides us with defaults and works both in development and production. Now our index page should look like this.

If you visit your browser the image should now be displaying correctly.

image_browser_display.png

Now let’s add some basic styling to our global CSS file.

And for the image:

Because we want to visit a product-specific page we add a link that links to the single product page using the slug we get from strapi. First import Link from ‘next/link’ which is a built-in tool from next that helps us navigate through pages. And then wrap it around the anchor tags.

Our index page should now look like this:

If you click on the title you should now be redirected to the correct URL, the page will display a 404 message, but that is just because we haven't set the product-specific page yet.

product_page_redirect.png

Let’s work on the product page next.

Single Product Page

Create a single product page

create_slug_page.png

Delete the api folder as we’re not going to use it this time

delete_apifolder.png

And this is how the product page should look like.

We’ll be hardcoding the product since we’re just coding the page for now. We’ll be connecting Next with Strapi shortly. But before that let's also add a new helper function that is going to format the price for us.

Import the function inside our single product page and replace:

replace_this.png

With

replace_with.png

Test it out by setting the product price to 12.00 in JSON:

set_price.png

If you visit the browser this is how it should look now!

show_correct_price.png

Looks really good.

Next, we'll be connecting NextJs with Strapi so that you’ll be displaying data that is fetched from the API.

You’ll be using Static Site Optimization to ensure optimal loading times. Let’s get it going!

Adding Server Requests

At this point, we can display the Products page and the single product page. Now is time to connect Next with Strapi so that we’re fetching the real data coming from the live API.

We’ll start with the Products page and then move to the single product page. Since this data won’t change often, we can use static site generation to generate static pages, which will load very fast.

The key method here is called getStaticProps and you’ll see how we can use this either for a single static page or to generate a bunch of them.

Open up VS Code /index,js

Thanks to Next we can export this getStaticProps function

Which will return props that will be passed to a page component, in this case, Home component.

Import API_URL which is the URL to our API back-end that we set previously.

Now we’re at the point where we can delete the hardcoded JSON products. Let’s add a ‘products’ to our props. We get products from the getStaticProps function that we had just created. This is how our Home page should look now.

And that looks good

Reload the home page and check it out.

Next, let’s tackle the Single Product Page. First let’s open up [slug].js.

We’ll still use static site generation, but since there’s a dynamic parameter, we have to tell NextJS which paths to generate. We can do that by using a second method, called:

The params will be passed to getStaticProps and from the params, we can get the slug.

We can now use the slug to get the proper product with that slug by using the filters on the content API and returning the first result found.

Then add the product as a prop in our Product page.

product_as_prop.png

Refresh the page and you should now see different products in different paths.

If you are having troubles fetching the data, restart nextJS and look at the Strapi Terminal

console_errors.png

If you see 400 codes it means you forgot to open the users and permissions in the settings page.

If you don’t see requests then you probably made a typo when defining the API_URL in NextJS.

Adding more features

Let’s improve the look of the app by adding a navbar and footer. To add a global footer and header, open up, /pages/_app.js and add the components there.

Add the Header and Footer components and wrap them with tag.

We haven't created the Header and Footer components, so let's do it now.

footer_header_files.png

Footer

Now let’s style the footer, since it’s just one tag, let’s add it to the global styles.

Header

Import Link from ‘next/link’ and let’s use it to link to our index page.

Let’s also style the title in global CSS.

Add a floating back button. We’re using {“<“} to escape the character, you can use the Unicode entity if you want. And this is how our file should look now!

Let’s quickly add some styles to our button as well, and we’re done!

Adding authentication

Our next goal is to add authentication.

This will help us with storing orders, processing payments and it will provide a more consistent user experience.

We can set up authentication on the front-end by using magic link, which will then integrate to Strapi to make authenticated requests.

To keep things as simple as I can, we’ll set up the Context first, test it out, and then we’ll add Magic to add real and secure authentication.

Create a new AuthContext.js file and a context folder inside our src folder.

Create the context by importing.

And then create the AuthContext using the createContext function that react provides. We are also going to need the router from NextJS, so we'll import that as well. And we're also going to export the context since we want to use it in other components / pages.

Now let’s code the AuthProvider which will enable us to access data from the context anywhere in the app. We're also going to get the router object by using the useRouter hook which we get from next. And finally, for our initial set-up, we'll also return some JSX.

Now let's start creating our authentication logic inside of our AuthProvider. First, let’s create a method to Login

And let's add a method to log-out as well.

Remember, we'll be using magic link to do the authentication. This set-up is temporary and we'll be upgrading it in the near future. Let’s add the ContextProvider to all pages by going into our _app.js file and wrap everything inside tag with AuthProvider.

login_method.png

Then to show the efficacy of our work, let’s use the context in the header.

Inside of our component, we can get the data from context by using the useContext hook that we have just imported.

Now let's add some logic to display the login link if our user is not logged in and our placeholder avatar if the user is actually logged in. We will also wrap the user avatar inside of a Link and point it to /account page so we'll be able to see our orders in the future and wrap the Link around the log in an anchor tag in case the user is not yet logged in.

And this is how our header component should look now!

Let’s also add some quick styling.

Next, let’s create the simple login page

login_page.png

At this point, we've done a lot of work. We can finally test logging in.

test_login.png

If everything worked correctly you should see your email on the right top corner.

browser_products_view.png

Finally, let’s add an account page

On this page, we’ll show the user orders as well as a button to logout

For now, let’s just add the logout button

Try it out a few times and you’ll see that now you can:

  • Log in
  • Access the account page
  • Logout

Next, let’s add Magic to perform real authentication.

Add Magic Link Authentication

To use magic for authentication you’ll first need a set of credentials. You can get those by registering at https://magic.link/.

Magic offers a free tier for the first 100 recurring users with options to upgrade. Once you have created an app and have both secret and test keys you are ready for next steps. Open up our NextJS project in your terminal and type:

Next up let’s import the magic package we have just installed into our AuthContext.

Now let’s also add our public key that we got from magic to our urls.js file so we can use it later and import it inside of our AuthContext.

To connect with magic let’s declare a variable called “magic” outside our AuthContext. We do this because we are going to re-declare it later and use it inside of our authentication functions.

Next let’s use a useEffect hook inside of our AuthContext to set up magic on initial application load.

And that’s it, our magic authentication system is ready to be implemented inside of our login and logout functions, so let’s do that next. Let’s start by updating our login function.

Now let’s also update our logout function.

We can now finally test it out! But, there is one issue. If you try refreshing our user will not be persisted. This is something we are going to fix next.

Finally, to persist user login between refresh, let’s add checkUserLoggedin function.

And call it in the useEffect hook to make it run on the initial application load. Now our user should persist on refresh

Finalizing the look of Login page

Let’s add some style to our page by creating a new file called Login.module.css inside of our styles folder and add some basic styling.

style_files.png

And that’s it for our styles in the Login.module.css file. Now let’s import the styles to our login page. And this is how our login page should look like now.

You’ll also notice that displaying the whole email address breaks into multiple lines which we do not want. So instead of displaying the whole email address, let’s instead display an icon inside our Header component.

And so this is how our Header component should look now!

Connecting Magic to Strapi

Now that your user can log in to the front-end, we can use Magic to make authenticated requests to Strapi. In order to do that we’ll have to install the Strapi Plugin Magic and then customize one file in Strapi’s code.

This will make it so that whenever we make an authenticated request, Strapi will create or find the proper user, seamlessly and securely connecting the frontend and the backend.

First, shift to the Strapi Terminal, install the Strapi Plugin Magic by typing:

Then rebuild strapi with:

Then restart strapi by typing:

By refreshing localhost:1337/admin you’ll notice a new entry in the plugins section.

strapi_plugins.png

You can store your Magic Secret Key which you got from magic, it will be used by the plugin to log users in.

adding_magic_url.png

magic_secret_added.png

Next, we’ll need to override the default permissions policy. The policy is used to determine who the current logged in user is.

If you want to learn more, there’s a full video just for this plugin detailing the installation steps and features even further.

To customise Strapi’s behaviour, open extensions/users-permisisons/config.

policies.png

Create a new folder called policies inside extensions/users-permissions and a new file called permissions.js file inside policies.

Next, you’ll have to copy and paste the default version of this file, which you can find at any time on Strapi’s GitHub.

strapigithub_url.png

strapi_github.png

strapi_github_permissions.png

Visit Strapi’s Github at github.com/strapi/strapi.

Packages -> strapi-plugin-users-permissions.js

Or visit this link

strapi-plugin-users-permissions.png

/config/policies/permissions.js

This change makes it so that if you make a request to Strapi, while using a Valid Magic Generated Bearer Token, Strapi will either create or add the user with the email that was used to log in with magic.

This connects the front-end and the back-end in a very seamless way.

To show you what I mean, let’s work on the Orders Collection Type that we’re going to be displaying to our user and that we will be processing through Stripe in the future.

Testing the Connection

In order to make authenticated requests with Magic, you’ll need to retrieve a bearer token.

Let’s add this functionality to our AuthContext so we can test the Magic and Strapi Integration.

To start, let’s create a function called getToken inside our AuthContext to retrieve the bearer token from magic.

And now call it inside of the checkUserLoggedIn function. We do this because we want to console log our unique token, so we can test it out on Postman.

Open up the console in your browser and refresh the page. If everything worked you should see your unique token getting printed in your console.

console-log.png

Now using this token let’s make a request on Postman to our back-end. Don’t forget to set the type to bearer token in the “Authorization” tab and copy in the token.

postman.png

Let’s create orders in strapi with the following fields: status (enum: paid unpaid), total (number decimals) and relationships with checkout_session (text), users_permissions_user -> User, product -> Product ( a single product can have / be in many orders).

order_strapi.png

edit_user.png

Next, let’s open up orders for authenticated users.

strapi_settings.png

This means that non logged in users will not be able to retrieve the orders.

permissions_settings2.png

PRIVACY DISCUSSION HERE:

I could just use a filter, but the problem is that it would allow other users to fetch orders that don’t belong to them. The simplest way is to override the default find and findOne Controllers so that a user can only find their own Orders. You can do so by opening up /api/orders/

creating_order.png

Create a Test Product. Make sure to publish it.

binding_userto_order.png

Attach the order to the user you have The user has been automatically created by Strapi Plugin Magic when you first entered the email and authenticated.

Now create a second order, without a user.

If you get an ‘Invalid token’ error the reason might be that the token expired, so retrieve a new one by refreshing your application and copying it from the console.

postman_error.png

postman_response_correct.png

Make the GET request and you’ll see you have access to both orders. This is a privacy violation that we’re going to fix next. Because we don’t want users to see each other's orders, ONLY the orders that are associated with that specific user.

Show me only my orders

We can show a user only their orders by overriding the default controller inside /api/order/controllers/order.js file that strapi generates.

First, we’ll import a util function that comes shipped with strapi.

Next we’ll override the find function with our own logic with privacy in mind.

And for the end, we’ll also override findOne function that will return a single order by provided id, but since we have privacy in mind we are only going to return if the passed order id matches the order id and user matches the order’s user.

  1. Create the Orders Page

Now that we can retrieve the orders for the logged in user, let’s show these orders in the Account Page.

We’ll have to make authenticated requests, which means we’ll export getToken from our ContextProvider in our AuthContext file..

Now let’s import getToken on our Accounts page.

getToken.png

Awesome, to simplify our fetching logic let’s create a custom hook called useOrders in the account page.

Remember to import API_URL

We can quickly check the value of orders with a quick console log line here.

Now that we know it’s working, let’s actually render the orders so they will be displayed on the page.

Next, let’s move the user email and logout button below, and let’s separate with an hr so it is more distinct.

Let’s also add a loading status so we inform the client that something is actually happening behind the scenes.

And for the last thing let’s add a Head section for SEO purposes. Also, don’t forget to import Head from next/head.

And we’re done! This is how our account page should look now!

Process Orders

It’s time to create and process the orders with Stripe. Specifically, we’ll be using Stripe Hosted Checkout to provide our customers with a secure, privacy compliant, and easy to implement checkout solution. We’ll pre-fill the email for them and then redirect them to a success page.

Order Processing will consist of the following steps:

  1. Our customer will start the order by clicking a button
  2. The button will make a request to strapi, which will make a request to Stripe to generate a checkout session
  3. If the checkout session is generated successfully, we’ll create the order and set it to an ‘unpaid’ status
  4. We’ll return the checkout session, this will be used in the frontend by Stripe’s SDK to redirect the user to the Stripe Hosted Checkout.
  5. Once the Stripe Checkout is Successful, we’ll redirect the customer to a success page.
  6. We’ll use the success page to tell strapi to use the Stripe SDK to verify that the payment was processed.
  7. If the payment was successful, we’ll update the order to be paid.

This behaviour can be extended by using Stripe’s webhooks with very little extra code. We’ll code the Checkout and Order Creation behaviour first then we’ll code the BuyButton and finally we’ll code the Confirmation page and the code to verify that an order was successful.

Get the Stripe Stuff

To continue, you’ll need a stripe account. I’m going to use development data to keep things simple

stripewb.png

Let’s get started by going to our terminal and typing:

In the meantime get your key from the strapi admin panel.

stripe_admin.png

stripe_keys.png

stripe_secretkey.png

You can inject the Secret Key by using a .env file. You’ll also use the PK in the order controller file.

env.png

Now import stripe and add the PK from environmental variables inside your order controller file.

Don’t forget to restart the strapi development server because we changed the .env file for changes to apply.

So what happens is we receive a product and user from our front-end.

  1. We verify that we do indeed have that product in our ecommerce web application.
  2. Using the Stripe SDK we create a checkout session with the product and user email.
  3. Lastly we create the order with unpaid status

Then return the session ID.

Let’s test that out with Postman.

Remember that you’ll need to use a fresh bearer token from Magic which you can get by reloading the page and checking out the console in your browser.

token.png

Testing Stripe and Strapi

Then use it to make a post request to http://localhost:1337/orders/ with the token to create the order. Don’t forget to also provide a product id.

postman_orders.png

postman_bearer.png

postman_body.png

Stripe Front-end

Now that we have confirmed that it works, let’s switch over to our NextJS application in the front-end. Let’s create a BuyButton component and CSS module for that component.

buybutton_component.png

buybutton_css.png

Let’s quickly add some basic styles inside of our BuyButton.module.css.

Since we want only authenticated users to be able to purchase goods we’ll make the component redirect to the login page if the user is logged out otherwise it will start the checkout flow. Now let’s create the component, it should look something like this.

Next let’s add Stripe functionality on our front-end by first installing Stripe’s front-end library with:

Then restart the frontend with npm run dev

Inside the BuyButton let's create a stripePormise variable by running the loadStripe function that stripe provides with our key.

Instead of passing the key directly to the loadStripe function let’s add it to our urls.js file and export it from there.

And update our import and loadStripe function by adding the STRIPE_PK.

Next up, let’s add the option for authenticated users to actually buy the product by creating a button that calls a handler function when pressed.

And add the handler function inside of our BuyButton which sends a request to our back.

Now our BuyButton component should look something like this.

We can now test it. When clicking the BUY button you should see Stripe’s front-end SDK redirect us to a checkout page. Let’s simulate us purchasing the item by using a fake credit card which Stripe provides (Use card 42424242424242424242424 and any 3 numbers for PIN)!

confirmationpage_browser.png

On confirmation, we should be redirected to the success page, with a custom query parameter. This query parameter is the checkout session id. We are going to use it to verify that the payment was processed and update the order. Let’s work on this logic next!

Verify an Order

Like we said above the query parameter is the successful checkout session id which we are going to use to verify that an order has been paid and update its status on Strapi by using this id.

First, we’ll be using Stripe to verify that payment was processed and then we’ll update the order to ‘paid’ after that, we are going to then return the updated order back to our front-end application. Now in case, the order is not verified, we’ll instead throw an error which can happen sometimes due to the asynchronous nature of working with APIs.

By using this setup, we can have a success page, which triggers the update. The success page can also be used for marketing purposes because the customer will visit the page on success.

First, let’s go into our strapi application and add a new function to our order controller.

Let’s now test it out! First we need to create the route, by editing the routes.json file in our order folder. Make sure that the handler points to the correct function (order.confirm).

Now we just need to open the endpoint (confirm) to the public in our Strapi admin panel and test the endpoint out in Postman.

order_permissions.png

postman_checkoutsession.png

If everything works correctly and we get the checkout_session id back then everything works correctly and we can move onto the last part - success page.

Success Page

We’ll need to retrieve the session_id from the query param. With that we can make a call to a method that we’ll write, but first let’s create the success page in our pages folder and import everything we need.

Next up, let’s create a new custom hook to retrieve the session id from the query param and send it back to our Strapi back-end.

And some feedback to the user.

Our success page should look like this:

And that’s it! You’ve successfully built an E-commerce Application with Nextjs, Strapi, Magic, and Stripe! Congratulations!

Deploy the Backend

We’ll be deploying the backend to Heroku.

deploying_backend_new.png

deploying_backend_name.png

Add the STRIPE_PK environmental variable to heroku, by going to Settings tab under the ‘Config Vars’.

deploying_backend_vars.png

In order to set up our application for deployment we’ll need to do three things:

  1. Set up Node Version
  2. Prepare the database
  3. Add file upload provider

Setting up the Node version

Most of the time that builds fail it is because of this step. Make sure to add your node version to the package.json file.

Setting up the Database

We need to store data somewhere, in development we have been using a local database. For production we are going to set up Postgres that Heroku provides. Let’s open up our Strapi back-end in our terminal and add:

Next up, let’s update the database.js file inside the config folder on strapi. Change it to a function with an explicit return. Then check if we’re in production, in case we are in production use the Postgres otherwise use SQLite database for development purposes. Remember DATABASE_URL will be provided by Heroku.

To get the DATABASE_URL let’s go to Heroku.

heroku_db.png

heroku_pg.png

Adding file upload provider

The very last thing before our back-end is live we need to add the upload provider. There are multiple providers, but we’re going to use S3 which Strapi supports. Copy and paste the strapi-provider-upload-aws-s3 plugin from strapi documents or from down below and paste it inside the plugins.js file in the config folder.

Remember that you still need to configure your AWS keys.

And that’s it!