Seamless Image Handling: Uploading to Firebase Storage and Storing Image URLs in MongoDB Using Node.js
Welcome Coders! 💻️
In this guide, we'll utilize Firebase Storage to securely store images for our application while storing image URLs within our database model.
Let's get started! 👍️
Prerequisites
Basic knowledge of Node.js
Installed Node.js and npm
Access to a MongoDB database
Set up Firebase Storage for image upload
Setting up Firebase Admin SDK with Nodejs
Create a Firebase Account:
Sign in to Firebase.
Click Go to console.
Click + Add Project and follow the prompts to create a project. You can name your project anything you want.
Once the project is created, the project configuration page is open.
Generate a new private key for Admin SDK (node js)
In the Firebase console click on the Setting icon: Project settings -> Service accounts.
Click on Generate new private key.
Rename the downloaded JSON file to Firebaseservicekey.json and move it to the project containing the main nodejs server file.
Setting up the Nodejs Server
Familiarity with Node.js and API integration will be beneficial when following this guide.
Setup and Configuration:
Initialize Node.js project: npm init
Install necessary packages:
npm install express mongoose firebase-admin multer uuid-v4 dotenv cors
Project folder structure:
create files according to folder structure.
- Server.js: main server file containing express server, routes, MongoDB connection.
require('dotenv').config()
const express=require('express')
const app=express()
const mongoose=require('mongoose')
const cors=require('cors')
//server port
const port=process.env.PORT||4000
//mongodb database url
const url=process.env.url
//artist route
const artistRoute=require('./routes/artistRoute')
app.use(express.urlencoded({extended:true}))
app.use(express.json())
app.use(cors())
//Routers
app.use('/artist',artistRoute)
//starting server
app.listen(port,()=>{
console.log(`Listening on port:${port}`)
})
//Database connection mongoDB
mongoose.connect(url)
const db=mongoose.connection
db.on('error',(error)=>{
console.error('MongoDB connection error:',error)
})
db.once('open',()=>{
console.log('Connected to MongoDB')
})
- artistModel.js: Artist schema for storing artist name, cover_img (URL & file path)
const mongoose=require('mongoose')
const artistSchema=new mongoose.Schema({
name:{
type:String,
},
picture:{
url:{
type:String,
},
filepath:{
type:String,
}
},
type:{
type:String,
default:'artist'
}
})
module.exports=mongoose.model('Artist',artistSchema)
artistRoute.js: routes to create, update, delete artist.
const express=require('express')
const router=express.Router()
//unique id
const UUID = require("uuid-v4");
//firebase bucket
const bucket=require('../firebaseadmin')
//upload file in firebase
const uploadFile=require('../firebasestorage/upload')
// Configure multer to store uploaded files in memory
const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });
//artist model
const artistModel=require('../models/artistModel')
//Create Artist
router.post('/',upload.single('file'),async(req,res)=>{
try{
//file data containing :buffer,mimetype,name,etc
const file = req.file;
//filepath of image in firebase bucket.
const filename = `aura/artist_img/${Date.now()}_${file.originalname}`;
//unique id
const uuid=UUID()
//after successfull upload of image a url is returned.
const fileURL = await uploadFile(file.buffer, filename,file.mimetype,uuid);
//creating artist model
const artist=new artistModel({
name:req.body.name,
picture:{
//url to access image
url:fileURL,
//filepath can be used to delete image in future.
filepath:filename
}
})
//saving artist model
await artist.save()
res.status(200).json({message:'Artist Saved successfully'})
}catch(err){
console.log(err)
res.status(500).send('Internal Server Error')
}
})
//Get all artist
router.get('/',async(req,res)=>{
try{
const artist=await artistModel.find()
res.status(200).json({data:artist,message:'success'})
}catch(err){
console.log(err)
res.status(500).json({data:err,message:'Internal Server Error!'})
}
})
//Update Artist
router.put('/update',upload.single('file'),async(req,res)=>{
try{
//if image file present then replace image otherwise just update req.body data.
if(!req.file){
//update artist model without image
const artist=await artistModel.findByIdAndUpdate(req.body.id,req.body,{new:true})
}else{
//update artist model with image
const artist=await artistModel.findById(req.body.id)
//delete previous image present in bucket
await bucket.file(artist.filepath).delete()
//upload new one
const filename = `aura/artist_pic/${Date.now()}_${req.file.originalname}`;
const uuid=UUID()
const fileURL = await uploadFile(req.file.buffer, filename, req.file.mimetype,uuid);
//updating url and filepath
artist.picture=fileURL
artist.filepath=filename
//update artist model
const updateartist=await artistModel.findByIdAndUpdate(artist._id,artist,{new:true})
}
res.status(200).json({message:'Artist Updated Successfully'})
}catch(err){
console.log(err)
res.status(500).send('Internal Server Error')
}
})
//Delete Artist
router.delete('/delete/:id',async(req,res)=>{
try{
//find artist in database using id
const artist=await artistModel.findById(req.params.id)
//delete image object in firebase bucket
await bucket.file(artist.filepath).delete()
//delete artist model in database
const artistdelete=await artistModel.findByIdAndDelete(artist._id)
res.status(200).json('Artist deleted successfully')
}catch(err){
console.log(err)
res.status(500).json('Internal Server Error')
}
})
//Get Single Artist by Id
router.get('/single/:id',async(req,res)=>{
try{
const artist=await artistModel.findById(req.params.id)
res.status(200).json({data:artist,message:'success'})
}catch(err){
console.log(err)
res.status(500).json({data:err,message:'Internal Server Error'})
}
})
module.exports=router
- firebaseadmin.js: firebase bucket config file
require('dotenv').config()
var admin = require("firebase-admin");
//firebase Accountkey
var serviceAccount = require("./Firebaseservicekey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: process.env.BUCKET_URL,
});
const bucket = admin.storage().bucket();
module.exports=bucket
- upload.js: to handle file upload to firebase bucket using createWriteStream method and return a signed URL of the file(image) to store it in the database.
const bucket=require('../firebaseadmin')
const { promisify } = require('util');
const uploadFile = promisify((buffer, filename, mimetype,uuid, cb) => {
const fileUpload = bucket.file(filename);
const blobStream = fileUpload.createWriteStream({
metadata: {
contentType: mimetype,
metadata: {
firebaseStorageDownloadTokens: uuid
}
},
});
blobStream.on('error', (error) => {
console.error(error);
cb({ error: 'An error occurred while uploading the file' });
});
blobStream.on('finish', () => {
const fileURL = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/${encodeURIComponent(filename)}?alt=media&token=${uuid}`;
cb(null, fileURL);
});
blobStream.end(buffer);
});
module.exports=uploadFile
- .env: MongoDB connection URL and firebase bucket URL.
url=mongodb_database_url
//eg: mongodb://localhost:27017
BUCKET_URL=firebase_bucket_url
//eg: gs://firebase_project_name.appspot.com/
In Firebase Console ->Storage menu - copy the bucket URL and store it in the .env file
- package.json: npm package details and scripts to run the server.js file
{
"name": "project",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"firebase-admin": "^11.10.1",
"mongoose": "^7.4.3",
"multer": "^1.4.5-lts.1",
"uuid-v4": "^0.1.0"
}
}
- Run the node server
npm run dev
- The server starts running on port:4000
After successfully running our node server we can test the route
For this guide, we will use THUNDER CLIENT to send HTTP/ requests to our server.
Thunder Client can be downloaded from the VS. code extension tab.
Send HTTP Post request to create a new artist:
http://localhost:4000/artist
After a successful post request, you will get a response: Status:200 OK and a success message!
The cover image URL and file path are saved in the Artist model.
That's it!
I hope you find the information valuable.
For any questions, you can reach me on Linkedin
Feel free to checkout my Music streaming application AURA created using Nodejs, Vitejs, MongoDB and Firebase storage.
Subscribe to my newsletter
Read articles from Lunaram Suthar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by