Vue

Learn how to configure Vue for use with Filebase.

What is Vue?

Vue is an open-source JavaScript framework used for building user interfaces and single-page applications (SPAs). Vue is designed to be incrementally adoptable, which means that it can be integrated into an existing project or used to build a new project from scratch. It is also highly modular, with a core library that focuses on the view layer of an application and a set of optional libraries that can be used to add more functionality.

Read below to learn how to use Vue with Filebase.

Prerequisites:

1. Start by creating a new directory for your Vue app, then navigate into that directory and initialize the workspace:

mkdir filebase-vue-app

cd filebase-vue-app

npm init

Accept the default options when prompted.

2. Next, install the following packages:

npm install --save express multer dotenv cors body-parser mongodb multer-s3 aws-sdk

3. Create an .env file with the following environment variables:

DATABASE=mongodb://127.0.0.1:27017/
PORT=5005
AWSAccessKeyId=FILEBASE_ACCESS_KEY
AWSSecretKey=FILEBASE_SECRET_KEY
AWSRegion=us-east-1
AWSBucket=filebase-bucket-name

Replace the environment variable values with the values that match your Filebase account and MongoDB configuration.

4. Create a new file called index.js that contains the following code:

const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const dotenv = require('dotenv')
const aws = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
const mongodb = require('mongodb')

dotenv.config()
const app = express()
//middleWare
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())

aws.config.update({
  apiVersion: "2006-03-01",
  accessKeyId: process.env.AWSAccessKeyId,
  secretAccessKey: process.env.AWSSecretKey,
  region: process.env.AWSRegion
  endpoint: "https://s3.filebase.com",
})

const s3 = new aws.S3()

const upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: process.env.AWSBucket,
    acl: "public-read",
    contentType: multerS3.AUTO_CONTENT_TYPE,
    metadata: (req, file, cb) => {
      cb(null, { fieldName: file.fieldname})
    },
    key: (req, file, cb) => {
      cb(null, Date.now().toString() + file.originalname)
    }
  })
})

app.get('/', async (req, res) => {
  res.status(200).send('S3 Upload Backend')
})

app.post('/', upload.single('photo'), async (req, res) => {
  try {
    const users = await loadUsersCollection();
    await users.insertOne({
      first_name: req.body.first_name,
      last_name: req.body.last_name,
      phone: req.body.phone,
      email: req.body.email,
      gender: req.body.gender,
      photo: req.file.location,
    })
    res.status(201).send('success')
  }
  catch (err) {
    res.status(500).send({
      error: 'error occured creating user'
    })
  }
})
async function loadUsersCollection () {
  const client = await mongodb.MongoClient.connect(process.env.DATABASE, {
    useNewUrlParser: true,
    useUnifiedTopology: true
  })
  return client.db('vue-multers3').collection('users')
}

const port = process.env.PORT || 5000
app.listen(port, () => {
  console.log(server started on port ${port})
});

5. Then, start the server with the command:

node index.js

6. Next, install the Vue package to get started on the front end of the Vue app:

npm install -g @vue/cli

7. Then, create a new Vue app with the command:

vue create filebase-vue-app

8. Navigate inside the new Vue app directory and add the Vuetify package:

cd vue-multers3-frontend

vue add vuetify

9. Then, start the Vue front end with the command:

npm run serve

10. Navigate to the localhost:8080 address to view the Vue app:

11. Next, create a new file called register.vue in the components folder. Insert the following code into this file:

