In this tutorial, we’ll be learning how to integrate social authentication into our Strapi application. In order to do this, we’ll be building simple notes sharing application with Strapi backend and Nuxt.js frontend, we’ll also use Quill.js as our text editor, Nodemailer for emails, we’ll integrate infinite scrolling and many more features.
In the third part of the tutorial series on social authentication with Strapi you will learn how to integrate vue-quill-editor to enable users to create content, image uploads, copying links, and email sharing.
In the first part, was all about getting started, we looked at a couple of things like getting started with Strapi, installing Strapi, and building a backend API with Strapi.
And in the second part, we learned how to add social providers (Github, Facebook) and the Nuxtjs frontend.
What’ll you need for this tutorial:
Here’s what the final version of our application looks like
The GitHub repository for the front-end application. The repo for the back-end application.
Table of contents
We need a way for our application users to create and edit notes, that’s why we’re going to integrate vue-quill-editor.
Execute the following lines of code
1yarn add vue-quill-editor //using yarn
2
3npm install vue-quill-editor //using npm
Open up your nuxt.config.js
file then add the following lines of code
1css: [ 'quill/dist/quill.snow.css', 'quill/dist/quill.bubble.css', 'quill/dist/quill.core.css' ],
2
3plugins: [
4 {
5 src: '~/plugins/vue-quill-editor',
6 mode: 'client'
7 },
8]
Create a vue-quill-editor.js
file in the plugins folder, then add the following lines of code to the file
1import Vue from 'vue'
2import VueQuillEditor from 'vue-quill-editor/dist/ssr'
3Vue.use(VueQuillEditor)
Now that we have successfully install and set-up vue-quill-editor, we can use it in our application.
Building the note page
Execute the following lines of code to create a notes/_id.vue
file
1cd pages
2mkdir notes
3touch _id.vue
Open up the file and fill it up with the lines of code below.
1<template>
2 <div>
3 <Nav />
4 <div class="w-4/5 sm:w-2/3 mx-auto">
5 <button class="button--blue my-3" @click="toggleAddEditors">
6 Add Editor
7 </button>
8 <NuxtLink :to="`/notes/preview/${res.id}`" class="button--blue">
9 Preview
10 <span><font-awesome-icon :icon="['fas', 'eye']" /></span>
11 </NuxtLink>
12 <div
13 v-if="addEditor"
14 class="absolute hex left-0 top-0 bottom-0 right-0 w-full"
15 >
16 <div class="bg-white sm:w-1/3 w-4/5 shadow-lg p-10 mx-auto mt-32">
17 <p v-if="error" class="text-red-600 my-3">{{ error }}</p>
18 <p v-if="success" class="text-green-600 my-3">{{ success }}</p>
19 <form @submit="addNewEditor">
20 <input
21 v-model="editorEmail"
22 class="p-5 font-bold w-full border-3 border-black-500"
23 type="email"
24 placeholder="Email"
25 />
26 <button type="submit" class="button--blue my-3">Add</button>
27 </form>
28 <button type="submit" class="button--green" @click="toggleAddEditors">
29 Cancel
30 </button>
31 </div>
32 </div>
33 <!-- <div>
34 <input
35 v-model="title"
36 class="p-5 font-bold text-4xl w-4/5 border-3 border-black-500"
37 type="text"
38 :readonly="!isAuthor"
39 @change="update()"
40 />
41 <span> <font-awesome-icon :icon="['fas', 'pen-alt']" /> </span>
42 </div> -->
43 <div id="toolbar"></div>
44 <div
45 ref="quill-editor"
46 v-quill:myQuillEditor="editorOption"
47 class="quill-editor shadow-2xl"
48 :content="content"
49 @change="onEditorChange($event), update()"
50 @blur="onEditorBlur($event)"
51 @focus="onEditorFocus($event)"
52 @ready="onEditorReady($event)"
53 >
54 <form ref="formInput">
55 <input
56 id="file"
57 ref="input"
58 name="files"
59 class="file"
60 type="file"
61 style="display: none"
62 @change="doUpload"
63 />
64 </form>
65 </div>
66 </div>
67 </div>
68</template>
69<script>
70export default {
71 async middleware({ $auth, route, redirect, store, $strapi, $axios }) {
72 const token = $auth.$storage.getUniversal('jwt')
73 const response = await $axios.get(`/notes/${route.params.id}`, {
74 headers: {
75 Authorization: `Bearer ${token}`,
76 },
77 })
78 const note = await response.data
79 const noteAuthorId = note.users_permissions_user.id
80 if (
81 $auth.$storage.getUniversal('user') === null ||
82 ($auth.$storage.getUniversal('user').id !== noteAuthorId &&
83 note.Editors.findIndex((editor) => {
84 return editor.id === $auth.$storage.getUniversal('user').id
85 }) === -1)
86 ) {
87 return redirect(`/notes/preview/${route.params.id}`)
88 }
89 },
90 async asyncData({ $strapi, store, route }) {
91 const note = await $strapi.$notes.findOne(route.params.id)
92 store.commit('setNote', note)
93 },
94 data() {
95 const self = this
96 return {
97 res: '',
98 error: '',
99 isAuthor: '',
100 title: '',
101 token: this.$auth.$storage.getUniversal(`jwt`),
102 content: '',
103 addEditor: false,
104 editorEmail: '',
105 editorOption: {
106 // some quill options
107 modules: {
108 toolbar: {
109 container: [
110 ['bold', 'italic', 'underline', 'strike'], // toggled buttons
111 ['link'],
112 ['blockquote', 'code-block'],
113 ['image'],
114 [{ header: 1 }, { header: 2 }], // custom button values
115 [{ list: 'ordered' }, { list: 'bullet' }],
116 [{ script: 'sub' }, { script: 'super' }], // superscript/subscript
117 [{ indent: '-1' }, { indent: '+1' }], // outdent/indent
118 [{ direction: 'rtl' }], // text direction
119 [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
120 [{ header: [1, 2, 3, 4, 5, 6, false] }],
121 [{ color: [] }, { background: [] }], // dropdown with defaults from theme
122 [{ font: [] }],
123 [{ align: [] }],
124 ['clean'], // remove formatting button
125 ],
126 handlers: {
127 image() {
128 this.quill.format('image', false) // disable the quill internal upload image method
129 self.imgHandler(this)
130 },
131 },
132 },
133 },
134 },
135 }
136 },
137 watch: {
138 res(newRes, oldRes) {
139 if (this.$auth.$storage.getUniversal('user')) {
140 this.isAuthor =
141 newRes.users_permissions_user.id ===
142 this.$auth.$storage.getUniversal('user').id
143 }
144 },
145 },
146 created() {
147 const res = this.$store.getters.getNote
148 console.log('res', res)
149 this.title = res.title
150 this.res = res
151 this.content = res.content ? res.content : ''
152 },
153 methods: {
154 handleRemove(file, fileList) {
155 },
156 handlePreview(file) {
157 },
158 onEditorBlur(editor) {
159 },
160 onEditorFocus(editor) {
161 this.$refs['quill-editor'].firstElementChild
162 .getElementsByTagName('h1')
163 .forEach((el) => console.log(el.textContent))
164 },
165 onEditorReady(editor) {
166 },
167 onEditorChange({ editor, html, text }) {
168 this.content = html
169 },
170 imgHandler(handle) {
171 this.quill = handle.quill
172 const inputfile = this.$refs.input
173 inputfile.click()
174 },
175 async update() {
176 const params = {
177 title: this.$refs[
178 'quill-editor'
179 ].firstElementChild?.getElementsByTagName('h1')[0]?.textContent,
180 content: this.content,
181 }
182 console.log('params', params)
183 await this.$axios.$put(`/notes/${this.$route.params.id}`, params, {
184 headers: {
185 Authorization: `Bearer ${this.token}`,
186 },
187 })
188 },
189 },
190}
191</script>
192<style scoped>
193.container {
194 margin: 0 auto;
195 padding: 50px 0;
196}
197.ql-toolbar span {
198 color: #fff;
199}
200</style>
What we’ve done here is to build out the layout of the notes page, where users could create and edit notes.
The application takes the text-content of the first <h1></h1>
tag to be the name of the note.
The page has a middleware to check if a user is logged in or not and to check user permissions. We also fetch data and commit it to the store in the asyncData hook.
Building our store
Nuxt.js supports vuex right out the box
Execute the following lines
1cd store
Fill up the index.js
file with the following lines of code
1export const state = () => ({
2 note: {}
3})
4export const getters = {
5 getNote: (state) => state.note
6}
7export const actions = {
8}
9export const mutations = {
10 setNote: (state, currentNote) => (state.note = currentNote),
11 updateEditors: (state, editors) => (state.note.Editors = editors)
12}
We’ve set up the vuex store completely, next we’ll look at image upload.
We need a way for users to upload images to our Strapi backend, and our application does not support that right now, what happens is that the image is displayed in our editor but not uploaded to the server so on page reload we have our other data but not the image.
Let’s fix that shall we?
Execute the following lines
1cd pages
Open up the notes/_id.vue
and add the following lines to your methods object.
1async doUpload() {
2 const file = this.$refs.formInput
3 const formdata = new FormData(file) // Create a form object
4 console.log(file)
5 if (
6 !this.$auth.$storage.getUniversal('user') ||
7 this.res.users_permissions_user.id ===
8 this.$auth.$storage.getUniversal('user').id
9 ) {
10 const res = await this.$axios.post(
11 `/upload`,
12 formdata
13 )
14 console.log('res', res)
15 const { index } = this.quill.getSelection()
16 this.quill.insertEmbed(
17 index,
18 'image',
19 `${res.data[0].formats.small.url}`
20 )
21 console.log(this.content)
22 }
23},
That’s all for Image uploads, Strapi makes it really easy to pull off. To know more about image uploads with Strapi, check the Strapi documentation.
In order to host our resources on Aws-s3 we need an AWS account, head over to the AWS website, sign up to get your AWS credentials. In order to see your credentials:
My Security Credentials
. That should take you to a page containing your security credentials.Now, that we’ve gotten our security credentials, we can integrate AWS-s3 in our Strapi application.
To use AWS-s3 in our Strapi application we need to follow the steps to install a new provider.
1 yarn add strapi-provider-upload-aws-s3 //using yarn
2
3 npm install strapi-provider-upload-aws-s3 --save //using npm
services
, under storage
, click on s3
.create bucket
, which should take you to the bucket configuration page, choose your setting and finally click create bucket
. config/plugins.js
file as follows1 module.exports = ({ env }) => ({
2 upload: {
3 provider: 'aws-s3',
4 providerOptions: {
5 accessKeyId: env('AWS_ACCESS_KEY_ID'),
6 secretAccessKey: env('AWS_ACCESS_SECRET'),
7 region: 'aws-region',
8 params: {
9 Bucket: 'my-bucket',
10 },
11 },
12 },
13 });
.env
file and add your AWS_ACCESS_SECRET
and AWS_ASCESS_KEY_ID
.Please replace my-bucket
in the code snippet above with the name of the bucket you created in instruction 3. Also you may leave the region
as an empty string or fill in your original region from the Aws console, be aware that some regions do not support s3.
We need a way for users to share notes, request edit access and add editors. That’s what we’re going to build in this section.
Build the Strapi backend for email sharing
Open up your Strapi backend in your code editor. We’ll be using email-templates and pug for email templating.
To install email-templates and pug, execute the following lines of code.
1yarn add email-templates pug //using yarn
2
3npm install email-templates pug //using npm
Now we can start building our templates
1mkdir emails
2cd emails
3mkdir addEditors requestAccess shareLink
4cd addEditors
5touch html.pug subject.pug text.pug
Fill up the subject.pug
file with the following
1= `Hi ${email}, Permission from NoteApp`
Fill up the html.pug
file with the following
1h1 Hey there
2 p.
3 #{author} just granted you edit access to a note. We just need to make sure you are aware.
4 Please, click the link below to check the note.
5a(href=`http://localhost:3000/notes/${id}`) View note!
then
1cd ..
2cd requestAccess
3touch html.pug subject.pug text.pug
Fill up the subject.pug
file with the following
1= `Hi ${author}, Permission from NoteApp`
Fill up the html.pug
file with the following
1h1 Hey there
2 p.
3 #{email} would like to request edit access of your note. We just need to make sure you are aware.
4 Please, click the button below and to grant edit access.
5a(href=`http://localhost:3000/notes/${id}`) grant access!
then
1cd ..
2cd shareLink
3touch html.pug subject.pug text.pug
Fill up the subject.pug
file with the following
1= `Hi ${author}, Permission from NoteApp`
Fill up the html.pug
file with the following
1h1 Hey there
2 p.
3 #{sharer} just invited you to view a note. Please, click the link below to check the note.
4a(href=`http://localhost:3000/notes/preview/${id}`) View note!
Next, carry out the following
1cd ../..
2cd api
3cd note
4cd services
Open up the note.js
file and fill it up as follows.
1'use strict';
2/**
3 * Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/services.html#core-services)
4 * to customize this service
5 */
6
7const Email = require('email-templates');
8const email = new Email({
9 message: {
10 from: process.env.GMAIL_USER
11 },
12 send: true,
13 transport: {
14 service: 'Gmail',
15 host: 'smtp.gmail.com',
16 port: 465,
17 ssl: true,
18 tls: true,
19 auth: {
20 user: process.env.GMAIL_USER, // your gmail username
21 pass: process.env.GMAIL_PASSWORD //your gmail password
22 }
23 }
24});
25module.exports = {
26 send: (to, template, locals) => {
27 // Setup e-mail data.
28 return new Promise((resolve, reject) => {
29 resolve(
30 email.send({
31 template,
32 message: {
33 to
34 },
35 locals
36 })
37 );
38 });
39 }
40};
Rename your .env.example
file to .env
then open it up and fill in your credentials.
1GMAIL_PASSWORD=yourpassword
2GMAIL_USER=example@gmail.com
Execute the following
1cd ..
2cd config
Open up the routes.json
file and add the following to it
1{
2 "method":"POST",
3 "path": "/notes/:id/addEditors",
4 "handler": "note.addEditors",
5 "config": {
6 "policies": []
7 }
8},
9{
10 "method":"POST",
11 "path": "/notes/:id/requestEditAccess",
12 "handler": "note.requestEditAccess",
13 "config": {
14 "policies": []
15 }
16},
17{
18 "method":"POST",
19 "path": "/notes/:id/shareLink",
20 "handler": "note.shareLink",
21 "config": {
22 "policies": []
23 }
24}
then finally,
1cd ..
2cd controllers
Open up the note.js
file and fill it up with the following code
1'use strict';
2/**
3 * Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/controllers.html#core-controllers)
4 * to customize this controller
5 */
6module.exports = {
7 async addEditors(ctx) {
8 const { editorEmail, noteAuthor } = ctx.request.body;
9 const id = ctx.params.id;
10 const locals = {
11 email: editorEmail,
12 id,
13 author: noteAuthor
14 };
15 strapi.services.note
16 .send(editorEmail, 'addEditors', locals)
17 .then((res) => console.log(res.originalMessage))
18 .catch((err) => console.log(err));
19 ctx.send({
20 ok: true
21 });
22 console.log(ctx.request.body);
23 },
24 async requestEditAccess(ctx) {
25 const { noteAuthor, userEmail } = ctx.request.body;
26 const id = ctx.params.id;
27 const locals = {
28 email: userEmail,
29 id,
30 author: noteAuthor
31 };
32 strapi.services.note
33 .send(noteAuthor, 'requestAccess', locals)
34 .then((res) => console.log(res.originalMessage))
35 .catch((err) => console.log(err));
36 ctx.send({
37 ok: true
38 });
39 },
40 async shareLink(ctx) {
41 console.log(ctx.request.body);
42 const { sharer, recievers } = ctx.request.body;
43 const id = ctx.params.id;
44 const locals = {
45 id,
46 sharer
47 };
48 recievers.forEach((reciever) => {
49 locals.reciever = reciever;
50 strapi.services.note
51 .send(reciever, 'shareLink', locals)
52 .then((res) => console.log(res.originalMessage))
53 .catch((err) => console.log(err));
54 ctx.send({
55 ok: true
56 });
57 });
58 }
59};
Now, our Strapi application supports email-sharing, next we’ll integrate the email-sharing ability in the Nuxt.js front-end.
Updating our **notes/_id.vue**
file
Open up your notes/_id.vue
file and add the following lines of code to your methods object
1async addNewEditor(e) {
2 e.preventDefault()
3 try {
4 const [newEditor] = await this.$strapi.$users.find({
5 email: this.editorEmail,
6 })
7 console.log(newEditor)
8 const oldEditors = this.res.Editors
9 if (
10 newEditor !== undefined &&
11 oldEditors.findIndex((editor) => editor.email === newEditor.email) ===
12 -1
13 ) {
14 const updatedEditors = [...oldEditors, newEditor]
15 // call api to send mail
16 await this.$axios.$post(
17 `/notes/${this.res.id}/addEditors`,
18 {
19 editorEmail: this.editorEmail,
20 noteAuthor: this.$auth.$storage.getUniversal('user').email,
21 },
22 {
23 headers: {
24 Authorization: `Bearer ${this.token}`,
25 },
26 }
27 )
28 // update editors in strapi backend
29 await this.$axios.$put(
30 `/notes/${this.$route.params.id}`,
31 {
32 Editors: updatedEditors,
33 },
34 {
35 headers: {
36 Authorization: `Bearer ${this.token}`,
37 },
38 }
39 )
40 // update editors in store
41 this.$store.commit('updateEditors', updatedEditors)
42 this.success = `Author added Successfully`
43 this.error = ''
44 } else {
45 this.error = `The user with that email doesn't exist or is already an editor`
46 this.success = ''
47 console.log(`${this.error}`)
48 }
49 this.editorEmail = ''
50 } catch (e) {
51 this.$nuxt.error(e)
52 }
53 },
54 toggleAddEditors() {
55 this.addEditor = !this.addEditor
56 this.error = ''
57 this.success = ''
58 },
Now, we can add editors using their emails, provided the user with the email is a user in our database.
Building our preview page
Execute the following lines of code to create a notes/preview/_id.vue
page.
1cd pages
2cd notes
3mkdir preview
4touch _id.vue
Open up the _id.vue
file and fill it up with the following code
1<template>
2 <div>
3 <Nav />
4
5 <div class="w-4/5 sm:w-2/3 mx-auto">
6 <div v-if='error' class="hex absolute left-0 top-0 h-full w-full">
7 <div
8 class="border-3 bg-white sm:w-1/3 w-4/5 shadow-lg p-10 mx-auto mt-32"
9 >
10 <p class="my-3">{{ error }}</p>
11 <button class="button--blue" @click="clearError">Ok</button>
12 </div>
13 </div>
14 <div class="flex items-center space-x-5">
15 <NuxtLink
16 v-if="isEditor"
17 class="button--green"
18 :to="`/notes/${note.id}`"
19 >
20 Edit
21 <span><font-awesome-icon :icon="['fas', 'pen']" /></span>
22 </NuxtLink>
23 <button
24 v-if="!isEditor"
25 class="button--green"
26 @click="requestEditAccess"
27 >
28 Request Edit Permissions
29 </button>
30 <Share v-if="user" :id="note.id" class="z-10" />
31 <p class="cursor-pointer" @click="doCopy">
32 Copy Link
33 <span><font-awesome-icon :icon="['fas', 'copy']" /></span>
34 </p>
35 </div>
36 <!--<h1 class="my-3 text-4xl font-black">{{ note.title }}</h1>-->
37 <div
38 v-quill:myQuillEditor="editorOption"
39 class="quill-editor shadow-2xl"
40 :content="note.content"
41 @ready="onEditorReady($event)"
42 @focus="onEditorFocus($event)"
43 ></div>
44 </div>
45 </div>
46</template>
47<script>
48export default {
49 async asyncData({ $strapi, route }) {
50 const note = await $strapi.$notes.findOne(route.params.id)
51 return { note }
52 },
53 data() {
54 return {
55 error: '',
56 user: this.$auth.$storage.getUniversal('user'),
57 message: 'http://localhost:3000' + this.$route.fullPath,
58 token: this.$auth.$storage.getUniversal('jwt'),
59 editorOption: {
60 modules: {
61 toolbar: '',
62 },
63 },
64 }
65 },
66 computed: {
67 isEditor() {
68 return (
69 this.$auth.$storage.getUniversal('user') !== null &&
70 (this.$auth.$storage.getUniversal('user').id ===
71 this.note.users_permissions_user.id ||
72 this.note.Editors.findIndex((editor) => {
73 return editor.id === this.$auth.$storage.getUniversal('user').id
74 }) !== -1)
75 )
76 },
77 },
78 methods: {
79 onEditorReady(editor) {
80 editor.disable()
81 },
82 onEditorFocus(editor) {
83 editor.disable()
84 },
85 assignValue(e) {
86 console.log(e.target.value)
87 this.recieverEmail = e.target.value
88 },
89 async requestEditAccess(e) {
90 e.preventDefault()
91 console.log(`requesting edit access...`)
92 if (this.$auth.$storage.getUniversal('user') !== null) {
93 await this.$axios.$post(
94 `/notes/${this.note.id}/requestEditAccess`,
95 {
96 noteAuthor: this.note.users_permissions_user.email,
97 userEmail: this.$auth.$storage.getUniversal('user').email,
98 },
99 {
100 headers: {
101 Authorization: `Bearer ${this.token}`,
102 },
103 }
104 )
105 } else {
106 this.error = 'please login to request access'
107 console.log('please login to request access')
108 }
109 },
110 clearError() {
111 this.error = ""
112 },
113 },
114}
115</script>
116<style scoped>
117.container {
118 width: 60%;
119 margin: 0 auto;
120 padding: 50px 0;
121 background: #fff;
122}
123</style>
Building the Share component
Execute the following lines of code to create a Share.vue
file
1cd components
2touch Share.vue
Open up the file and fill it up with the following code
1<template>
2 <div>
3 <button class="button--blue my-3" @click="toggleShare">
4 Share
5 <span><font-awesome-icon :icon="['fas', 'share']" /></span>
6 </button>
7 <div v-if="share" class="hex absolute left-0 top-0 h-full w-full">
8 <div
9 class="border-3 bg-white sm:w-1/3 w-4/5 shadow-lg p-10 mx-auto mt-32"
10 >
11 <form @submit="shareLink">
12 <input v-model="emails" type="email" class="w-full p-3 my-5" placeholder="Email" />
13 <button type="submit" v-if="emails" class="button--blue my-3">Send</button>
14 </form>
15 <button type="submit" class="button--green" @click="toggleShare">
16 Cancel
17 </button>
18 </div>
19 </div>
20 </div>
21</template>
22<script>
23export default {
24 name: 'Share',
25 props: ['id'],
26 data() {
27 return {
28 share: false,
29 emails: '',
30 }
31 },
32 methods: {
33 async shareLink(e) {
34 e.preventDefault()
35 console.log(`sharing link`)
36 const token = this.$auth.$storage.getUniversal('jwt')
37 if (this.$auth.$storage.getUniversal('user') !== null) {
38 const recieverEmails = this.emails.split(', ')
39 console.log(recieverEmails)
40 await this.$axios.$post(
41 `/notes/${this.id}/shareLink`,
42 {
43 sharer: this.$auth.$storage.getUniversal('user').email,
44 recievers: recieverEmails,
45 },
46 {
47 headers: {
48 Authorization: `Bearer ${token}`,
49 },
50 }
51 )
52 this.emails = ''
53 } else {
54 console.log(`please login to share`)
55 }
56 },
57 toggleShare() {
58 this.share = !this.share
59 },
60 },
61}
62</script>
63<style scoped></style>
Now, we have our share component working, Let’s create a way for users to copy the link of an article.
We’ll use a package called nuxt-clipboard, execute the following lines of code to install nuxt-clipboard
1yarn add nuxt-clipboard //using yarn
2
3npm install nuxt-clipboard //using npm
Open up your nuxt.config.js
file and add the following lines of code
1modules: [
2 //...other modules
3 'nuxt-clipboard'
4]
Execute the following line of code
1cd pages
2cd notes
3cd preview
Open up the _id.vue
file and add the following lines of code to the methods object.
1doCopy() {
2 this.$copyText(this.message).then(
3 function (e) {
4 alert('Copied')
5 },
6 function (e) {
7 alert('Can not copy')
8 }
9 )
10},
Now, we are able to copy links and share links.
In order to host our application on Heroku, we need to create a Heroku account. Head over to Heroku to create an account, if you don’t have one already.
To get started with hosting our Strapi Application on Heroku, visit the Strapi docs. The docs contains details about Heroku installation for different machine types and also a basic walk through. When you’re done with the installation, you can proceed as follows:
1 heroku login
Follow the instructions in order to login.
1 heroku create
Settings
.settings
, click reveal config vars
..env
file, then add the config variables
to your Heroku application. e.g Enter DATABASE_URI
as a key
, then you add it’s respective value to the value
field. Do the same for all your environment variables.1 git push heroku master
The deployment may take a few minutes. At the end, logs will display the URL of your project (e.g. https://empty-streams-80976.herokuapp.com
).
Now that we have our Strapi application hosted on Heroku, and we also have the URL of the project, it’s time to make some changes to the Nuxt.js frontend,
Open up your Nuxt.js frontend in your favorite code editor, navigate to the nuxt.config.js
file and add the following lines of code.
1axios: {
2 baseURL: 'https://your-strapi-Url.herokuapp.com'
3},
4
5strapi: {
6 ...,
7 url: 'https://your-strapi-url.herokuapp.com'
8}
What we’ve done here is to change the baseURL
of axios
. i.e the default URL with which all API calls using axios
will be prefixed with. We also did the same thing for the @nuxtjs/strapi
package, now all request using the @nuxtjs/strapi
module will be made to our hosted Strapi application. Now, we can make changes to all API calls that make use of axios
. Change all API calls that were of the form http:localhost:1337/notes
to just /notes
i.e http:localhost:1337/…
to /…
, the axios baseURL
does the prefixing for us.
Finally we can host the Nuxt.js Application on Heroku.
Follow the instructions 1 and 2 in the hosting the strapi application on heroku
section, then proceed as follows:
1 heroku buildpacks:set heroku/nodejs
Settings
.settings
, click reveal config vars
.Host
has a value of 0.0.0.0
NODE_ENV
has a value of production
NODE_CONFIG_PRODUCTION
has a value of false
1 git push heroku master
We have our application hosted, however if you try to log into the application using any of the social providers you get the following error:
http://localhost:1337/?error=Grant%3A%20missing%20session%20or%20misconfigured%20provider#_=_
Let’s see what causes this and how we can resolve it.
From the Strapi documentation, it is stated that:
config/server.js
: Setting up the server urlhttp://localhost:1337
. Don't forget to check the backend url set in the example app at src/config.js
.Now we know what causes Grant’s Error, let’s see how we can solve it. Luckily for us, solving this error does not require much effort, kudos to Strapi for making things easy.
config/server.js
file then add the following line of code1 module.exports = ({ env }) => ({
2 ...,
3 url: env('', 'https://your-strapi-url.herokuapp.com/'),
4 ...
5 });
Replace the localhost URL with that of your hosted Strapi application and voila, your application begins to work as normal.
That’s the end of this series, hopefully you now have what it takes to tackle social authentication with Strapi backend and nuxt.js frontend. Hope you had a good time reading this tutorial, until next time.
This tutorial is divided into 3 parts
This article is a guest post by Alex Godwin. He wrote this blog post through the Write for the Community program.
Alexander Godwin is a Software Developer and writer that likes to write code and build things. Learning by doing is the best way and it's how Alex helps others learn. Follow him on Twitter (@oviecodes)