Introduction
JAMSTACK is a JavaScript-powered stack that enables you to harness the powers of JavaScript, APIs, and markups. You can build with the JAMSTACK by using the various JavaScript frameworks and libraries and integrate with serverless functions or APIs. It is fast, lean and inexpensive.
In this tutorial, you will learn how to build JAMSTACK apps with a feature to upload images. You will use Cloudinary to power the upload functionality and Auth0 for authentication. This can come in handy when building photo albums, e-commerce applications, and business websites.
For a brief introduction to Vue.js, you can check out this article, Getting Started with Vue JS: The Progressive JavaScript Framework.
Prerequisites
Basic familiarity with JavaScript, and ES6 and Vue.js).
A text editor. E.g. Sublime Text, Visual Studio Code, Atom, etc.
A Cloudinary account. You can sign up for a Cloudinary account for free.
An Auth0 account. You may create a free Auth0 account here.
Install the Project Dependencies
You need the Node.js runtime and the npm to use Vue.js. The Node package manager, npm will enable you to use Node.js on the command-line interface, CLI. If you do not have Node.js or npm installed, you can follow the instructions here to install them on your machine.
Another tool that you need to follow in this tutorial is the Vue CLI package. The vue
library will provide the capabilities of the Vue.js framework while vue-cli
will enable you to use certain terminal commands to interact with Vue.js and your project.
You can go ahead to install Vue and the vue-cli
tool if you have Node.js and npm on your machine already. Use the following terminal commands to install the two packages respectively.
npm install vue
npm install -g @vue/cli
In the next section, you will set up your Vue.js project.
Create Vue Project
Use the following vue-cli
command to set up a Vue.js project on your terminal. You may name the project vue-photo-uploader
.
vue create vue-photo-uploader
Select Vue.js 2 version in the options prompted and allow Vue to scaffold the project folders and files for you. You should get a message that looks like the following in your terminal.
📄 Generating README.md...
🎉 Successfully created project vue-photo-uploader.
👉 Get started with the following commands:
Now, you need to set up the new project to handle file uploads. This is the purpose of the next section of the tutorial.
Configue the Vue.js Project for File Uploads
To handle file uploads, you will need to create an interface for your users to be able to upload files. To do that, modify the App.vue
file of your project as follows:
<template>
<div id="app">
<div>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Learn how to upload photos with Cloudinary"/>
</div>
<div>
<div>
<button @click="openUploadModel">Add Photo</button>
</div>
</div>
</div>
</template>
In the above code, you edited the message to be displayed by the HelloWorld
component. You changed it to learn how to upload photos with Cloudinary
. In addition, you created a button that calls the openUploadModel
method. The method will open the Cloudinary upload modal when the button is clicked. Now, add the method inside the part of the App.vue
file as follows:
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld,
},
methods: {
openUploadModel() {
}
}
</script>
The full code of the App.vue
file becomes:
<template>
<div id="app">
<div>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Learn how to upload photos with Cloudinary"/>
</div>
<div>
<div>
<button @click="openUploadModel">Add Photo</button>
</div>
</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld,
},
methods: {
openUploadModal() {
}
}
</script>
Configure Cloudinary Upload Widget
Login and obtain parameters
Login to your Cloudinary account and go to your console dashboard and get your cloud name. Also, get your upload preset from your Cloudinary acount settings. Copy the two values somewhere because you will need them in your Vue.js project.
Configure Upload Preset settings
In your account settings page, scroll down the Upload Settings tab to the Upload Preset section as shown below.
Activate the unsigned uploading feature by clicking the Enable unsigned uploading link. The link should change to Signed as shown below.
If you don't enable unsigned upload preset, you might get the following error.
Upload preset must be whitelisted for unsigned uploads
Next, navigate to the index.html
page in the public
folder of your project. Then, include the Cloudinary widget script inside the
<body>
<script src="https://widget.cloudinary.com/v2.0/global/all.js" type="text/javascript"></script>
...
</body>
Next, modify the openUploadModal
of the App.vue
file as follows:
...
openUploadModal() {
window.cloudinary.openUploadWidget(
{ cloud_name: '<CLOUD_NAME>',
upload_preset: '<UPLOAD_PRESET>'
},
(error, result) => {
if (!error && result && result.event === "success") {
console.log('Image uploaded..: ', result.info);
}
}).open();
}
In the above code, you added the logic of the @click
event of the button by which calls the openUploadWidget
method that activates the Cloudinary file upload modal.
Replace CLOUD_NAME
and UPLOAD_PRESET
in the above code with the values you got from your Cloudinary account.
Next, run the project on your terminal:
npm run serve
Then, navigate to 127.0.0.1:8000
on your browser to add images to the application. You should get a page that looks like the following image.
Click the Add Photo
button and the upload modal will be displayed like the following.
Now, you can upload your images.
Deliver and View Images with Cloudinary
Cloudinary enables the delivery of images to the browser via its Vue.js SDK, cloudinary-vue
. Install the library to get started:
npm install cloudinary-vue
You need the following Cloudinary Vue.js components to achieve the goal of this section of the article.
CldContext
- this tag helps you to set the parameters that all the child components share.
CldImage
- the Cloudinary Image tag. It sources images from Cloudinary and makes them available on the browser.
CldTransformation
- the tag that allows you to describe the transformations that are applied to the images delivered.
Next, import the components in the <script>
section of the App.vue
file. In addition, add the image url
and public_id
to the Data property of Vue.js. These two variables will be defined later in the article.
<script>
...
import { CldContext, CldImage, CldTransformation } from 'cloudinary-vue'
export default {
name: 'App',
components: {
HelloWorld,
CldContext,
CldImage,
CldTransformation
},
data() {
return {
url: '',
publicId: ''
}
},
...
</script>
Then, apply the imported components in the template code.
<template>
<div id="app">
...
<div>
<cld-context cloudName="CLOUD_NAME">
<div style="display: flex; justify-content: center;">
<cld-image :publicId="publicId" width="250">
<cld-transformation width="200" height="200" gravity="face" crop="thumb" />
</cld-image>
<cld-image :publicId="publicId" width="250">
<cld-transformation width="200" height="200" gravity="face" crop="thumb" />
</cld-image>
<cld-image :publicId="publicId" width="250">
<cld-transformation width="200" height="200" gravity="face" crop="thumb" />
</cld-image>
<cld-image :publicId="publicId" width="250">
<cld-transformation width="200" height="200" gravity="face" crop="thumb" />
</cld-image>
<cld-image :publicId="publicId" width="250">
<cld-transformation width="200" height="200" gravity="face" crop="thumb" />
</cld-image>
</div>
</cld-context>
</div>
</div>
</template>
Don't forget to replace CLOUD_NAME
in the code above with the real value gotten from your dashboard.
Whenever you upload an image, Cloudinary assigns a public_id
to an image whenever you are uploading one.
Therefore, you can use the public_id
returned in Cloudinary's JSON response when an image is being uploaded.
So, modify the openUploadModal()
method to capture the public_id
of an uploaded image.
openUploadModal() {
window.cloudinary.openUploadWidget(
{ cloud_name: 'CLOUD_NAME',
upload_preset: 'UPLOAD_PRESET'
},
(error, result) => {
if (!error && result && result.event === "success") {
console.log('Image uploaded..: ', result.info);
//add the next 2 lines
this.url = result.info.url;
this.publicId = result.info.public_id;
}
}).open();
}
The full code of the App.vue
file is the following:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Learn how to upload photos with Cloudinary"/>
<div>
<button @click="openUploadModel">Add Photo</button>
</div>
<div>
<cld-context cloudName="diiayntjq">
<div style="display: flex; justify-content: center;">
<cld-image :publicId="publicId" width="250">
<cld-transformation width="600" height="600" gravity="face" crop="thumb" />
</cld-image>
</div>
</cld-context>
</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import { CldContext, CldImage, CldTransformation } from 'cloudinary-vue'
export default {
name: 'App',
components: {
HelloWorld,
CldContext,
CldImage,
CldTransformation
},
data() {
return {
url: '',
publicId: ''
}
},
methods: {
openUploadModel() {
window.cloudinary.openUploadWidget(
{ cloud_name: 'diiayntjq',
upload_preset: 'bi7uln2q'
},
(error, result) => {
if (!error && result && result.event === "success") {
console.log('Image uploaded..: ', result.info);
this.url = result.info.url;
this.publicId = result.info.public_id;
}
}).open();
}
}
}
</script>
Authenticating with Auth0
Auth0 allows you to authenticate and authorize users. It integrates with several languages and frameworks for building applications. Furthermore, Auth0 proves a Universal Login page to help with authenticating users. The login page redirects users back to your application after they log in.
Create an Auth0 Application
To start the authentication setup, log in to your Auth0 dashboard and follow the following steps to create an Auth0 Application.
- Click "Applications" in the left sidebar menu. Supply these parameters:
- Name: Vue.js Image Uploader
- Application type as Single Page Applications
- Click "Create" button to complete the application creation
- Click on the "Settings" tab of the newly created Application page. Set the following parameters:
-
Allowed Callback URLs:
http://localhost:8080
-
Allowed Logout URLs:
http://localhost:8080
-
Allowed Web Origins:
http://localhost:8080
-
Allowed Callback URLs:
- Scroll down the page and click on the "Save Changes" button to save the settings.
Install Auth0
Install the Client SDK for Auth0 Single-Page Applications:
npm install @auth0/auth0-spa-js
Since you need to move from one page or link to another in the Vue.js application, you will need the Vue router. Add it with the following command and choose 'yes' when asked if you'd like to use it in history mode.
vue add router
Create a new folder inside the src
folder and name it auth
. Then, create a new file inside the auth
folder and name it index.js
. Add the following code inside the file.
import Vue from "vue";
import createAuth0Client from "@auth0/auth0-spa-js";
/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
let instance;
/** Returns the current instance of the SDK */
export const getInstance = () => instance;
/** Creates an instance of the Auth0 SDK. If one has already been created, it returns that instance */
export const useAuth0 = ({
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
redirectUri = window.location.origin,
...options
}) => {
if (instance) return instance;
// The 'instance' is simply a Vue object
instance = new Vue({
data() {
return {
loading: true,
isAuthenticated: false,
user: {},
auth0Client: null,
popupOpen: false,
error: null
};
},
methods: {
/** Authenticates the user using a popup window */
async loginWithPopup(options, config) {
this.popupOpen = true;
try {
await this.auth0Client.loginWithPopup(options, config);
this.user = await this.auth0Client.getUser();
this.isAuthenticated = await this.auth0Client.isAuthenticated();
this.error = null;
} catch (e) {
this.error = e;
// eslint-disable-next-line
console.error(e);
} finally {
this.popupOpen = false;
}
this.user = await this.auth0Client.getUser();
this.isAuthenticated = true;
},
/** Handles the callback when logging in using a redirect */
async handleRedirectCallback() {
this.loading = true;
try {
await this.auth0Client.handleRedirectCallback();
this.user = await this.auth0Client.getUser();
this.isAuthenticated = true;
this.error = null;
} catch (e) {
this.error = e;
} finally {
this.loading = false;
}
},
/** Authenticates the user using the redirect method */
loginWithRedirect(o) {
return this.auth0Client.loginWithRedirect(o);
},
/** Returns all the claims present in the ID token */
getIdTokenClaims(o) {
return this.auth0Client.getIdTokenClaims(o);
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently(o) {
return this.auth0Client.getTokenSilently(o);
},
/** Gets the access token using a popup window */
getTokenWithPopup(o) {
return this.auth0Client.getTokenWithPopup(o);
},
/** Logs the user out and removes their session on the authorization server */
logout(o) {
return this.auth0Client.logout(o);
}
},
/** Use this lifecycle method to instantiate the SDK client */
async created() {
// Create a new instance of the SDK client using members of the given options object
this.auth0Client = await createAuth0Client({
...options,
client_id: options.clientId,
redirect_uri: redirectUri
});
try {
// If the user is returning to the app after authentication..
if (
window.location.search.includes("code=") &&
window.location.search.includes("state=")
) {
// handle the redirect and retrieve tokens
const { appState } = await this.auth0Client.handleRedirectCallback();
this.error = null;
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(appState);
}
} catch (e) {
this.error = e;
} finally {
// Initialize our internal authentication state
this.isAuthenticated = await this.auth0Client.isAuthenticated();
this.user = await this.auth0Client.getUser();
this.loading = false;
}
}
});
return instance;
};
// Create a simple Vue plugin to expose the wrapper object throughout the application
export const Auth0Plugin = {
install(Vue, options) {
Vue.prototype.$auth = useAuth0(options);
}
};
The options object in the above code is used to pass in the domain
and clientId
values. Hence, you have to create a JSON file called auth_config.json
in the root folder of the Vue.js application and add the following values to it. Obtain the domain and clientId values from the "Settings" tab of the Auth0 Application page and put them in the JSON file like the following.
{
"domain": "YOUR_DOMAIN",
"clientId": "YOUR_CLIENT_ID"
}
Next, import the Auth0 plugin and Vue router in the main.js
file as shown below.
...
import router from './router' // newly added line
// Import the Auth0 configuration
import { domain, clientId } from "../auth_config.json";
// Import the plugin here
import { Auth0Plugin } from "./auth";
// Install the authentication plugin here
Vue.use(Auth0Plugin, {
domain,
clientId,
onRedirectCallback: appState => {
router.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
);
}
});
Vue.config.productionTip = false;
new Vue({
router, // newly added line
render: h => h(App)
}).$mount("#app");
Configure Log in and Log out follows
Edit the App.vue file by adding two buttons for login and log out after the HelloWorld tag in the template code.
<template>
<div id="app">
...
<div v-if="!$auth.loading">
<!-- show login when not authenticated -->
<button v-if="!$auth.isAuthenticated" @click="login">Log in</button>
<!-- show logout when authenticated -->
<button v-if="$auth.isAuthenticated" @click="logout">Log out</button>
</div>
...
</div>
</template>
Next, add the login and log out methods in the list of methods inside the <script>
section
...
methods: {
// Log the user in
login() {
this.$auth.loginWithRedirect();
},
// Log the user out
logout() {
this.$auth.logout({
returnTo: window.location.origin
});
},
openUploadModel() {
...
Now, you have added the login and log-out buttons and can proceed to test the authentication flow. Go to 127.0.0.1:8000 on your web browser. You will see the login button present now as shown below.
When you click the login button, you will be directed to the Auth0 Universal login Page where your users can sign up or log in after which they will be redirected to your application.
You can study the The Complete Guide to Vue.js User Authentication with Auth0 for a detailed explanation of the Auth0 implementation in Vue.js.
Conclusion
With the knowledge gained, you can try implementing the image and video upload in your next project. You may also develop a full-fledged user profile with Auth0. Much more, you can learn about the JAMSTACK and its ecosystem as you build more JAMSTACK projects.
Thanks for your attention.
Content created for the Hackmamba Jamstack Content Hackathon using Auth0, Cloudinary and Vue.js.
Top comments (1)
that is really amazing and valuable information you have shared . thanks for sharing . Apartments for rent london england