<template>
  <v-container fluid class="my-3">
    <v-row align="center" justify="center">
      <v-col cols="12">
        <v-card style="width: 100%" flat>
          <div class="text-center pt-6"><h1 class="font-weight-light display-2 primary--text">Register!</h1></div>
          <v-row class="pa-4">
            <v-col cols="12" md="6">
              <v-img src="@/assets/contactus.jpg" class="d-block ml-auto mr-auto" max-width="350px" />
            </v-col>
            <v-col cols="12" md="6">
              <v-form ref="forma" v-model="form1" lazy-validation>
                <v-row>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="first_name"
                      label="First Name"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="last_name"
                      label="Last Name"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="phone"
                      label="Phone"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="email"
                      label="Email"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="age"
                      type="number"
                      label="Age"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-file-input
                      v-model="photo"
                      :rules="photoRules"
                      accept="image/png, image/jpeg"
                      label="Pick a Picture"
                      outlined
                      dense
                      prepend-icon=""
                      prepend-inner-icon="mdi-camera"
                      hide-details
                    >
                    </v-file-input>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="password"
                      :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
                      :rules="passwordRules"
                      :type="show1 ? 'text' : 'password'"
                      hint="At least 6 characters"
                      @click:append="show1 = !show1"
                      outlined
                      dense
                      hide-details
                    >
                    </v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="6"
                  >
                    <v-text-field
                      v-model="confirmPassword"
                      :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
                      :rules="passwordConfirmation"
                      :type="show1 ? 'text' : 'password'"
                      hint="At least 6 characters"
                      @click:append="show1 = !show1"
                      outlined
                      dense
                      hide-details
                    >
                    </v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                    md="12"
                  >
                    <v-text-field
                      v-model="address"
                      label="Address"
                      outlined
                      dense
                      hide-details
                    ></v-text-field>
                  </v-col>
                  <v-col
                    cols="12"
                  >
                    <v-btn
                      color="primary"
                      rounded
                      block
                      class="mt-3"
                      @click="onSubmit"
                      :disabled="!form1"
                    >
                      Submit
                    </v-btn>
                  </v-col>
                </v-row>
              </v-form>
            </v-col>
          </v-row>
        </v-card>
      </v-col>
    </v-row>
    <v-snackbar
      v-model="snackbar"
      :timeout="timeout"
      color="success"
      top
    >
      {{ message }}
    </v-snackbar>
  </v-container>
</template>

<script>
export default {
  data () {
    return {
      message: 'success',
      snackbar: false,
      timeout: 2000,
      show1: false,
      form1: false,
      submitSuccess: false,
      first_name: '',
      last_name: '',
      phone: '',
      email: '',
      age: '',
      photo: null,
      address: '',
      password: '',
      confirmPassword: '',
      photoRules: [v => !v || v.size < 5000000 || 'Image should be less than 5MB'],
      passwordRules: [v => !!v || 'Password is required',
        v => (v && v.length >= 6) || 'password should be 6 characters'],
      passwordConfirmation: [v => !!v || 'Password is required',
        v => (v === this.password) || 'password should match'],
      required: [value => !!value || 'Required.']
    }
  }
}
</script>

<style>
</style>

12. Then, insert the following code in the app.vue file:

<template>
  <v-app>
    <v-app-bar
      app
      color="primary"
      dark
    >
      <div class="d-flex align-center">
        <v-img
          alt="Vuetify Logo"
          class="shrink mr-2"
          contain
          src="<https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png>"
          transition="scale-transition"
          width="40"
        />

        <v-img
          alt="Vuetify Name"
          class="shrink mt-1 hidden-sm-and-down"
          contain
          min-width="100"
          src="<https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png>"
          width="100"
        />
      </div>

      <v-spacer></v-spacer>

      <v-btn
        href="<https://github.com/vuetifyjs/vuetify/releases/latest>"
        target="_blank"
        text
      >
        <span class="mr-2">Latest Release</span>
        <v-icon>mdi-open-in-new</v-icon>
      </v-btn>
    </v-app-bar>

    <v-main>
      <Register/>
    </v-main>
  </v-app>
</template>

<script>
import Register from './components/Register'
export default {
  name: 'App',
  components: {
    Register
  },
  data: () => ({
    //
  })
}
</script>

13. If you refresh your Vue app, it should now resemble this:

14. Next, install the axois package:

npm install axios

15. Then, create the file registeraxios.vue in the components folder and insert the following code:

<template>
      <v-container fluid class="my-3">
        <v-row align="center" justify="center">
          <v-col cols="12">
            <v-card style="width: 100%" flat>
              <div class="text-center pt-6"><h1 class="font-weight-light display-2 primary--text">Register!</h1></div>
              <v-row class="pa-4">
                <v-col cols="12" md="6">
                  <v-img src="@/assets/contactus.jpg" class="d-block ml-auto mr-auto" max-width="350px" />
                </v-col>
                <v-col cols="12" md="6">
                  <v-form ref="forma" v-model="form1" lazy-validation>
                    <v-row>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="first_name"
                          label="First Name"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="last_name"
                          label="Last Name"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="phone"
                          label="Phone"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="email"
                          label="Email"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="age"
                          type="number"
                          label="Age"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-file-input
                          v-model="photo"
                          :rules="photoRules"
                          accept="image/png, image/jpeg"
                          label="Pick a Picture"
                          outlined
                          dense
                          prepend-icon=""
                          prepend-inner-icon="mdi-camera"
                          hide-details
                        >
                        </v-file-input>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="password"
                          :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
                          :rules="passwordRules"
                          :type="show1 ? 'text' : 'password'"
                          hint="At least 6 characters"
                          @click:append="show1 = !show1"
                          outlined
                          dense
                          hide-details
                        >
                        </v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="6"
                      >
                        <v-text-field
                          v-model="confirmPassword"
                          :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
                          :rules="passwordConfirmation"
                          :type="show1 ? 'text' : 'password'"
                          hint="At least 6 characters"
                          @click:append="show1 = !show1"
                          outlined
                          dense
                          hide-details
                        >
                        </v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                        md="12"
                      >
                        <v-text-field
                          v-model="address"
                          label="Address"
                          outlined
                          dense
                          hide-details
                        ></v-text-field>
                      </v-col>
                      <v-col
                        cols="12"
                      >
                        <v-btn
                          color="primary"
                          rounded
                          block
                          class="mt-3"
                          @click="onSubmit"
                          :disabled="!form1"
                        >
                          Submit
                        </v-btn>
                      </v-col>
                    </v-row>
                  </v-form>
                </v-col>
              </v-row>
            </v-card>
          </v-col>
        </v-row>
        <v-snackbar
          v-model="snackbar"
          :timeout="timeout"
          color="success"
          top
        >
          {{ message }}
        </v-snackbar>
      </v-container>
    </template>
    
    <script>
    import axios from 'axios'
    const url = '<http://localhost:5005/>'
    export default {
      data () {
        return {
          message: 'submission success',
          snackbar: false,
          timeout: 4000,
          show1: false,
          form1: false,
          submitSuccess: false,
          first_name: '',
          last_name: '',
          phone: '',
          email: '',
          age: '',
          photo: null,
          address: '',
          password: '',
          confirmPassword: '',
          photoRules: [v => !v || v.size < 5000000 || 'Image should be less than 5MB'],
          passwordRules: [v => !!v || 'Password is required',
            v => (v && v.length >= 6) || 'password should be 6 characters'],
          passwordConfirmation: [v => !!v || 'Password is required',
            v => (v === this.password) || 'password should match'],
          required: [value => !!value || 'Required.']
        }
      },
      computed: {
        photoValidation () {
          if (this.photo) {
            return true
          } else return false
        }
      },
      methods: {
        clearForm () {
          this.$refs.forma.reset()
        },
        async onSubmit () {
          if (this.$refs.forma.validate() && this.photoValidation) {
            const blob = this.photo
            const formData = new FormData()
            formData.append('first_name', this.first_name)
            formData.append('last_name', this.last_name)
            formData.append('photo', blob)
            formData.append('phone', this.phone)
            formData.append('email', this.email)
            formData.append('age', this.age)
            formData.append('password', this.password)
            formData.append('address', this.address)
    
            axios.post(url, formData).then((res) => {
              if (res.status === 201) {
                console.log(res)
                this.clearForm()
                this.snackbar = true
              }
            }).catch((err) => {
              console.log(err.response)
            })
          }
        }
      }
    }
    </script>
    
    <style>
    
    </style>

16. Now, when a user submits the form in the Vue app, the image file they include in the form will be uploaded to the configured Filebase bucket!

Last updated