<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Initial Apps]]></title><description><![CDATA[Incubating software for industry and more]]></description><link>https://www.initialapps.com/</link><image><url>https://www.initialapps.com/favicon.png</url><title>Initial Apps</title><link>https://www.initialapps.com/</link></image><generator>Ghost 5.2</generator><lastBuildDate>Thu, 16 Apr 2026 22:08:14 GMT</lastBuildDate><atom:link href="https://www.initialapps.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Adding App Authentication with PassportJs on a MEVN Stack]]></title><description><![CDATA[<p>This tutorial will teach you how to create signup and login functionality on an app built with Vue3, Express, Mongo, and Node. &#xA0;We&apos;ll start where our last tutorial left off: <a href="https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/">Connecting our ExpressJs/Vue3 App to a Mongoose Database</a>. &#xA0;Following along this example project will show</p>]]></description><link>https://www.initialapps.com/adding-app-authentication-with-passportjs-on-a-mevn-stack/</link><guid isPermaLink="false">64a1f0e8466557050e80de59</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sat, 08 Jul 2023 22:06:24 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1578088085518-738839b57548?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQyfHxrZXlzfGVufDB8fHx8MTY4ODg1MzkxNHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1578088085518-738839b57548?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQyfHxrZXlzfGVufDB8fHx8MTY4ODg1MzkxNHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Adding App Authentication with PassportJs on a MEVN Stack"><p>This tutorial will teach you how to create signup and login functionality on an app built with Vue3, Express, Mongo, and Node. &#xA0;We&apos;ll start where our last tutorial left off: <a href="https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/">Connecting our ExpressJs/Vue3 App to a Mongoose Database</a>. &#xA0;Following along this example project will show you how to get started with user logins.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git Repo Here:<a href="https://github.com/Initial-Apps/vue3-mevn-authentication"> https://github.com/Initial-Apps/vue3-mevn-authentication</a></div></div><h2 id="prerequisites">Prerequisites</h2><ul><li>This tutorial picks up from our last tutorial: <a href="https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/">Connecting our ExpressJs/Vue3 App to a Mongoose Database</a></li><li>Make sure to have <code>.env</code> variables setup with your MongoDB as this file cannot be in our git repo.</li><li>A basic understanding of <a href="https://www.initialapps.com/building-your-first-app-getting-started-with-git/">git</a>, and npm.</li></ul><h2 id="setup">Setup</h2><p>Let&apos;s start by cloning the project from our <a href="https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/">last tutorial</a>. &#xA0;</p><pre><code>git clone git@github.com:Initial-Apps/vue3-express-mongo.git passportJs-MEVN</code></pre><p>Open a new terminal window, move into the <code>server</code> folder and install our npm modules.</p><pre><code>cd passportJs-MEVN/server
npm install</code></pre><p>Make sure to also have your <code>.env</code> folder setup with your MongoDB key as we did in the <a href="https://www.initialapps.com/building-your-first-app-getting-started-with-git/">last tutorial</a>. &#xA0; </p><p>You can then start the server by running:</p><pre><code>node serverjs</code></pre><p>Next, let&apos;s also setup the client side. Open a new terminal window, move into the <code>client</code> folder and install our npm modules.</p><pre><code>cd client
npm install</code></pre><p>You can then start the client development server by:</p><pre><code>npm run dev</code></pre><h2 id="implementing-passportjs">Implementing PassportJs</h2><p>PassportJs is a popular npm module that runs on our server. &#xA0;This module provides functionality for signing up, as well as logging in and out on our server. &#xA0;Some notable functionalities include validating usernames and passwords, storing logged in user session data for access on our server.</p><p>We start by making a <code>passport</code> directory on our <code>server</code>. &#xA0;Open a new terminal window and make the new directory inside the <code>server</code> folder by running the following from the terminal or using your preferred code editor:</p><pre><code>cd server
mkdir passport</code></pre><p>Then we make a new file titled <code>init.js</code> and add the following:</p><pre><code>const login = require(&apos;./login&apos;);
const signup = require(&apos;./signup&apos;);
const User = require(&apos;../models/user&apos;);

module.exports = function(passport){
	passport.serializeUser(function(user, done) {
		done(null, { 
			_id: user._id,
		});
	});
	passport.deserializeUser(function(userSession, done) {	
		User.findById(userSession._id)
		.then((user)=&gt;{
			console.log(&apos;deserializingUser&apos;);
			done(null, user)
		})
		.catch((err)=&gt;{
			console.log(err);
			done(err)
		})
	});
	login(passport);
	signup(passport);
}</code></pre><p>In summarizing the above, the <code>serialize</code> function stores the user id into the session for our app to reference. &#xA0;The <code>deserialize</code> function can retrieve any user data we specify based on the stored user id. Together, these functions enable Passport.js to maintain user sessions and facilitate authentication throughout the application.</p><p>Then we make a new file titled <code>signup.js</code> and add the following:</p><pre><code>const LocalStrategy = require(&apos;../node_modules/passport-local&apos;).Strategy;
const mongoose = require(&apos;mongoose&apos;);
const User = require(&apos;../models/user&apos;);
const bCrypt = require(&apos;bcrypt-nodejs&apos;);

module.exports = function(passport){
	// LOCAL AUTHORIZATION STRATEGY
	passport.use(&apos;signup&apos;, new LocalStrategy({
		usernameField: &apos;email&apos;,
		passReqToCallback : true // allows us to pass back the entire request to the callback
	},
	function(req, username, password, done) {
		findOrCreateUser = function(){
			// find a user in Mongo with provided username
			User.findOne({ &apos;email&apos; :  username })
			.then((user)=&gt;{
				// If already exists
				if (user) {
					console.log(&apos;User already exists with email: &apos;+username);
					return done(null, false, &apos;That Email Already Exists&apos;);
				} else {
					// Hash the password
					const hash = createHash(password);
					// If there is no user with that email
					// create the user
					let newUser = new User({
						email: username,
						password: hash,
					});	
					// save the user
					newUser.save()
					.then((user)=&gt; {
						console.log(&apos;User Registration succesful&apos;);
						return done(null, newUser);						
					})
					.catch((err)=&gt;{
						if (err){
							console.log(&apos;Error in Saving user: &apos;+err);
							throw err;
						}
					});
				}
			})
			.catch((err)=&gt;{
				console.log(&apos;Error in SignUp: &apos;+err);
				return done(err);
			});
		};
		// Delay the execution of findOrCreateUser and execute the method
		// in the next tick of the event loop
		process.nextTick(findOrCreateUser);
	}));
	// Generates hash using bCrypt
	const createHash = function(password){
		return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
	}
}</code></pre><p>The above code first checks for any issues such as searching our database for any existing email address matches to ensure the user email is unique. &#xA0;It then encrypts the user password using <code>bcrypt</code>, &#xA0;stores it to our database on the save function, and returns our new user to our prior created <code>init.js</code> for use in our session. </p><p>Next we create a file called <code>login.js</code> and add the following:</p><pre><code>const LocalStrategy   = require(&apos;passport-local&apos;).Strategy;
const User = require(&apos;../models/user&apos;);
const bCrypt = require(&apos;bcrypt-nodejs&apos;);

module.exports = function(passport){
	passport.use(&apos;login&apos;, new LocalStrategy({
		usernameField: &apos;email&apos;,
		passReqToCallback : true
	},
	function(req, username, password, done) {
		username = username.toLowerCase(); // clear caps
		User.findOne({email: username})
		.catch((err) =&gt; {
			return done(err);
		})
		.then ((user) =&gt; {
			if(!user) {
				console.log(&apos;User Not Found with username &apos;+username);
				return done(null, false, &apos;Invalid Username&apos;);
			}
			// User exists but no password, log the error
			if (user &amp;&amp; !user.password){
				console.log(&apos;No Password&apos;);
				return done(null, false, &apos;No Password&apos;); // redirect back to login page
			}
			// User exists but wrong password, log the error
			if (!isValidPassword(user, password)){
				console.log(&apos;Invalid Password&apos;);
				return done(null, false, &apos;Invalid Password&apos;); // redirect back to login page
			}
			if(isValidPassword(user, password)){
                return done(null, user);
			}
		})
	}));
	const isValidPassword = function(user, password){
		return bCrypt.compareSync(password, user.password);
	}
}</code></pre><p>The above file handles our server logins. &#xA0;When a user logs in, this code validates the email and password by checking that it matches in our database. If all matches up with no errors, it will return our user for session use in our prior created <code>init.js</code> file.</p><h2 id="setting-up-our-database-user-model">Setting Up Our Database User Model</h2><p>Next we will setup our database model. Since we are creating an authentication app, we will need a place to store our user data, such as email addresses and passwords. That&apos;s where our MongoDB comes in. &#xA0;In order to do this, we&apos;ll need to create a data model, outlining what data we will store for each user.</p><p>First, we will create a <code>models</code> folder in our <code>server</code> directory, to hold the data models. &#xA0;If using the terminal, do this by running the following from within our <code>server</code> directory, or use your preferred code editor:</p><pre><code>mkdir models</code></pre><p>Create a new file called <code>user.js</code> and add the following:</p><pre><code>const mongoose = require(&apos;mongoose&apos;);

function toLower (v) {
	if(v){
		return v.toLowerCase();
	}
}

const Users = new mongoose.Schema({
	email: { type: String, set: toLower },
	password: String,
});

module.exports = mongoose.model(&apos;User&apos;, Users);</code></pre><p>A brief explanation of the above: &#xA0;First we require the mongoose module, which is a middleware that connects our app data with our database. &#xA0;The <code>toLower</code> function is used to rewrite all email addresses to lowercase. &#xA0;This is done as a best practice to ensure all email addresses are formatted the same and we don&apos;t accidentally add 2 of the same email addresses because one is capitalized and hence not recognized as different from it&apos;s lowercase counterpart. &#xA0;Finally, we define the data we are collecting in our <code>mongoose.Schema</code>. &#xA0; In our case, we are storing just the user <code>email</code> address, and <code>password</code>. &#xA0;</p><h2 id="building-our-server">Building Our Server</h2><p>Next we setup our server, which runs on Node and Express. &#xA0;Together, these code modules handle all actions performed by our user on the server side. &#xA0;These include actions such as listening for user signups, logins, or logouts. &#xA0;</p><p>Let&apos;s open the <code>server.js</code> file. &#xA0;I am modifying the file from our last tutorial as follows:</p><pre><code>const express = require(&apos;express&apos;)
const mongoose = require(&apos;mongoose&apos;);
const bodyParser = require(&apos;body-parser&apos;);
const session  = require(&apos;express-session&apos;); // Authentication is stored in sessions, so we&apos;ll use express-session
const MongoStore = require(&apos;connect-mongo&apos;);  // Used to connect our session data to our MongoDB
const passport = require(&apos;passport&apos;); // Passport is used for authentication

const app = express()
const port = 3000
require(&apos;dotenv&apos;).config(); 

// Connect to our MONGOOSE DB
const connectString = process.env.MONGODB;  
mongoose.connect(connectString, { useNewUrlParser: true }).then(
  () =&gt; { 
    console.log (&apos;Succeeded connected to database&apos;);
   },
  err =&gt; { 
    console.log (&apos;ERROR connecting to database: &apos; + err);
   }
);

// Initialize Passport
const initPassport = require(&apos;./passport/init&apos;);
initPassport(passport);
// Create the Session
app.use(session({
	secret: process.env.SESSION_SK,
	resave: true,
	saveUninitialized: false,
	cookie: {
    	httpOnly: false,
		sameSite: true,
		maxAge: 10* 4 * 60 * 60 * 1000,
	},
	store: MongoStore.create({
		mongoUrl: connectString,
		ttl: 10*4 * 60 * 60, // logout after 1hr
	})
}));
app.use(passport.initialize()); // initialize passport login sessions
app.use(passport.session()); // for persistent login sessions


app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.post(&apos;/api/login&apos;, (req, res, next) =&gt; {
	// general flow, 1) calls passport/login, 2) this function, 3) serializes in passport/init 4) deserializes in passport/init
	passport.authenticate(&apos;login&apos;, function(err, user, info) {
		if (err) {
			console.log(&apos;err&apos;);
			return res.sendStatus(400);
		}
		if (!user) {
			console.log(&apos;no user&apos;);
			return res.sendStatus(400);
		}		
		req.logIn(user, function(err) {
			if (err) { return next(err); } else {
				console.log(req.session)
				// return
				return res.sendStatus(200);					
			}
		});
	})(req, res, next);
});

/*** POST SIGNUP A USER ***/
app.post(&apos;/api/signup&apos;, (req, res, next) =&gt; {
	passport.authenticate(&apos;signup&apos;, registerUser)(req, res, next);
	function registerUser(err, user, info) {
		if (err) { console.log(err); return res.send(err) }
		if (!user) {
			return res.status(400).json(info);
		}
		if (user){
			req.logIn(user, function(err) {
				if (err) { return next(err); }
				// return
				return res.sendStatus(200);
			});
		}
	}
});

/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	res.clearCookie(&apos;connect.sid&apos;); 
	req.logout(function(err) {
		console.log(&apos;logged out&apos;);
		console.log(err)
		req.session.destroy(function (err) {
			res.send();
		});
	});
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`)
})</code></pre><p>Let&apos;s take a closer look at what was changed. &#xA0;We are requiring 3 new npm modules: <code>express-session</code>, <code>connect-mongo</code>, and <code>passport</code>. &#xA0;</p><pre><code>const session  = require(&apos;express-session&apos;); // Authentication is stored in sessions, so we&apos;ll use express-session
const MongoStore = require(&apos;connect-mongo&apos;);  // Used to connect our session data to our MongoDB
const passport = require(&apos;passport&apos;); // Passport is used for authentication</code></pre><p><code>express-session</code> and <code>connect-mongo</code> are both used to connect our login sessions to our Mongo database. These allow us to store a user login session on our database after the user has logged in. &#xA0;Our server script can then connect and check our database if a user is logged in. &#xA0;</p><p>We then initialize passport <code>initPassport</code> and start a <code>session</code>.</p><pre><code>// Initialize Passport
const initPassport = require(&apos;./passport/init&apos;);
initPassport(passport);
// Create the Session
app.use(session({
	secret: process.env.SESSION_SK,
	resave: true,
	saveUninitialized: false,
	cookie: {
 		httpOnly: false,
		sameSite: true,
		maxAge: 10 * 4 * 60 * 60 * 1000,
	},
	store: MongoStore.create({
		mongoUrl: connectString,
		ttl: 10 *4 * 60 * 60,
	})
}));
app.use(passport.initialize()); // initialize passport login sessions
app.use(passport.session()); // for persistent login sessions</code></pre><p>The above adds our passport <code>init.js</code>, <code>signup.js</code>, and <code>login.js</code> files for use. &#xA0;The session then creates a connection between our server and database to handle the login session. &#xA0;The session will be stored as a cookie on our server, and contain needed login information such as a unique identifier for the session, and when the session should expire for server and database ( <code>maxAge</code> &amp; <code>ttl</code>).</p><p>The last part we added are the API routes:</p><pre><code>app.post(&apos;/api/login&apos;, (req, res, next) =&gt; {
	passport.authenticate(&apos;login&apos;, function(err, user, info) {
		if (err) {
			console.log(&apos;err&apos;);
			return res.sendStatus(400);
		}
		if (!user) {
			console.log(&apos;no user&apos;);
			return res.sendStatus(400);
		}		
		req.logIn(user, function(err) {
			if (err) { return next(err); } else {
				console.log(req.session)
				// return
				return res.sendStatus(200);					
			}
		});
	})(req, res, next);
});

/*** POST SIGNUP A USER ***/
app.post(&apos;/api/signup&apos;, (req, res, next) =&gt; {
	passport.authenticate(&apos;signup&apos;, registerUser)(req, res, next);
	function registerUser(err, user, info) {
		if (err) { console.log(err); return res.send(err) }
		if (!user) {
			return res.status(400).json(info);
		}
		if (user){
			req.logIn(user, function(err) {
				if (err) { return next(err); }
				// return
				return res.sendStatus(200);
			});
		}
	}
});

/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	res.clearCookie(&apos;connect.sid&apos;); 
	req.logout(function(err) {
		console.log(&apos;logged out&apos;);
		console.log(err)
		req.session.destroy(function (err) {
			res.send();
		});
	});
});</code></pre><p>The above routes handle the client post actions when the user signs up, logs in, or logs out. &#xA0;They utilize the passport scripts we created and sends authentication data back to the client.</p><p>Since, we&apos;ve added new npm modules, we&apos;ll need to install them. &#xA0;From our terminal within the <code>server</code> directory, install them by running the following:</p><pre><code>npm install  express-session connect-mongo passport passport-local bcrypt-nodejs</code></pre><p>We also need to create a session key in our <code>.env</code> file. &#xA0;This can be any secret text that you like, and adds a security layer when sending data between our server and our mongo database. &#xA0;In the example below, I am adding this under our MONGODB key created in our <a href="https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/">last tutorial</a>.</p><pre><code>MONGODB=mongodb+srv://&lt;username&gt;:&lt;password&gt;@cluster0.ltkorue.mongodb.net/?retryWrites=true&amp;w=majority
SESSION_SK = mysecretkey</code></pre><h2 id="building-our-client">Building Our Client</h2><p>Now we build our front end framework using VueJs. The frontend framework defines everything on our user&apos;s browser, including how the app looks via HTML &amp; CSS. &#xA0;It also handles our client side actions, for example when the user clicks &quot;Login,&quot; the client side will post this action to our server.</p><p>Now, we&apos;ll be working from our <code>client</code> directory. &#xA0;First, we&apos;ll be using the <code>vue3-cookies</code> module to get our authentication cookie on the client. &#xA0;Let&apos;s install this now. &#xA0;From our <code>client</code> directory, run the following:</p><pre><code>npm install vue3-cookies --save</code></pre><p>Next, open the <code>App.vue</code> file and rewrite the entire file as follows:</p><pre><code>&lt;template&gt;
	&lt;div&gt;
		&lt;main class=&quot;main&quot;&gt;
			&lt;form v-if=&quot;!authenticated&quot;&gt;
				&lt;input
					type=&quot;text&quot;
					placeholder=&quot;Email Address&quot;
					v-model=&quot;email&quot;
				/&gt;
				&lt;input
					type=&quot;password&quot;
					placeholder=&quot;Password&quot;
					v-model=&quot;password&quot;
				/&gt;
				&lt;input type=&quot;submit&quot; value=&quot;Sign Up&quot; @click.prevent=&quot;onSignup&quot;/&gt;
				&lt;input type=&quot;submit&quot; value=&quot;Login&quot; @click.prevent=&quot;onLogin&quot; /&gt;
			&lt;/form&gt;
			&lt;form @submit.prevent=&quot;onLogout&quot; v-if=&quot;authenticated&quot;&gt;
				&lt;h1&gt;You&apos;re Logged In!&lt;/h1&gt;
				&lt;br&gt;
				&lt;input type=&quot;submit&quot; value=&quot;Logout&quot; /&gt;
			&lt;/form&gt;
		&lt;/main&gt;
	&lt;/div&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
	import { defineComponent, onMounted, ref } from &apos;vue&apos;
	import axios from &apos;axios&apos;
	import { useCookies } from &quot;vue3-cookies&quot;;

	axios.defaults.withCredentials = true
	export default defineComponent({
		setup() {
			const { cookies } = useCookies() as any
			return { cookies }
		},
		data() {
			return {
				email: &quot;&quot; as string,
				password: &quot;&quot; as string,
				result: &quot;&quot; as string,
				authenticated: false as boolean,
			}
		},
		computed: {
			checkCookie() {
				if (this.cookies.get(&apos;connect.sid&apos;)) {
					return true
				} else {
					return false
				}
			},
		},
		methods: {
			onSignup() {
				try {
				axios
				.post(&quot;/api/signup&quot;, { 
					email: this.email,
					password: this.password,
				})
				.then((response) =&gt; {
					this.authenticated = true
				}, (error) =&gt; {
					throw error || new Error(`Request failed`);
				})
				} catch(error:any) {
				}
			},
			onLogin() {
				try {
				axios
				.post(&quot;/api/login&quot;, { 
					email: this.email,
					password: this.password,
				})
				.then((response) =&gt; {
					this.authenticated = true
				}, (error) =&gt; {
					throw error || new Error(`Request failed`);
				})
				} catch(error:any) {
					// Consider implementing your own error handling logic here
					alert(error.message);
				}
			},
			onLogout() {
				try {
					axios
					.post(&quot;/api/logout&quot;)
					.then((response) =&gt; {
						this.authenticated = false
					}, (error) =&gt; {
						throw error || new Error(`Request failed`);
					})
				} catch(error:any) {
					// Consider implementing your own error handling logic here
					console.error(error);
					alert(error.message);
				}
			},
		},
		mounted(){
			this.authenticated = this.checkCookie
		}
	});
&lt;/script&gt;

&lt;style&gt;
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff2)
        format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff) format(&quot;woff&quot;);
    font-weight: normal;
    font-style: normal;
  }
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff2) format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff) format(&quot;woff&quot;);
    font-weight: bold;
    font-style: normal;
  }
  .main,
  .main input {
    font-size: 16px;
    line-height: 24px;
    color: #353740;
    font-family: &quot;ColfaxAI&quot;, Helvetica, sans-serif;
  }
  .main {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 60px;
  }
  .main .icon {
    width: 34px;
  }
  .main h3 {
    font-size: 32px;
    line-height: 40px;
    font-weight: bold;
    color: #202123;
    margin: 16px 0 40px;
  }
  .main form {
    display: flex;
    flex-direction: column;
    width: 320px;
  }
  .main input {
    padding: 12px 16px;
    border: 1px solid #10a37f;
    border-radius: 4px;
    margin-bottom: 24px;
    outline-color: #10a37f;
  }
  .main ::placeholder {
    color: #8e8ea0;
    opacity: 1;
  }
  .main input[type=&quot;submit&quot;] {
    padding: 12px 0;
    color: #fff;
    background-color: #10a37f;
    border: none;
    border-radius: 4px;
    text-align: center;
    cursor: pointer;
  }
  .main .result {
    font-weight: bold;
    margin-top: 40px;
  }
&lt;/style&gt;</code></pre><p>Let&apos;s explain the above. &#xA0;The first major change is our login form:</p><pre><code>&lt;form v-if=&quot;!authenticated&quot;&gt;
    &lt;input
        type=&quot;text&quot;
        placeholder=&quot;Email Address&quot;
        v-model=&quot;email&quot;
    /&gt;
    &lt;input
        type=&quot;password&quot;
        placeholder=&quot;Password&quot;
        v-model=&quot;password&quot;
    /&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Sign Up&quot; @click.prevent=&quot;onSignup&quot;/&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Login&quot; @click.prevent=&quot;onLogin&quot; /&gt;
&lt;/form&gt;
&lt;form @submit.prevent=&quot;onLogout&quot; v-if=&quot;authenticated&quot;&gt;
    &lt;h1&gt;You&apos;re Logged In!&lt;/h1&gt;
    &lt;br&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Logout&quot; /&gt;
&lt;/form&gt;</code></pre><p>We&apos;ve added an HTML form that takes both the email and password inputs. &#xA0;We&apos;ve also added a signup and login button. &#xA0;Below that is another form intended for logged in users, with only one button to log out. &#xA0;Both forms use the <code>v-if</code> functionality provided by VueJs to check against an <code>authenticated</code> variable. &#xA0;If the user is not authenticated, it shows the first login/signup form, and if the user is authenticated, it shows the second logout form. &#xA0; </p><h3 id="implementing-vue3-cookies">Implementing Vue3 Cookies</h3><p>The next major highlight is the use of <code>vue3-cookies</code>:</p><pre><code>import { useCookies } from &quot;vue3-cookies&quot;;

axios.defaults.withCredentials = true
export default defineComponent({
    setup() {
        const { cookies } = useCookies() as any
        return { cookies }
    },</code></pre><p>Per the <code>vue3-cookies</code> documentation, we are importing this module and making it available to our app during <code>setup()</code>. When a user logs in, our server sends a cookie to the client. It&apos;s important for our client to search for this cookie, especially on hard reloads, to see if we&apos;ve already logged in. &#xA0;Hence, later in this file we add a computed property to check for authentication by cookie:</p><pre><code>computed: {
    checkCookie() {
        if (this.cookies.get(&apos;connect.sid&apos;)) {
            return true
        } else {
            return false
        }
    },
},</code></pre><p>We utilize this function whenever our app mounts, which is added later in our script as follows:</p><pre><code>mounted(){
    this.authenticated = this.checkCookie
}</code></pre><p>The above check happens whenever the app mount, such as during a hard reload. Hard reload will clear most of our app data but does not clear cookies, hence we can check for authentication if our cookie is still present.</p><h3 id="adding-our-client-data">Adding Our Client Data</h3><p>Next, we define the variables that our client will use. &#xA0;In our case: <code>email</code>, <code>password</code>, and <code>authenticated</code>. &#xA0;We can see these variables sprinkled into our app&apos;s HTML forms that we wrote earlier. </p><pre><code>data() {
    return {
        email: &quot;&quot; as string,
        password: &quot;&quot; as string,
        authenticated: false as boolean,
    }
},</code></pre><h3 id="handling-user-actions">Handling User Actions</h3><p>Finally, we add the javascript functions on the client for handling user signups, logins, and logouts. &#xA0;You can also find these functions defined on our HTML form buttons that we wrote earlier. &#xA0;These functions post the necessary user data to our server routes.</p><pre><code>methods: {
    onSignup() {
        try {
        axios
        .post(&quot;/api/signup&quot;, { 
            email: this.email,
            password: this.password,
        })
        .then((response) =&gt; {
            this.authenticated = true
        }, (error) =&gt; {
            throw error || new Error(`Request failed`);
        })
        } catch(error:any) {
        }
    },
    onLogin() {
        try {
        axios
        .post(&quot;/api/login&quot;, { 
            email: this.email,
            password: this.password,
        })
        .then((response) =&gt; {
            this.authenticated = true
        }, (error) =&gt; {
            throw error || new Error(`Request failed`);
        })
        } catch(error:any) {
            // Consider implementing your own error handling logic here
            alert(error.message);
        }
    },
    onLogout() {
        try {
            axios
            .post(&quot;/api/logout&quot;)
            .then((response) =&gt; {
                this.authenticated = false
            }, (error) =&gt; {
                throw error || new Error(`Request failed`);
            })
        } catch(error:any) {
            // Consider implementing your own error handling logic here
            console.error(error);
            alert(error.message);
        }
    },
},</code></pre><h2 id="with-that-your-authentication-app-is-ready-to-go">With that, your authentication app is ready to go!</h2><p>Let&apos;s restart our client and server scripts that we ran earlier (press <code>ctrl c</code> to stop any existing scripts). &#xA0;</p><p>From the <code>client</code> directory, run:</p><pre><code>npm run dev</code></pre><p>From the <code>server</code> directory, run:</p><pre><code>node server.js</code></pre><p>And, we should then see our app in action:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.initialapps.com/content/images/2023/07/image-13.png" class="kg-image" alt="Adding App Authentication with PassportJs on a MEVN Stack" loading="lazy" width="580" height="460"><figcaption>When Signed Out</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.initialapps.com/content/images/2023/07/image-14.png" class="kg-image" alt="Adding App Authentication with PassportJs on a MEVN Stack" loading="lazy" width="536" height="292"><figcaption>When Logged In</figcaption></figure><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git Repo Here:<a href="https://github.com/Initial-Apps/vue3-mevn-authentication"> https://github.com/Initial-Apps/vue3-mevn-authentication</a></div></div>]]></content:encoded></item><item><title><![CDATA[How to Properly Logout using PassportJs, Express-Session, on a Single Page App]]></title><description><![CDATA[Proper logout using PassportJs & Express-Session for single page web application, clearing both the client cookie and database session.]]></description><link>https://www.initialapps.com/properly-logout-passportjs-express-session-for-single-page-app/</link><guid isPermaLink="false">64a79dbf466557050e80ded1</guid><category><![CDATA[Tutorials]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Fri, 07 Jul 2023 05:55:05 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1581553673739-c4906b5d0de8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHBhc3Nwb3J0fGVufDB8fHx8MTY4ODcwOTIwNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1581553673739-c4906b5d0de8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHBhc3Nwb3J0fGVufDB8fHx8MTY4ODcwOTIwNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to Properly Logout using PassportJs, Express-Session, on a Single Page App"><p>A short explanation for how to properly logout using <a href="https://www.passportjs.org/">PassportJs</a>, <a href="https://www.npmjs.com/package/express-session">Express-Session</a> for single page web application. &#xA0;This short tutorial will share how to fully logout server side and clear the client side cookie as well.</p><h2 id="the-short-answer">The short answer:</h2><pre><code>/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	res.clearCookie(&apos;connect.sid&apos;);  // clear the session cookie
	req.logout(function(err) {  // logout of passport
		req.session.destroy(function (err) { // destroy the session
			res.send(); // send to the client
		});
	});
});</code></pre><h2 id="a-thorough-explanation">A thorough explanation:</h2><p>While the documentation for PassportJs and Express Session are relatively clear for login in a user, I could not find great documentation for how these all worked together to logout a user. &#xA0;Hence, I&apos;m not sure if the above is the official and proper way to logout, and only arrived at this by much trial and error. &#xA0;My goal was to clear the session on both the server and client. &#xA0;So, I&apos;ll walk you through a short summary of my tests and findings! &#x1F913;</p><h3 id="logging-out-with-the-passportjs-logout-function">Logging out with the PassportJs logout function </h3><pre><code>app.post(&apos;/logout&apos;, function(req, res, next){
  req.logout(function(err) {
    if (err) { return next(err); }
    res.redirect(&apos;/&apos;);
  });
});</code></pre><p>PassportJs provides the example above, but this won&apos;t work for my single page web app, as I want to avoid using <code>res.redirect(&apos;/&apos;)</code>. So below is my first re-write:</p><pre><code>/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	req.logout(function(err) {
		console.log(err)
	});
});</code></pre><p>Using <code>req.logout</code> will logout the user and clear the cookie on our database, but it does not clear that pesky cookie on the client. &#xA0;You can find this cookie by opening google developer console in your browser and clicking the <code>applications</code> tab.</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-9.png" class="kg-image" alt="How to Properly Logout using PassportJs, Express-Session, on a Single Page App" loading="lazy" width="1100" height="375" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-9.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-9.png 1000w, https://www.initialapps.com/content/images/2023/07/image-9.png 1100w" sizes="(min-width: 720px) 720px"></figure><h2 id="adding-expressjs-clear-cookie-and-sending">Adding ExpressJs Clear Cookie and sending</h2><p>Next, I am adding <code>res.clearCookie()</code> provided by <a href="https://expressjs.com/en/api.html#res.clearCookie">ExpressJs</a>. &#xA0;I am also creating an empty send response to update our client (see code comments below):</p><pre><code>
/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	res.clearCookie(&apos;connect.sid&apos;);  // clear the cookie
	req.logout(function(err) {
		console.log(err)
		res.send(); // send to the client
	});
});</code></pre><p>Interestingly, this also clears the cookie on the database, but just keeps updating our client side cookie to something new?</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-11.png" class="kg-image" alt="How to Properly Logout using PassportJs, Express-Session, on a Single Page App" loading="lazy" width="1056" height="311" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-11.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-11.png 1000w, https://www.initialapps.com/content/images/2023/07/image-11.png 1056w" sizes="(min-width: 720px) 720px"></figure><h2 id="finally-lets-destroy-the-session-in-express-session">Finally, let&apos;s Destroy the Session in Express-Session</h2><p>Express-session has a function to <a href="https://github.com/expressjs/session#readme">destroy</a> our session. &#xA0;Let&apos;s then destroy our session prior to sending to the client, as shown in the commented code below.</p><pre><code>/*** LOGOUT A USER ***/
app.post(&apos;/api/logout&apos;, (req, res, next) =&gt; {
	res.clearCookie(&apos;connect.sid&apos;); 
	req.logout(function(err) {
		console.log(err)
		req.session.destroy(function (err) { // destroys the session
			res.send();
		});
	});
});</code></pre><p>After firing up our server again, we see that the above code clears the session in our database. &#xA0;It also clears our cookie client side (Hooray!).</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-12.png" class="kg-image" alt="How to Properly Logout using PassportJs, Express-Session, on a Single Page App" loading="lazy" width="1035" height="266" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-12.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-12.png 1000w, https://www.initialapps.com/content/images/2023/07/image-12.png 1035w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item><item><title><![CDATA[Connecting our ExpressJs/Vue3 App to a Mongoose Database]]></title><description><![CDATA[<p>A starter example tutorial for connecting ExpressJs to a Mongoose database hosted on MongoDB.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git Repo Here: https://github.com/Initial-Apps/vue3-express-mongo</div></div><h2 id="prerequisites">Prerequisites</h2><p>Here&apos;s what you will need in order to get started:</p><ul><li>Setup a <a href="https://www.mongodb.com/atlas/database">MongoDB</a> account to host your database.</li><li>We will continue on the simple</li></ul>]]></description><link>https://www.initialapps.com/connecting-our-expressjs-vue3-app-to-a-mongoose-database/</link><guid isPermaLink="false">64a1afa2466557050e80dd08</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sun, 02 Jul 2023 19:34:37 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1552071379-041b32707fed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fG1vbmdvb3NlfGVufDB8fHx8MTY4ODMyNjQ4Nnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1552071379-041b32707fed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fG1vbmdvb3NlfGVufDB8fHx8MTY4ODMyNjQ4Nnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database"><p>A starter example tutorial for connecting ExpressJs to a Mongoose database hosted on MongoDB.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git Repo Here: https://github.com/Initial-Apps/vue3-express-mongo</div></div><h2 id="prerequisites">Prerequisites</h2><p>Here&apos;s what you will need in order to get started:</p><ul><li>Setup a <a href="https://www.mongodb.com/atlas/database">MongoDB</a> account to host your database.</li><li>We will continue on the simple starter <a href="https://www.initialapps.com/express-vue3-vite/">Vue3/ExpressJS app</a> from our prior post.</li></ul><h2 id="getting-started">Getting Started</h2><p>First, let&apos;s clone our prior Vue3/ExpressJS app and rename it. &#xA0;Run the following:</p><pre><code>git clone git@github.com:Initial-Apps/vue3-express.git vue3-express-mongo</code></pre><p>Now, let&apos;s move into our app and install our modules on our <code>server</code>:</p><pre><code>cd vue3-express-mongo/server
npm install</code></pre><p>Next, let&apos;s start the server to make sure all is working. &#xA0;Run the following:</p><pre><code>node server.js</code></pre><h2 id="setting-up-mongodb">Setting up MongoDB</h2><p>Login to your MongoDB account: <a href="https://cloud.mongodb.com/">https://cloud.mongodb.com/</a></p><p>Select <code>New Project</code> and name your project. You can add members if you like. Then click <code>Create Project</code>.</p><p>Next, continue to <code>Build a Database</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-1.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="824" height="422" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-1.png 600w, https://www.initialapps.com/content/images/2023/07/image-1.png 824w" sizes="(min-width: 720px) 720px"></figure><p>Select whatever plan you like and click <code>create</code>.</p><p>Next, create a username and password for your database, and be sure to save this info.</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-2.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="1003" height="771" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-2.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-2.png 1000w, https://www.initialapps.com/content/images/2023/07/image-2.png 1003w" sizes="(min-width: 720px) 720px"></figure><p>Then, select connect from <code>My Local Environment</code> (see below):</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-3.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="1021" height="371" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-3.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-3.png 1000w, https://www.initialapps.com/content/images/2023/07/image-3.png 1021w" sizes="(min-width: 720px) 720px"></figure><p>Finally, select <code>Finish and Close</code>. Hooray, we&apos;ve setup our database! &#x1F913;</p><h2 id="connecting-expressjs-to-our-mongodb">Connecting ExpressJs to our MongoDB</h2><p>Now let&apos;s work on our server to connect ExpressJs to MongoDB. To do this, we&apos;ll work on our server middleware file <code>server/server.js</code>. Let&apos;s open this file and get to work. &#xA0;</p><p>I&apos;m adding the following code to <code>server.js</code> just after defining our imported modules. I&apos;ve added code comments as well.</p><pre><code>require(&apos;dotenv&apos;).config(); // needed to read our environment variables
const mongoose = require(&apos;mongoose&apos;);  // We need the mongoose module
const connectString = process.env.MONGODB;  // This holds our username and password, stored as a environment variable
mongoose.connect(connectString, { useNewUrlParser: true }).then(
  () =&gt; { 
    console.log (&apos;Succeeded connected to database&apos;);
   },
  err =&gt; { 
    console.log (&apos;ERROR connecting to database: &apos; + err);
   }
);</code></pre><p>Here&apos;s what the above code does:</p><ul><li>Adds the <code>dotenv</code> module so that we can store our MongoDB username and password as an environment variable. &#xA0;This is preferred over placing this sensitive information directly in your code.</li><li>Adds the <code>mongoose</code> module for connecting to our MongoDB database</li><li>The <code>connectString</code> variable will hold our login credentials for MongoDB. &#xA0;We&apos;ll set this up next.</li><li>Finally, we add the connect script to connect to our DB.</li></ul><p>Next, we have to setup our login and password for MongoDB as an environment variable. &#xA0;You do not want to ever share this file because it has sensitive login information. &#xA0;That said, I&apos;ve created a <code>example_env.txt</code> file in our <a href="https://github.com/Initial-Apps/vue3-express-mongo">github repo</a> to provide template. &#xA0;</p><p>We make sure our environment credentials are hidden by opening your <code>.gitignore</code> file, adding the following to the end of the file and save:</p><pre><code># env files
.env</code></pre><p>Next, we&apos;ll get our connect string from MongoDB. You can find your connect string format on your MongoDB dashboard. &#xA0;Click <code>connect</code> and then connect by Drivers.</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-4.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="1037" height="417" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-4.png 600w, https://www.initialapps.com/content/images/size/w1000/2023/07/image-4.png 1000w, https://www.initialapps.com/content/images/2023/07/image-4.png 1037w" sizes="(min-width: 720px) 720px"></figure><p>We are going to add our key using <code>mongoose</code> so we only need to pay attention to the <strong>Driver</strong> and the <strong>Connection String</strong>.</p><p>Select <code>Node</code> for the Driver like so:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-5.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="639" height="195" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-5.png 600w, https://www.initialapps.com/content/images/2023/07/image-5.png 639w"></figure><p>Then copy and paste your Connection String:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-6.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="959" height="201" srcset="https://www.initialapps.com/content/images/size/w600/2023/07/image-6.png 600w, https://www.initialapps.com/content/images/2023/07/image-6.png 959w" sizes="(min-width: 720px) 720px"></figure><p>Now create a file named <code>.env</code> and add your connect URL. The contents of the file should look something like this (replacing &lt;username&gt; and &lt;password&gt; with the MongoDB username and password we created in the last section). Save the file once this is added.</p><pre><code>MONGODB=mongodb+srv://&lt;username&gt;:&lt;password&gt;@cluster0.ltkorue.mongodb.net/?retryWrites=true&amp;w=majority</code></pre><p>Then we install the modules we need for the connection. &#xA0;From the terminal window that we used to run our node server (the one that we ran <code>node server.js</code>). &#xA0;First stop the server (hit <code>ctrl+c</code>). &#xA0;Now install the modules:</p><pre><code>npm install mongoose dotenv</code></pre><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text"><strong>Avoiding pitfalls:</strong> Be sure you are using the latest node vs needed for mongoose. I&apos;m using Node v18.12.1 and Mongooose v^7.3.1.</div></div><p></p><p>Now we can restart our server by running:</p><pre><code>node server.js</code></pre><p>If all runs properly, you should see the following on your console:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/07/image-8.png" class="kg-image" alt="Connecting our ExpressJs/Vue3 App to a Mongoose Database" loading="lazy" width="340" height="45"></figure><p>Congrats as you are now connected to your database!</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git Repo Here: https://github.com/Initial-Apps/vue3-express-mongo</div></div><h2 id="whats-next">What&apos;s Next</h2><p>Next, we need to start using your new database for various data! &#xA0;Want to see how we do that? &#xA0;Stay tuned or subscribe as we continue this app.</p>]]></content:encoded></item><item><title><![CDATA[ChatGPT OpenAI Example Starter Project using Vue3 and Express]]></title><description><![CDATA[A tutorial for getting started with OpenAI's API, based on their quickstart guide, but coded in Vue3 and ExpressJs.]]></description><link>https://www.initialapps.com/chatgpt-openai-example-starter-project-using-vue3-and-express/</link><guid isPermaLink="false">649a5d6b466557050e80da21</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Fri, 30 Jun 2023 05:28:18 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1679083216051-aa510a1a2c0e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGNoYXRncHR8ZW58MHx8fHwxNjg4MDA4NjIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1679083216051-aa510a1a2c0e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGNoYXRncHR8ZW58MHx8fHwxNjg4MDA4NjIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="ChatGPT OpenAI Example Starter Project using Vue3 and Express"><p>AI is hot these days, and OpenAI has an excellent and powerful API for all to use! They have a nice tutorial for getting started, with a source repo built in ReactJs. I wanted to do this with Vue3 and share with the world. So without further ado, here&apos;s how to setup a starter app with OpenAI using Vue3 and Expressjs.</p><p>You can find more about <a href="https://github.com/openai/openai-quickstart-node">OpenAI&apos;s quickstart guide example here</a>. &#xA0;This example app takes in an animal and spits out 3 suggestions for a pet name. &#xA0;The quickstart app is built on ReactJs and Nextjs. This guide will rewrite the quickstart guide using Vue3 and ExpressJs. &#xA0;Why, because I really like Vue3! &#x1F913;</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git repo here: https://github.com/Initial-Apps/openai-quickstart-vue3</div></div><h2 id="prerequisite-setting-up-an-app-with-vue3-and-expressjs">Prerequisite: Setting up an app with Vue3 and ExpressJs</h2><p>This tutorial picks up from where our last tutorial left off. &#xA0;If you want to setup your app from scratch, please see the below link:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.initialapps.com/express-vue3-vite/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Setting up Vue3, Vite, and Express</div><div class="kg-bookmark-description">A simple example setup of Vue3 using Vite, with ExpressJS and NodeJS on the backend.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://s3-us-west-1.amazonaws.com/initialapps/2018/06/favicon-256-1.png" alt="ChatGPT OpenAI Example Starter Project using Vue3 and Express"><span class="kg-bookmark-author">Initial Apps</span><span class="kg-bookmark-publisher">Andrew Akagawa</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1561883088-039e53143d73?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHZ1ZXxlbnwwfHx8fDE2ODc5MzI5MTF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="ChatGPT OpenAI Example Starter Project using Vue3 and Express"></div></a></figure><p>Otherwise, you can skip ahead by cloning the repo from the link below:</p><pre><code>git clone https://github.com/Initial-Apps/vue3-express.git</code></pre><h2 id="setting-up-the-client-side">Setting up the client side</h2><p>Let&apos;s first start with some housekeeping. We&apos;ll give our app a new directory name and then move into our new repo:</p><pre><code>mv vue3-express openai-quickstart-vue3
cd openai-quickstart-vue3</code></pre><p>Now let&apos;s start the client side. &#xA0;Move into your new apps client folder, install our modules, and run the dev script:</p><pre><code>cd client
npm install
npm run dev</code></pre><p>Hooray, we should now have a Vue starter app running! Navigate to <a href="http://localhost:5173/">http://localhost:5173/</a> to see it.</p><h2 id="setting-up-our-openai-credentials">Setting up our OpenAI credentials</h2><p>Now lets start to work on on server. &#xA0;Open a new terminal, move in the server folder, and install our npm modules.</p><pre><code>cd server
npm install</code></pre><p>Next, let&apos;s make sure our environment credentials are hidden. &#xA0;In the <code>server</code> directory, open your <code>.gitignore</code> file, add the following to the end of the file and save:</p><pre><code># env files
.env</code></pre><p>This will keep your API credentials hidden to your git repo.</p><p>Next, create a new file and name it <code>.env</code>. &#xA0;Copy and paste the following into your <code>.env</code> file. &#xA0;Add your <a href="https://platform.openai.com/account/api-keys">OpenAPI key</a>. &#xA0;</p><pre><code># Do not share your OpenAI API key with anyone! It should remain a secret.
OPENAI_API_KEY=</code></pre><h2 id="setting-up-expressjs">Setting up ExpressJs</h2><p>Now, let&apos;s setup our server script. &#xA0;Open the <code>server.js</code> file and rewrite as follows:</p><pre><code>const express = require(&apos;express&apos;)
const app = express()
const port = 3000
const bodyParser = require(&apos;body-parser&apos;);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

require(&apos;dotenv&apos;).config(); // needed to read our environment variables

// the next 3 const integrate the openai API
const {Configuration, OpenAIApi} =  require(&quot;openai&quot;);
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

// this post function is a taken from openai quickstart example, but modified to work with expressjs
app.post(&apos;/api/generator&apos;, async (req, res) =&gt; {
  if (!configuration.apiKey) {
      res.status(500).json({
          error: {
              message: &quot;OpenAI API key not configured, please follow instructions in README.md&quot;,
          }
      });
      return;
  }

  const animal = req.body.animal || &apos;&apos;;
  if (animal.trim().length === 0) {
      res.status(400).json({
          error: {
              message: &quot;Please enter a valid animal&quot;,
          }
      });
      return;
  }

  try {
      const completion = await openai.createCompletion({
          model: &quot;text-davinci-003&quot;,
          prompt: generatePrompt(animal),
          temperature: 0.6,
      });
      res.status(200).json({ result: completion.data.choices[0].text });
  } catch(error) {
      // Consider adjusting the error handling logic for your use case
      if (error.response) {
          console.error(error.response.status, error.response.data);
          res.status(error.response.status).json(error.response.data);
      } else {
          console.error(`Error with OpenAI API request: ${error.message}`);
          res.status(500).json({
              error: {
              message: &apos;An error occurred during your request.&apos;,
              }
          });
      }
  }
  function generatePrompt(animal) {
  const capitalizedAnimal =
      animal[0].toUpperCase() + animal.slice(1).toLowerCase();
      return `Suggest three names for an animal that is a superhero.

      Animal: Cat
      Names: Captain Sharpclaw, Agent Fluffball, The Incredible Feline
      Animal: Dog
      Names: Ruff the Protector, Wonder Canine, Sir Barks-a-Lot
      Animal: ${capitalizedAnimal}
      Names:`;
  }    
})

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`)
})</code></pre><p>This updated code adds the openai generator route to our server script. The <code>generator</code> route is basically a rewrite of the the <a href="https://github.com/openai/openai-node">quickstart example</a> provided by OpenAI, so that it works with ExpressJs. The server script makes all the API calls to OpenAI, so feel free to come back to this later and play around with it for fun!</p><p>We also must install the OpenAI API, and <code>dotenv</code> to read the access keys in our <code>.env</code> folder. &#xA0;We do this by running the following install scripts:</p><pre><code>npm install openai
npm install dotenv --save</code></pre><p>Now we can run our server with the foll0wing command from the server directory to start our sever:</p><pre><code>node server.js</code></pre><h2 id="finally-lets-rewrite-the-vuejs-frontend">Finally, let&apos;s rewrite the VueJs frontend</h2><p>First, let&apos;s rewrite our <code>App.vue</code> file. &#xA0;You can find this in the <code>client\src\</code> directory. Basically, we are rewriting the OpenAI <a href="https://github.com/openai/openai-node">quickstart example</a> from React to Vue as follows:</p><pre><code>&lt;template&gt;
  &lt;div&gt;
    &lt;Head&gt;
      &lt;title&gt;OpenAI Quickstart&lt;/title&gt;
      &lt;link rel=&quot;icon&quot; href=&quot;/dog.png&quot; /&gt;
    &lt;/Head&gt;

    &lt;main class=&quot;main&quot;&gt;
      &lt;h3&gt;Name my pet&lt;/h3&gt;
      &lt;form @submit.prevent=&quot;onSubmit&quot;&gt;
        &lt;input
          type=&quot;text&quot;
          name=&quot;animal&quot;
          placeholder=&quot;Enter an animal&quot;
          v-model=&quot;animalInput&quot;
        /&gt;
        &lt;input type=&quot;submit&quot; value=&quot;Generate names&quot; /&gt;
      &lt;/form&gt;
      &lt;div class=&quot;result&quot;&gt;{{result}}&lt;/div&gt;
    &lt;/main&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
	import { defineComponent, onMounted, ref } from &apos;vue&apos;
  import axios from &apos;axios&apos;

	export default defineComponent({
		data() {
			return {
				animalInput: &quot;&quot; as string,
        result: &quot;&quot; as string,
			}
		},
		methods: {
			onSubmit() {
        try {
          axios
          .post(&quot;/api/generator&quot;, { animal: this.animalInput })
          .then((response) =&gt; {
            this.result = response.data.result;
          }, (error) =&gt; {
            throw error || new Error(`Request failed`);
          })
        } catch(error:any) {
          // Consider implementing your own error handling logic here
          console.error(error);
          alert(error.message);
        }
      }
		}
	});
&lt;/script&gt;

&lt;style&gt;
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff2)
        format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff) format(&quot;woff&quot;);
    font-weight: normal;
    font-style: normal;
  }
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff2) format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff) format(&quot;woff&quot;);
    font-weight: bold;
    font-style: normal;
  }
  .main,
  .main input {
    font-size: 16px;
    line-height: 24px;
    color: #353740;
    font-family: &quot;ColfaxAI&quot;, Helvetica, sans-serif;
  }
  .main {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 60px;
  }
  .main .icon {
    width: 34px;
  }
  .main h3 {
    font-size: 32px;
    line-height: 40px;
    font-weight: bold;
    color: #202123;
    margin: 16px 0 40px;
  }
  .main form {
    display: flex;
    flex-direction: column;
    width: 320px;
  }
  .main input[type=&quot;text&quot;] {
    padding: 12px 16px;
    border: 1px solid #10a37f;
    border-radius: 4px;
    margin-bottom: 24px;
    outline-color: #10a37f;
  }
  .main ::placeholder {
    color: #8e8ea0;
    opacity: 1;
  }
  .main input[type=&quot;submit&quot;] {
    padding: 12px 0;
    color: #fff;
    background-color: #10a37f;
    border: none;
    border-radius: 4px;
    text-align: center;
    cursor: pointer;
  }
  .main .result {
    font-weight: bold;
    margin-top: 40px;
  }
&lt;/style&gt;</code></pre><h2 id="congrats-for-setting-up-your-vue3-app-with-openai">Congrats for setting up your Vue3 app with OpenAI!</h2><p>You should now have a working app that harnesses the power of AI and ChatGPT (or text-davinci). &#xA0;Refresh your <a href="http://localhost:5173/">http://localhost:5173/</a> to see it. &#xA0;When you enter an animal in the text box, the AI API will give you suggestions for 3 names for this animal. &#xA0;Now that you have the initial setup, feel free play around with all the possibilities that this AI technology brings!</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2023/06/pet.png" class="kg-image" alt="ChatGPT OpenAI Example Starter Project using Vue3 and Express" loading="lazy" width="970" height="570" srcset="https://www.initialapps.com/content/images/size/w600/2023/06/pet.png 600w, https://www.initialapps.com/content/images/2023/06/pet.png 970w" sizes="(min-width: 720px) 720px"></figure><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git repo here: https://github.com/Initial-Apps/openai-quickstart-vue3</div></div>]]></content:encoded></item><item><title><![CDATA[Setting up Vue3, Vite, and Express]]></title><description><![CDATA[A simple example setup of Vue3 using Vite, with ExpressJS and NodeJS on the backend.]]></description><link>https://www.initialapps.com/express-vue3-vite/</link><guid isPermaLink="false">649bb627466557050e80db13</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Wed, 28 Jun 2023 06:16:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1561883088-039e53143d73?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHZ1ZXxlbnwwfHx8fDE2ODc5MzI5MTF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1561883088-039e53143d73?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHZ1ZXxlbnwwfHx8fDE2ODc5MzI5MTF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Setting up Vue3, Vite, and Express"><p>A quick guide to getting started with Vue3, Vite, and ExpressJS &amp; NodeJS for our backend. &#xA0;Let&apos;s jump right in!</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git repo here: https://github.com/Initial-Apps/vue3-express</div></div><h2 id="first-lets-setup-vue3">First, Let&apos;s Setup Vue3</h2><p>First let&apos;s make a directory to house our app and open that directory.</p><pre><code>mkdir vue3-express
cd vue3-express</code></pre><p>Next, make sure you&apos;re up to date with node, npm, etc. and run the following.</p><!--kg-card-begin: markdown--><pre><code>npm init vue@latest
</code></pre>
<!--kg-card-end: markdown--><p>This will walk through the official Vue3 install script. &#xA0;It will ask you to name your app. I named mine <code>client</code>.</p><p>For the rest of the questions, if you don&apos;t know the answer I recommend you answer no. When done with these questions, you will have a starter Vue3 app! </p><p>Finish the install by changing into the directory and installing the npm packages (use the following code):</p><pre><code>  cd client
  npm install
  npm run dev</code></pre><p>Hooray, we should now have a Vue starter app running! Navigate to <a href="http://localhost:3000/" rel="nofollow">http://localhost:5173/</a> to see it. Mine looks like this:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-12.42.08-PM.png" class="kg-image" alt="Setting up Vue3, Vite, and Express" loading="lazy"></figure><h2></h2><h2 id="setting-up-expressjs">Setting up ExpressJs</h2><p>Now we install ExpressJs. &#xA0;First, we should start a new terminal in our parent directory named <code>vue3-express</code>. &#xA0;Then create a new directory for our server, which I will name this directory <code>server</code>. &#xA0;We then move into our <code>server</code> directory and initialize it in npm.</p><pre><code>mkdir server
cd server
npm init</code></pre><p>Next, we &#xA0;install our needed modules: ExpressJs for our API posts, and Body-Parser to get the posted data. &#xA0;We install these by running the following.</p><pre><code>npm install express
npm install body-parser
</code></pre><p>Next, we setup our server script. &#xA0;Create a new file titled <code>server.js</code> (or whatever you want to name your server file) and add the following:</p><pre><code>const express = require(&apos;express&apos;)
const app = express()
const port = 3000
const bodyParser = require(&apos;body-parser&apos;);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));


app.post(&apos;/api&apos;, (req, res) =&gt; {
  console.log(req.body)
  res.status(200).json({ result: req.body.text });
})

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`)
})</code></pre><p>The above is a very simple node server using express as a middleware to capture post data from our client.</p><p>Start the server by running:</p><pre><code>node server.js</code></pre><h2 id="finally-lets-configure-vite-and-create-a-vue3-test-form">Finally, Let&apos;s Configure Vite and Create a Vue3 Test Form</h2><p>Open a new terminal window and move back into our client directory and install Axios for post requests. Run the following:</p><pre><code>cd client
npm install axios
</code></pre><p>First, let&apos;s configure Vite to proxy our server so the client and server ports can talk to each other. &#xA0;Open <code>vite.config.ts</code> and add the the proxy info such that the entire file will now look like this:</p><pre><code>import { fileURLToPath, URL } from &apos;node:url&apos;

import { defineConfig } from &apos;vite&apos;
import vue from &apos;@vitejs/plugin-vue&apos;

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      &apos;@&apos;: fileURLToPath(new URL(&apos;./src&apos;, import.meta.url))
    }
  },
  server: {
		proxy: {
			&apos;/api&apos;: {
				target: &apos;http://localhost:3000&apos;,
				changeOrigin: true,
				secure: false,
				ws: true,
			},
		},
	} 
})
</code></pre><p>Next, lets completely rewrite our <code>App.vue</code> file (inside the <code>src</code> directory) so that it is a test form, to try out our post:</p><pre><code>&lt;template&gt;
  &lt;div&gt;
    &lt;main class=&quot;main&quot;&gt;
      &lt;form @submit.prevent=&quot;onSubmit&quot;&gt;
        &lt;input
          type=&quot;text&quot;
          placeholder=&quot;Enter Anything&quot;
          v-model=&quot;testInput&quot;
        /&gt;
        &lt;input type=&quot;submit&quot; value=&quot;Test Server&quot; /&gt;
      &lt;/form&gt;
      &lt;div class=&quot;result&quot;&gt;{{result}}&lt;/div&gt;
    &lt;/main&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
	import { defineComponent, onMounted, ref } from &apos;vue&apos;
  import axios from &apos;axios&apos;

	export default defineComponent({
		data() {
			return {
				testInput: &quot;&quot; as string,
        result: &quot;&quot; as string,
			}
		},
		methods: {
			onSubmit() {
        try {
          axios
          .post(&quot;/api&quot;, { text: this.testInput })
          .then((response) =&gt; {
            this.result = response.data.result;
          }, (error) =&gt; {
            throw error || new Error(`Request failed`);
          })
        } catch(error:any) {
          // Consider implementing your own error handling logic here
          console.error(error);
          alert(error.message);
        }
      }
		}
	});
&lt;/script&gt;

&lt;style&gt;
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff2)
        format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIRegular.woff) format(&quot;woff&quot;);
    font-weight: normal;
    font-style: normal;
  }
  @font-face {
    font-family: &quot;ColfaxAI&quot;;
    src: url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff2) format(&quot;woff2&quot;),
      url(https://cdn.openai.com/API/fonts/ColfaxAIBold.woff) format(&quot;woff&quot;);
    font-weight: bold;
    font-style: normal;
  }
  .main,
  .main input {
    font-size: 16px;
    line-height: 24px;
    color: #353740;
    font-family: &quot;ColfaxAI&quot;, Helvetica, sans-serif;
  }
  .main {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 60px;
  }
  .main .icon {
    width: 34px;
  }
  .main h3 {
    font-size: 32px;
    line-height: 40px;
    font-weight: bold;
    color: #202123;
    margin: 16px 0 40px;
  }
  .main form {
    display: flex;
    flex-direction: column;
    width: 320px;
  }
  .main input[type=&quot;text&quot;] {
    padding: 12px 16px;
    border: 1px solid #10a37f;
    border-radius: 4px;
    margin-bottom: 24px;
    outline-color: #10a37f;
  }
  .main ::placeholder {
    color: #8e8ea0;
    opacity: 1;
  }
  .main input[type=&quot;submit&quot;] {
    padding: 12px 0;
    color: #fff;
    background-color: #10a37f;
    border: none;
    border-radius: 4px;
    text-align: center;
    cursor: pointer;
  }
  .main .result {
    font-weight: bold;
    margin-top: 40px;
  }
&lt;/style&gt;</code></pre><p>Now, you should be able to see the new form at <a href="http://localhost:5173/">http://localhost:5173/</a>. &#xA0;When you enter information and submit the form, you should see this info log on the server and then see it sent back to the client.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.initialapps.com/content/images/2023/06/log.png" class="kg-image" alt="Setting up Vue3, Vite, and Express" loading="lazy" width="345" height="52"><figcaption>Server Log</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.initialapps.com/content/images/2023/06/test-1.png" class="kg-image" alt="Setting up Vue3, Vite, and Express" loading="lazy" width="571" height="343"><figcaption>Client Result</figcaption></figure><p>Congrats, you&apos;ve setup a new node app using Vue3, Vite, and ExpressJS! &#xA0;Thanks for reading and hope this helps. </p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Git repo here: https://github.com/Initial-Apps/vue3-express</div></div>]]></content:encoded></item><item><title><![CDATA[Customizing bootstrap 5 with Sass! Example provided.]]></title><description><![CDATA[An example for how to create custom theme color pallets in Bootstrap 5 using Sass. This post focuses on Bootstrap's color mapping functionality.]]></description><link>https://www.initialapps.com/customizing-bootstrap-5-with-sass/</link><guid isPermaLink="false">62b0c4c2902d3246ef85e92a</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Tue, 21 Jun 2022 13:37:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1464820453369-31d2c0b651af?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvbG9yZnVsfGVufDB8fHx8MTY1NTgxODM1Mw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1464820453369-31d2c0b651af?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvbG9yZnVsfGVufDB8fHx8MTY1NTgxODM1Mw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Customizing bootstrap 5 with Sass! Example provided."><p>An example for how to create custom theme color pallets in Bootstrap 5 using Sass. This post focuses on Bootstrap&apos;s color mapping functionality.</p><p>I was super happy to learn Bootstrap 5 is jQuery-less. Don&apos;t get me wrong, jQuery deserves it&apos;s place in legendary JS history, but doesn&apos;t play well with a lot of the latest frameworks today. </p><p>That said, it took me awhile to figure out how the Sass color mapping worked, so I shall share my experimentation in this post, using a new Vue app as an example! </p><p><a href="https://github.com/initialapps/bootstrap-5-sass-example">Github repo here.</a></p><h2 id="the-setup">The Setup</h2><p>First off, I&apos;m creating a new Vue 3 app with Vite to compile our Sass. Feel free to skip this section if you&apos;re not interested in setting things up and just want to see the Sass customization.</p><p>Make sure you&apos;re up to date with node, npm, etc. and run the following. </p><pre><code>npm init vue@latest</code></pre><p>If you run into any issues, you can find the official instructions for starting a Vue 3 app <a href="https://vuejs.org/guide/quick-start.html">provided here</a>. </p><p>The installer asks a series of questions, that you can answer mostly no too as we&apos;re just using this as an easy setup to compile the sass. </p><p>Name the project whatever you like (mine is bootstrap-5-sass-example). Next, move into it when created and install.</p><pre><code>cd bootstrap-5-sass-example
npm install
npm run dev</code></pre><p>Hooray, we should now have a Vue starter app running! Navigate to http://localhost:3000/ to see it. Mine looks like this:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-12.42.08-PM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="2000" height="1207" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-12.42.08-PM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-12.42.08-PM.png 1000w, https://www.initialapps.com/content/images/size/w1600/2022/06/Screenshot-2022-06-20-12.42.08-PM.png 1600w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-12.42.08-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now let&apos;s install bootstrap! Exit the running terminal <code>ctrl+c</code> and let&apos;s keep going:</p><pre><code>npm install --save bootstrap
npm install --save @popperjs/core</code></pre><p>Next, let&apos;s setup our Vite to compile Sass! </p><p>We start by making a scss file in our source directory. I&apos;m making the following new file &amp; path: <code>src&gt;assets&gt;sass&gt;main.scss</code>, and leaving the <code>main.scss</code> file blank for now. </p><p>Vue is currently using Vite to compile everything, so we can reference the path to our sass file in the Vite config file (<code>vite.config.ts</code>) by adding the following in the <code>export default defineConfig</code> block:</p><pre><code>  css: {
    preprocessorOptions: {
      sass: {
        additionalData: `
          @import &quot;@/assets/sass/main.scss&quot;;
        `
      }
    }
  }</code></pre><p>Also, per the Vite docs, we need to install the Sass pre-processor by running the following (<a href="https://vitejs.dev/guide/features.html#css-modules">more info provided here</a>):</p><pre><code>npm add -D sass</code></pre><p>Now we&apos;re almost Bootstrap ready! &#xA0;</p><p>Let&apos;s test this out by adding some nice buttons. I&apos;m going to grab some code from the Bootstrap website. We can add it into the main <code>App.vue</code> file in the source code (<code>src&gt;App.vue</code>). I&apos;m basically going to re-write the html <code>template</code> block inside this file with the following Bootstrap&apos;d out code:</p><pre><code>&lt;template&gt;
  &lt;div class=&quot;container mt-5&quot;&gt;
    &lt;h1&gt;Bootstrap Buttons!&lt;/h1&gt;
    &lt;div class=&quot;row&quot;&gt;
      &lt;div class=&quot;col&quot;&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-primary&quot;&gt;Primary&lt;/button&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-secondary&quot;&gt;Secondary&lt;/button&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-success&quot;&gt;Success&lt;/button&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-danger&quot;&gt;Danger&lt;/button&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-warning&quot;&gt;Warning&lt;/button&gt;
        &lt;button type=&quot;button&quot; class=&quot;btn btn-info&quot;&gt;Info&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre><p>We also need to import our sass file. Again, in the <code>App.vue</code> file we replace the <code>style</code> block to import our sass:</p><pre><code>&lt;style lang=&quot;scss&quot;&gt;
  @import &quot;@/assets/sass/main.scss&quot;;
&lt;/style&gt;</code></pre><h2 id="creating-the-custom-sass-file">Creating the custom Sass file</h2><p>Note, this is where things first got confusing for me. Bootstrap gives you either an option to load all of bootstrap (with no access to customization functions), or load parts of bootstrap (<a href="https://getbootstrap.com/docs/5.0/customize/sass/">see here</a>). For the life of me, I could not get Option B (including only parts of Bootstrap) to work. Since, I wanted all of bootstrap, as well as access to functions I decided to do both! e.g. I wanted to have my cake and eat it too! &#x1F382;</p><p>So here&apos;s my <code>main.scss</code> file (hacked together with both options):</p><pre><code>// Custom.scss

// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import &quot;../node_modules/bootstrap/scss/functions&quot;;

// 2. Include any default variable overrides here


// 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets)
@import &quot;../node_modules/bootstrap/scss/variables&quot;;
@import &quot;../node_modules/bootstrap/scss/variables-dark&quot;;

// 4. Include any default map overrides here


// 5. Import all the rest of Bootstrap 
@import &quot;../node_modules/bootstrap/scss/bootstrap&quot;;


// Then add additional custom code here</code></pre><p>Now lets run run the developer script in the terminal, to see things up and running:</p><pre><code>npm run dev</code></pre><p>It should look like this (with all the nice bootstrap colors)!</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.38.44-PM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="1160" height="497" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-10.38.44-PM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-10.38.44-PM.png 1000w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.38.44-PM.png 1160w" sizes="(min-width: 720px) 720px"></figure><h2 id="now-lets-customize-the-sass">Now lets customize the Sass!</h2><p>Bootstrap gives us some nice functions to do this. It&apos;s important to note where things are placed in the sass file for it to work properly, so pay attention to the comments in the <code>main.cscc</code> file. &#xA0;</p><p>I&apos;m going to head over to <a href="https://coolors.co/">coolors</a> to make a random new theme color palette. Here it is!</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.42.45-PM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="2000" height="333" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-10.42.45-PM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-10.42.45-PM.png 1000w, https://www.initialapps.com/content/images/size/w1600/2022/06/Screenshot-2022-06-20-10.42.45-PM.png 1600w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.42.45-PM.png 2393w" sizes="(min-width: 720px) 720px"></figure><p>Now let&apos;s re-write bootstrap&apos;s usual color variables (primary, secondary, etc) in the <code>main.scss</code> file, just under the 2. comment:</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Be sure to add under the #2. comment as order of code matters!</div></div><pre><code>// 2. Include any default variable overrides here
$primary: #D4CBE5;
$secondary:#CFC7D2;
$success: #BEA8AA;
$warning: #9E9885;
$danger: #7C7F65;</code></pre><p>Notice these re-writes are added before loading Bootstrap&apos;s variables in the 3. comments. That&apos;s because Boostrap uses the <code>!default</code> tag on all of it&apos;s variables. That meas the theme will take Bootstrap&apos;s defined value by default, if no other value has been assigned prior. </p><p>And our buttons now look like this:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.53.14-PM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="1078" height="288" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-10.53.14-PM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-10.53.14-PM.png 1000w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.53.14-PM.png 1078w" sizes="(min-width: 720px) 720px"></figure><h2 id="adding-our-own-classes">Adding our own classes</h2><p>Awesome! What if we want our own custom names for colors (e.g. <code>btn-lavender</code>), and with all the nice functionality, like changing shades when hovering over a button? Bootstrap has a mapping function for that! So, using <a href="https://coolors.co/4b4237-d5a021-ede7d9-a49694-736b60">coolors</a> again, here&apos;s a new palette:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.56.45-PM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="2000" height="283" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-10.56.45-PM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-10.56.45-PM.png 1000w, https://www.initialapps.com/content/images/size/w1600/2022/06/Screenshot-2022-06-20-10.56.45-PM.png 1600w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-10.56.45-PM.png 2389w" sizes="(min-width: 720px) 720px"></figure><p>Per the <a href="https://getbootstrap.com/docs/5.0/customize/sass/">bootstrap docs</a>, we can load the Bootstrap variables first then call the map-merge function to add our own new custom classes to our pallet!</p><p>First we define the custom colors after the 4. comment (under the variables and mixins):</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Be sure to add under the #4. comment as order of code matters!</div></div><pre><code>// 4. Include any default map overrides here
// Create your own map
$custom-colors: (
  &quot;dark-lava&quot;: #4B4237,
  &quot;goldenrod&quot;: #D5A021,
  &quot;alabaster&quot;: #EDE7D9,
  &quot;spanish-gray&quot;: #A49694,
  &quot;dim-gray&quot;: #736B60,
);

// Merge the maps
$theme-colors: map-merge($theme-colors, $custom-colors);</code></pre><p>Notice the <code>map-merge</code> function at the end. &#xA0;That&apos;s what creates our new color classes.</p><p>Now let&apos;s test them in our html (<code>App.vue</code>) file by adding the following:</p><pre><code>&lt;h1&gt;Custom Buttons!&lt;/h1&gt;
&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col&quot;&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-dark-lava&quot;&gt;Dark Lava&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-goldenrod&quot;&gt;Goldenrod&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-alabaster&quot;&gt;Alabaster&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-spanish-gray&quot;&gt;Spanish Gray&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-dim-gray&quot;&gt;Dim Gray&lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre><p>And now we check out the site:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-11.14.53-PM-1.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="1340" height="619" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-20-11.14.53-PM-1.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-20-11.14.53-PM-1.png 1000w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-20-11.14.53-PM-1.png 1340w" sizes="(min-width: 720px) 720px"></figure><p>We now have a set of fully functional customized theme classes!</p><h2 id="accessing-all-of-bootstraps-colors">Accessing all of Bootstrap&apos;s colors</h2><p>If you noticed, bootstrap has a ton of extra <a href="https://getbootstrap.com/docs/5.0/customize/color/">color classes</a>! &#xA0;However, they are not added by default so we can also activate them in our color mapping function.</p><p>In this example, I&apos;ll add the following: <code>purple-100</code>, <code>blue-200</code>, <code>pink</code>, and <code>teal-800</code>.</p><p>We add a bit more html code to our <code>App.vue</code> file:</p><pre><code>&lt;h1&gt;Additional Bootstrap Colors&lt;/h1&gt;
&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col&quot;&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-purple-100&quot;&gt;Purple-100&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-blue-200&quot;&gt;Blue-200&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-pink&quot;&gt;Pink&lt;/button&gt;
    &lt;button type=&quot;button&quot; class=&quot;btn btn-teal-800&quot;&gt;Teal-800&lt;/button&gt;
  &lt;/div&gt;
 &lt;/div&gt;  </code></pre><p>Also, we append to our <code>main.scss</code> file in the <code>$custom-colors</code>:</p><pre><code>// 4. Include any default map overrides here
// Create your own map
$custom-colors: (
  &quot;dark-lava&quot;: #4B4237,
  &quot;goldenrod&quot;: #D5A021,
  &quot;alabaster&quot;: #EDE7D9,
  &quot;spanish-gray&quot;: #A49694,
  &quot;dim-gray&quot;: #736B60,
  &quot;purple-100&quot;: $purple-100,
  &quot;blue-200&quot;: $blue-200,
  &quot;pink&quot;: $pink,
  &quot;teal-800&quot;: $teal-800,
);

// Merge the maps
$theme-colors: map-merge($theme-colors, $custom-colors);</code></pre><p>Notice, since we use this function after loading the bootstrap variables, we can just add the extra color variables to our custom colors. </p><p>And the final site looks like this:</p><figure class="kg-card kg-image-card"><img src="https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-21-8.12.14-AM.png" class="kg-image" alt="Customizing bootstrap 5 with Sass! Example provided." loading="lazy" width="1254" height="722" srcset="https://www.initialapps.com/content/images/size/w600/2022/06/Screenshot-2022-06-21-8.12.14-AM.png 600w, https://www.initialapps.com/content/images/size/w1000/2022/06/Screenshot-2022-06-21-8.12.14-AM.png 1000w, https://www.initialapps.com/content/images/2022/06/Screenshot-2022-06-21-8.12.14-AM.png 1254w" sizes="(min-width: 720px) 720px"></figure><h2 id="final-thoughts">Final thoughts</h2><p>Hope this helps you get started setting up Sass on Bootstrap 5!</p><p>Get the source code here: <a href="https://github.com/initialapps/bootstrap-5-sass-example">https://github.com/initialapps/bootstrap-5-sass-example</a></p>]]></content:encoded></item><item><title><![CDATA[Permission denied (publickey) Digital Ocean SSH]]></title><description><![CDATA[How to fix a "Permission Denied (publickey)" error for Digital Ocean, by temporarily allowing password authentication to update your SSH key.]]></description><link>https://www.initialapps.com/permission-denied-publickey-digital-ocean-ssh/</link><guid isPermaLink="false">5eb49ac2ca26f132a97ee4c9</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sat, 16 May 2020 15:18:23 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1550527882-b71dea5f8089?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1550527882-b71dea5f8089?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Permission denied (publickey) Digital Ocean SSH"><p></p><p>Locked out of your Digital Ocean server? &#xA0;Here&apos;s how to get back in. &#xA0;This short article is for anyone that has lost access to the SSH key used to setup their Digital Ocean droplet. </p><p><strong>This article will not help you, if you do not know the username and password used to setup your server.</strong> &#xA0;<a href="https://www.digitalocean.com/docs/droplets/resources/console/">In that case, you will have to instead reset your root password per the Digital Ocean docs.</a></p><p>Finally, we assume that you have some basic knowledge of Ubuntu, SSH keys, command line editors (if not, just google it).</p><p>Basic overview of the following steps:</p><ul><li>Access the Digital Ocean online terminal</li><li>Update the SSH config file to allow username and password login</li><li>Setup new SSH key</li><li>Revert your SSH config file</li></ul><p></p><h2 id="step-1-access-the-digital-ocean-online-terminal">Step 1: Access the Digital Ocean online terminal</h2><p>Log into your Digital Ocean account, select your droplet, and click Access &gt; Launch Console.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2020/05/access.949745c3cdd9acee5832e28ac3e3ced353e92199d4a0375cf7a380c09aacbdde.png" class="kg-image" alt="Permission denied (publickey) Digital Ocean SSH" loading="lazy"></figure><p>This will get you into your droplet through the online terminal. &#xA0;</p><p>If you&apos;re brave enough, you can try to update your ssh key directly through this terminal, but the interface is a bit clunky and crashed on me. Hence, I recommend the next steps.</p><h2 id="step-2-allow-username-and-password">Step 2: &#xA0;Allow username and password</h2><p>Next, we&apos;ll update the droplet&apos;s config file to allow SSH by username and password. Note, the below will be a less secure setup, but we&apos;ll change it back afterward.</p><p>Using your favorite editor (nano below), open the file:</p><pre><code>sudo nano /etc/ssh/sshd_config</code></pre><p>We&apos;ll update this file to turn off PAM (Pluggable Authentication Modules). &#xA0;Find this line and update to <code>no</code>:</p><pre><code>UsePAM no</code></pre><p>We&apos;ll also update this to allow password authentication:</p><pre><code>PasswordAuthentication yes</code></pre><p>Save and exit the file, and reload SSH on your droplet:</p><pre><code>sudo systemctl restart ssh</code></pre><h2 id="step-3-setup-your-new-ssh-key">Step 3: Setup your new SSH key</h2><p>If you don&apos;t have one already on your local machine, set this up:</p><pre><code>ssh-keygen -t rsa</code></pre><p>Copy your key to the server, using your username and droplet IP address in place of the demo below:</p><pre><code>ssh-copy-id username@droplet_IP</code></pre><p>You should now be able to complete the above, and access your droplet with your original username and password. &#xA0;Note, if you do not know your username and password, you will have to instead <a href="https://www.digitalocean.com/docs/droplets/resources/console/">reset your root password per the Digital Ocean docs</a>.</p><h2 id="step-4-revert-your-ssh-config-file">Step 4: Revert your SSH config file</h2><p>Now that you are setup with SSH keys, you can reset your config file back to its original settings. &#xA0;You can either use the Digital Ocean terminal, or SSH into your droplet <code>ssh username@droplet_IP</code>.</p><p>Using your favorite editor (nano below), open the file:</p><pre><code>sudo nano /etc/ssh/sshd_config</code></pre><p>Revert this file to turn PAM <code>on</code>:</p><pre><code>UsePAM yes</code></pre><p>Revert password authentication to <code>off</code>:</p><pre><code>PasswordAuthentication no</code></pre><p>Save and exit the file, and reload SSH on your droplet:</p><pre><code>sudo systemctl restart ssh</code></pre><p>Your droplet should now be good to go!</p>]]></content:encoded></item><item><title><![CDATA[Tracking where Covid-19 is in Orange County]]></title><description><![CDATA[Easily track and visualize where Corona Virus is spreading in Orange County using data provided by the OC Health Care Agency.]]></description><link>https://www.initialapps.com/orange-county-civic-hacking-for-covid-19/</link><guid isPermaLink="false">5e9c7494ca26f132a97ee3ab</guid><category><![CDATA[Civic Projects]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sun, 19 Apr 2020 18:47:18 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1584118624012-df056829fbd0?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1584118624012-df056829fbd0?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Tracking where Covid-19 is in Orange County"><p>We&apos;re excited to be sharing our first <a href="https://www.occiviccoders.com">OC Civic Coder&apos;s</a> project to help easily visualize where Corona Virus is spreading in Orange County using data provided by the <a href="https://www.ochealthinfo.com/phs/about/epidasmt/epi/dip/prevention/novel_coronavirus">OC Health Care Agency</a>. &#xA0;This open source app uses visualization charts, and bubble maps to show where Covid-19 is in Orange County, and how it is spreading over time. &#xA0;</p><p>The goal of this app is to provide tracking data to the general public that&apos;s easy to understand, in efforts to help slow the spread and see if our preventative measures are working. &#xA0;<a href="https://www.occiviccoders.com/covid-response-OC/">This app is hosted on the OC Civic Coders site (check it out)!</a></p><h2 id="how-corona-virus-has-spread-in-orange-county-over-time-by-city">How Corona Virus has spread in Orange County over time by city</h2><!--kg-card-begin: html--><figure class="kg-card kg-image-card"><a href="https://www.occiviccoders.com/covid-response-OC/" target="_blank"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2020/04/OC-covid-spread.png" class="kg-image" alt="Tracking where Covid-19 is in Orange County"></a></figure><!--kg-card-end: html--><p>Case counts have been gradually on the rise from mid March through April, however hospitalizations and ICU counts have been relatively flat. &#xA0;The dropdown menu provides individual case counts by city, thus allowing locals to track and encourage preventative measures close to home. &#xA0;The app additionally breaks down Covid-19 case ratios by gender and age.</p><h2 id="a-bubble-map-for-where-covid-19-is-occurring-in-orange-county">A bubble map for where Covid-19 is occurring in Orange County</h2><!--kg-card-begin: html--><figure class="kg-card kg-image-card"><a href="https://www.occiviccoders.com/covid-response-OC/" target="_blank"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2020/04/OC-covid-location.png" class="kg-image" alt="Tracking where Covid-19 is in Orange County"></a></figure><!--kg-card-end: html--><p>This bubble map displays visual representations for the amount of Corina Virus cases by city in Orange County. &#xA0;The dropdown menu additionally lets you visualize by case counts per population, which in turn displays some interesting results as opposed to visualizing by counts alone. &#xA0;The sidebar to the right sorts case counts from largest to smallest and points out the location when hovered over.</p><p>In addition, the app shows how the spread initially began, trending data by travel related, vs locally spread. &#xA0;Data related to travel is no longer being tracked as most cases now are locally spread within the community.</p><h2 id="built-open-source-by-local-civic-coders">Built open source by local civic coders</h2><p>The Orange County Covid-19 tracker is an open source project made by local residents. &#xA0;It&apos;s an interesting example of coding for causes together to help the local community. &#xA0;The code uses <a href="https://github.com/cheeriojs/cheerio">Cheerio</a>, <a href="https://github.com/axios/axios">Axios</a>, and <a href="https://nodejs.org/en/">Node</a> to get the data from the city website. &#xA0;It then uses <a href="https://www.chartjs.org/">Chartjs</a> and <a href="https://www.mapbox.com/">Mapbox</a> and <a href="https://d3js.org/">D3js</a> to visualize the data. &#xA0;<a href="https://github.com/occiviccoders/covid-response-OC">You can view, download, and contribute to this code on GitHub.</a></p><p></p><p></p><p> &#xA0;</p>]]></content:encoded></item><item><title><![CDATA[OC Civic Coders]]></title><description><![CDATA[Welcome to the very first meetup for OC Civic Coders! Grab your laptops and come out to build something great.]]></description><link>https://www.initialapps.com/oc-civic-coders/</link><guid isPermaLink="false">5e2340f9ca26f132a97ee38f</guid><category><![CDATA[Civic Projects]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sat, 18 Jan 2020 17:32:58 GMT</pubDate><media:content url="https://s3-us-west-1.amazonaws.com/initialapps/2020/01/grayscale-photo-of-computer-laptop-near-white-notebook-and-169573.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://s3-us-west-1.amazonaws.com/initialapps/2020/01/grayscale-photo-of-computer-laptop-near-white-notebook-and-169573.jpg" alt="OC Civic Coders"><p>Welcome to the very first meetup for OC Civic Coders! Grab your laptops and come out to build something great. Feel free to work individually or team up. Connect with others to learn and share knowledge.<br><br>Agenda:<br>1. New project ideas.<br>2. Share your experience levels and what you&apos;d like to gain from the group.<br>3. For those working on a project, share your progress.<br>4. For those completing a project, share what you&apos;ve created!<br>5. Open networking and collaboration time.<br><br>Tips for getting started:<br>- Work on something you&apos;re passionate about.<br>- Work on something practical. For beginners start of with a small project, and simple framework. For others, work on something with a reasonable enough scope to complete it. Look for existing data resources before starting a bunch of code work.<br>- Be prepared to learn. Whether you&apos;re just starting out, or using this as an opportunity to learn a new framework, anything can be achieved with google, patience, and coffee.</p><p>RSVP Here: <a href="https://www.meetup.com/OC-Civic-Coders/events/267756002/">https://www.meetup.com/OC-Civic-Coders/events/267756002/</a></p>]]></content:encoded></item><item><title><![CDATA[Visualizing Traffic Collisions in Los Angeles]]></title><description><![CDATA[Each week there are about a thousand traffic collisions in LA.  This app is a tool to visualize traffic collisions in Los Angeles. ]]></description><link>https://www.initialapps.com/visualizing-traffic-collisions-in-los-angeles/</link><guid isPermaLink="false">5e138ee2ca26f132a97ee349</guid><category><![CDATA[Civic Projects]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Mon, 06 Jan 2020 20:13:37 GMT</pubDate><media:content url="https://s3-us-west-1.amazonaws.com/initialapps/2020/01/city-near-mountain-during-golden-hour-2525903.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://s3-us-west-1.amazonaws.com/initialapps/2020/01/city-near-mountain-during-golden-hour-2525903.jpg" alt="Visualizing Traffic Collisions in Los Angeles"><p>Starting 2020 of right with another civic hacking project. &#xA0;This time, we&apos;ve thrown together an app that visualizes traffic collisions in Los Angeles. &#xA0;Each week there are about a thousand traffic collisions in LA. &#xA0;This data visualization tool can hopefully help for deeper insights, which can lead to new solutions. &#xA0;Big thanks to <a href="https://www.initialapps.com/visualizing-traffic-collisions-in-los-angeles/%20lacity.org">lacity.org</a> for providing the data!</p><h3 id="view-the-demo-website-here"><strong>View the demo website here</strong></h3><p><a href="https://developer.initialapps.com/la-collisions/">https://developer.initialapps.com/la-collisions/</a></p><h3 id="view-the-code-on-github"><strong>View the code on GitHub</strong></h3><p><a href="https://github.com/initialapps/la-collisions">https://github.com/initialapps/la-collisions</a></p><h2 id="project-goals"><strong>Project Goals</strong></h2><p>The goals for this project are simple:</p><ul><li>Exercise some JS data processing techniques</li><li>Provide something civically engaging</li><li>Create something open source, and see where others take it</li></ul><p>Feel free to pull the project from <a href="https://github.com/initialapps/la-collisions">GitHub</a>!</p><h2 id="contributing-to-this-project"><strong>Contributing To This Project</strong></h2><p>Civic hacking is an awesome way to learn coding, while providing value to your local community. &#xA0;This pretty close to a full vanilla javascript project, so contributing is easy. &#xA0;</p><h3 id="built-with"><strong>Built With</strong></h3><ul><li><a href="https://www.mapbox.com/" rel="nofollow">Mapbox</a> - For the maps</li><li><a href="https://getbootstrap.com/" rel="nofollow">Bootstrap</a> - Front end framework</li><li><a href="https://jquery.com/" rel="nofollow">jQuery</a> - Who doesn&apos;t use jquery?</li><li><a href="https://www.chartjs.org/" rel="nofollow">chartjs</a> - Nice charts</li><li><a href="https://flatpickr.js.org/" rel="nofollow">flatpickr</a> - Great date range picker</li></ul><h3 id="getting-started"><strong>Getting Started</strong></h3><p>The app is a simple, single page built with <a href="https://www.mapbox.com/" rel="nofollow">Mapbox</a>. Simply grab a Mapbox API key and <a href="https://github.com/initialapps/la-collisions">clone this repo</a> to use.</p><h3 id="connecting-with-contributors"><strong>Connecting With Contributors</strong></h3><p>Please comment below to connect with others. &#xA0;Share what you&apos;d like to see from this project, or share your creations. &#xA0;Let us know what to do next!</p><p><em><em>Happy Coding!</em></em></p>]]></content:encoded></item><item><title><![CDATA[Spotlight on IMC Intertech]]></title><description><![CDATA[IMC Intertech pushes the limits of micro precision parts, with parts less than .012 cu inches, and tolerancing on the level of microns.  ]]></description><link>https://www.initialapps.com/spotlight-imc-intertech/</link><guid isPermaLink="false">5de43a20a6b608073d931f70</guid><category><![CDATA[Websites]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Sun, 01 Dec 2019 22:12:11 GMT</pubDate><media:content url="https://s3-us-west-1.amazonaws.com/initialapps/2019/12/imc-micro-parts-atf.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/12/imc-micro-parts-atf.jpg" alt="Spotlight on IMC Intertech"><p><a href="https://www.imcintertech.com">IMC Intertech</a> is pushing the limits of micro precision parts, with parts less than .012 cu inches, and tolerancing on the level of microns. &#xA0;</p><p>We&apos;re thrilled to be working with <a href="https://www.imcintertech.com">IMC Intertech</a> on the launch of their new website. &#xA0;When it comes to micro precision and extremely small parts, <a href="https://www.imcintertech.com">IMC Intertech</a> provides unparalleled capabilities, offering incredibly small micro-parts, micro-tubes, micro-needles, and micro-sub-assemblies. &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://www.kaizendesk.com/content/images/2019/12/imc-micro-parts-2.png" class="kg-image" alt="Spotlight on IMC Intertech" loading="lazy"></figure><h2 id="imc-intertech-s-unique-capabilities">IMC Intertech&apos;s Unique Capabilities</h2><p><a href="https://www.imcintertech.com">IMC Intertech</a> provides solutions for a very niche specialty of extremely small part fabrication. &#xA0;Our goal was to help showcase that through use of both wide background images and focused part imagery to gain perspective on just how incredible their capabilities are. </p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://www.kaizendesk.com/content/images/2019/12/micro-fabrication-parts-thumb-2.png" width="1859" height="1328" loading="lazy" alt="Spotlight on IMC Intertech"></div><div class="kg-gallery-image"><img src="https://www.kaizendesk.com/content/images/2019/12/micro-pin-2.jpg" width="3275" height="2339" loading="lazy" alt="Spotlight on IMC Intertech"></div><div class="kg-gallery-image"><img src="https://www.kaizendesk.com/content/images/2019/12/micro-tube-1-1.jpg" width="3228" height="2305" loading="lazy" alt="Spotlight on IMC Intertech"></div></div></div></figure><p>IMC&apos;s capabilities opens new possibilities for designers and innovators, ranging from medical devices, electronics, and many others.</p><h2 id="site-features"><strong><strong>Site Features</strong></strong></h2><p><a href="https://www.imcintertech.com">IMC Intertech</a> is built on WordPress. &#xA0;The site seamlessly integrates with procurement software - <a href="https://www.kaizendesk.com/">KaizenDesk</a>, to streamline your supply chain paperwork, while growing your leads.</p><ul><li>Search engine optimized</li><li>Blog ready</li><li>Mobile optimized</li><li>Fast load times</li><li>Integration with <a href="https://www.kaizendesk.com/">KaizenDesk</a> Online Quoting and Invoicing Platform</li></ul><hr><blockquote><em><em><a href="https://www.initialapps.com/contact-us">Looking to create a new site, or upgrade your current site? Please, don&apos;t hesitate to reach out to us!</a></em></em></blockquote>]]></content:encoded></item><item><title><![CDATA[Testing Out Data-Driven Styling in Mapbox GL JS]]></title><description><![CDATA[Sharing some notes on Mapbox GL JS data driven styling.  Info provided on circle-radius, base, stops, and circle-color.]]></description><link>https://www.initialapps.com/testing-data-driven-styling-mapbox-gl-js/</link><guid isPermaLink="false">5dcb9499a6b608073d931d4d</guid><category><![CDATA[Civic Projects]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Fri, 15 Nov 2019 06:44:33 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1473163928189-364b2c4e1135?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1473163928189-364b2c4e1135?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Testing Out Data-Driven Styling in Mapbox GL JS"><p>Mapbox GL JS is a powerful mapping API, that can create really nice data visualizations and styling. &#xA0;Their maps look really clean and do a nice job at communicating engaging information interactively. &#xA0; </p><p>That said, getting started with Mapbox, and it&apos;s data driven styling may not be completely straightforward, and required me to do a little testing to get started. &#xA0;For this post, I&apos;ll be sharing notes on how this great tool works, using the example Mapbox has provided.</p><p></p><h2 id="style-circles-with-a-data-driven-property">Style circles with a data-driven property</h2><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/11/mabpox-gl-js-1.png" class="kg-image" alt="Testing Out Data-Driven Styling in Mapbox GL JS" loading="lazy"></figure><p>Mapbox provides a nice example for how data driven styling works, which maps out ethnicity data in San Francisco. &#xA0;Check out the code by <a href="https://docs.mapbox.com/mapbox-gl-js/example/data-driven-circle-colors/">clicking here</a>, or also pasted below:</p><pre><code class="language-javascript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset=&apos;utf-8&apos; /&gt;
    &lt;title&gt;Style circles with a data-driven property&lt;/title&gt;
    &lt;meta name=&apos;viewport&apos; content=&apos;initial-scale=1,maximum-scale=1,user-scalable=no&apos; /&gt;
    &lt;script src=&apos;https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js&apos;&gt;&lt;/script&gt;
    &lt;link href=&apos;https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css&apos; rel=&apos;stylesheet&apos; /&gt;
    &lt;style&gt;
        body { margin:0; padding:0; }
        #map { position:absolute; top:0; bottom:0; width:100%; }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div id=&apos;map&apos;&gt;&lt;/div&gt;
&lt;script&gt;
mapboxgl.accessToken = &apos;&lt;your access token here&gt;&apos;;
var map = new mapboxgl.Map({
    container: &apos;map&apos;,
    style: &apos;mapbox://styles/mapbox/light-v10&apos;,
    zoom: 12,
    center: [-122.447303, 37.753574]
});

map.on(&apos;load&apos;, function () {

    /* Sample feature from the `examples.8fgz4egr` tileset:
       {
           &quot;type&quot;: &quot;Feature&quot;,
           &quot;properties&quot;: {
               &quot;ethnicity&quot;: &quot;White&quot;
           },
           &quot;geometry&quot;: {
               &quot;type&quot;: &quot;Point&quot;,
               &quot;coordinates&quot;: [ -122.447303, 37.753574 ]
           }
       }
     */
    map.addLayer({
        &apos;id&apos;: &apos;population&apos;,
        &apos;type&apos;: &apos;circle&apos;,
        &apos;source&apos;: {
            type: &apos;vector&apos;,
            url: &apos;mapbox://examples.8fgz4egr&apos;
        },
        &apos;source-layer&apos;: &apos;sf2010&apos;,
        &apos;paint&apos;: {
            // make circles larger as the user zooms from z12 to z22
            &apos;circle-radius&apos;: {
                &apos;base&apos;: 1.75,
                &apos;stops&apos;: [[12, 2], [22, 180]]
            },
            // color circles by ethnicity, using a match expression
            // https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-match
            &apos;circle-color&apos;: [
                &apos;match&apos;,
                [&apos;get&apos;, &apos;ethnicity&apos;],
                &apos;White&apos;, &apos;#fbb03b&apos;,
                &apos;Black&apos;, &apos;#223b53&apos;,
                &apos;Hispanic&apos;, &apos;#e55e5e&apos;,
                &apos;Asian&apos;, &apos;#3bb2d0&apos;,
                /* other */ &apos;#ccc&apos;
            ]
        }
    });
});
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><p>If you haven&apos;t already received an access token from Mapbox, you can <a href="https://account.mapbox.com/auth/signup/">signup for one here</a>.</p><p>Mapbox examples are easy to get started, as you can get up and running simply by copying their code into a text file and saving with an html extension (e.g. mapbox-demo.html). &#xA0;You will just need to add your access token, by finding this line in the code, and adding it:</p><pre><code>mapboxgl.accessToken = &apos;&lt;your access token here&gt;&apos;;</code></pre><p></p><h2 id="how-the-styling-works">How the Styling Works</h2><h3 id="circle-radius">Circle Radius</h3><h4 id="stops">Stops</h4><p>By default, this allows you to set radius based on zoom level. &#xA0;The format is: [[z<em>oom</em>, <em>size</em>], [<em>zoom</em>, <em>size</em>]]. &#xA0;</p><p>To demonstrate this, let&apos;s change our stops to be from zoom level 12 to 14, so we can see a more dramatic shift:</p><pre><code class="language-javascript">&apos;circle-radius&apos;: {
	&apos;base&apos;: 1.75,
	&apos;stops&apos;: [[12, 2], [14, 180]]
}</code></pre><p>Now, if you zoom in just a little, here&apos;s the result:</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/11/mapbox-gl-js-2.png" class="kg-image" alt="Testing Out Data-Driven Styling in Mapbox GL JS" loading="lazy"></figure><p></p><h4 id="base">Base</h4><p>The base is the rate at which the transition takes between stops. &#xA0;It defaults to 1, which is a linear rate. &#xA0;You can go above or below that for an exponential rate in either direction.</p><p>To demonstrate this, lets initiate our map to load at a zoom level exactly in between our stops, at 13:</p><pre><code>var map = new mapboxgl.Map({
    container: &apos;map&apos;,
    style: &apos;mapbox://styles/mapbox/light-v10&apos;,
    zoom: 13,
    center: [-122.447303, 37.753574]
});</code></pre><p>From here, we can try base levels of both 1 and 100, and compare how they look on loading:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/11/mapbox-gl-js-3.png" width="2398" height="1600" loading="lazy" alt="Testing Out Data-Driven Styling in Mapbox GL JS"></div><div class="kg-gallery-image"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/11/mapbox-gj-js-4-1.png" width="2398" height="1600" loading="lazy" alt="Testing Out Data-Driven Styling in Mapbox GL JS"></div></div></div><figcaption>Base 1 (Left), Base 100 (Right)</figcaption></figure><p>Pretty dramatic difference! &#xA0;Setting the base to 1 gives us a linear relationship, so the dot size is half way between the set points on your stops. &#xA0;Moving the base to 100 gives us a more exponential curve, hence the dots are still small when loaded at midpoint, but will sharply increase as you near the stop setting.</p><h3 id="circle-color">Circle Color</h3><p>The circle color property is more straight forward and should just match the color to a given string property in the geojson object. </p><p>The format is: [&apos;match&apos;,[&apos;get&apos;, <em>object key</em>], <em>object value</em>, <em>color</em>,...]</p><p>The given json object has the following values for ethnicity: White, Black, Hispanic, Asian. &#xA0;Let&apos;s try changing the color for &apos;White&apos; to be white:</p><pre><code>&apos;circle-color&apos;: [
    &apos;match&apos;,
    [&apos;get&apos;, &apos;ethnicity&apos;],
    &apos;White&apos;, &apos;#ffffff&apos;, //hex color for white
    &apos;Black&apos;, &apos;#223b53&apos;,
    &apos;Hispanic&apos;, &apos;#e55e5e&apos;,
    &apos;Asian&apos;, &apos;#3bb2d0&apos;,
    /* other */ &apos;#ccc&apos;
]</code></pre><p>Now take a look at our map. &#xA0;It&apos;s a little more difficult to see, but the yellow dots have now changed to white.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/11/mapbox-gl-js-5.png" class="kg-image" alt="Testing Out Data-Driven Styling in Mapbox GL JS" loading="lazy"></figure><p></p><h2 id="final-thoughts">Final Thoughts</h2><p>The above post just scratches the surface of what Mapbox can do with data driven styling. &#xA0;You can additionally set sizes based on json properties, add map markers and more. &#xA0;Hope this helps you gain a footing with this API. &#xA0;If you find this info helpful and would like more content about Mapbox or data visualizations, please feel free to comment below.</p>]]></content:encoded></item><item><title><![CDATA[Going Deep on Mongoose Deep Population, and How To Populate Across Multiple Levels]]></title><description><![CDATA[A deep dive into Mongoose populate, to help avoid some pitfalls that you may encounter when setting this up.]]></description><link>https://www.initialapps.com/mongoose-why-you-may-be-having-issues-populating-across-multiple-levels/</link><guid isPermaLink="false">5d745982a6b608073d931af9</guid><category><![CDATA[Tutorials]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Wed, 11 Sep 2019 20:02:53 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1489347215392-cee3b4f539aa?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1489347215392-cee3b4f539aa?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Going Deep on Mongoose Deep Population, and How To Populate Across Multiple Levels"><p>If you&apos;ve made the switch from SQL to NoSQL, you&apos;ve probably started to dabble into Mongoose population as a means to make up for the lack of table joins. &#xA0;This, unfortunately, is one area where NoSQL databases fall short of their SQL counterpart. &#xA0;Mongoose population can be tricky to setup. &#xA0;This article take a deeper dive into Mongoose populate, to help avoid some pitfalls that you may encounter setting this up.</p><h2 id="what-mongoose-populate-does">What Mongoose Populate Does</h2><p>Mongoose stores data sets in JavaScript objects, hence you may have a JS object for <strong>Users</strong>. &#xA0;A user in turn, may have <strong>friends</strong>, which are an array of other <strong>Users</strong>. &#xA0;They will also likely have arrays of other objects, such as <strong>Posts</strong>, <strong>Comments</strong>, and even <strong>Comments</strong> of <strong>Comments</strong>. </p><p>In SQL you could simply perform table joins. &#xA0;For NoSQL databases like Mongo, your best option is to create separate JS object <em>schemas</em> for each, and reference the connection by object ID. &#xA0;For example, the User object will contain arrays for other Users, Posts, Comments, etc. &#xA0;Each array would only contain the object IDs of the objects they are pointing to (i.e. User IDs, Post IDs, Comment IDs, etc). &#xA0;When querying data, you can then call the populate function to load the data of the associated object for the arrays that you select.</p><p>Yes, this has drawbacks. &#xA0;For example, if you delete an object such as a <strong>User</strong>, you will also need to find everywhere that references that user, and delete the user ID from there as well (such as a user&apos;s friends). &#xA0;Not doing so can cause issues when trying to populate the ID&apos;s that no longer exist. &#xA0;Even with these drawbacks, you&apos;ll find that working with the populate function is still probably your best option, over other JavaScript hacks.</p><h2 id="populating-across-multiple-levels">Populating Across Multiple Levels </h2><p>Mongoose provides great <a href="https://mongoosejs.com/docs/populate.html">documentation</a> for how to get started with populating arrays. &#xA0;Below is a simple example they have provided for populating across multiple levels. &#xA0;In this case we have <strong>Users</strong>, with an array of friends that are other <strong>Users</strong>. &#xA0;We would like to populate the object to show the user&apos;s friends, and friends of friends.</p><p>First, we need the <strong>User</strong> schema:</p><pre><code class="language-javascript">const mongoose = require(&apos;mongoose&apos;);
const Schema = mongoose.Schema;
const ObjectId = mongoose.Schema.Types.ObjectId;


const userSchema = new Schema({
      name: String,
      friends: [{ type: ObjectId, ref: &apos;User&apos; }]
});


const User = mongoose.model(&apos;User&apos;, userSchema);</code></pre><p>Notice, we are only tracking an array of Object IDs for the user&apos;s friends. &#xA0;Those Object IDs are just other User IDs.</p><p></p><p>Next, we&apos;ll query by user name, and populate friends, and friends of friends:</p><pre><code class="language-javascript">User.findOne({
    name: &apos;Val&apos;
}).
populate({
    path: &apos;friends&apos;,
    // Get friends of friends - populate the &apos;friends&apos; array for every friend
    populate: { path: &apos;friends&apos; }
}).
exec(function (err, user) {
    if (err) return handleError(err);
    console.log(&apos;Here is the populated user: &apos;, user);
});</code></pre><p>The return value should convert all of the Object IDs in the friends array, to the actual data in the associated <strong>User</strong> schema. &#xA0;In the above example, we&apos;ve done this for 2 levels, so it will also populate data for friends of friends.</p><p></p><h2 id="populating-with-multiple-schemas-across-multiple-levels">Populating With Multiple Schemas Across Multiple Levels</h2><p>In many applications, you&apos;ll want to do a lot more populating. &#xA0;Here&apos;s how to do that. Let&apos;s add posts and post comments to our object.</p><p></p><p>First off, let&apos;s define our Schemas. These will need to be readily available:</p><pre><code class="language-javascript">const mongoose = require(&apos;mongoose&apos;);
const Schema = mongoose.Schema;
const ObjectId = mongoose.Schema.Types.ObjectId;


const userSchema = new Schema({
    name: String,
    friends: [{ type: ObjectId, ref: &apos;User&apos; }],
    posts: [{ type: ObjectId, ref: &apos;Post&apos; }]
});
const postSchema = new Schema({
    name: String,
    content: String,
    comments: [{ type: ObjectId, ref: &apos;Comment&apos; }]
});
const commentSchema = new Schema({
    name: String,
    content: String
});


const User = mongoose.model(&apos;User&apos;, userSchema);
const Post = mongoose.model(&apos;Post&apos;, postSchema);
const Comment = mongoose.model(&apos;Comment&apos;, commentSchema);</code></pre><p></p><p></p><p>Next, we query and populate the document. &#xA0;Let&apos;s say we&apos;re no longer interested in friends of friends, but we want to populate friends, posts, and post comments. &#xA0;Here&apos;s how we populate these:</p><pre><code class="language-javascript">User.findOne({
    name: &apos;Val&apos;
}).
populate({
    path: &apos;friends&apos;,
    model: &apos;User&apos;
}).
populate({
    path: &apos;posts&apos;,
    model: &apos;Post&apos;,
    populate: {
        path: &apos;comments&apos;,
        model: &apos;Comment&apos;
    }
}).
exec(function (err, user) {
    if (err) return handleError(err);
    console.log(&apos;Here is the populated user: &apos;, user);
});</code></pre><p>Notice, how we now have defined the <strong>model </strong>in each object. &#xA0;Mongoose will often need this with multiple schemas. &#xA0;</p><p>To populate multiple paths, we simply repeat the populate function. &#xA0;To dig deeper, we just populate a level deeper within the object. &#xA0;</p><p>Final note, if you get stuck, be sure to log out your errors on execution. &#xA0;Mongoose provides good error handling for those who listen!</p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Teach Yourself To Code Part 4 - Intro To VS Code]]></title><description><![CDATA[<p>If you&apos;ve followed us up until now, you&apos;ve probably found <em>nano</em> a bit frustrating to work with. &#xA0;That&apos;s where Visual Studios Code comes in to make your life a bit easier. &#xA0;</p><p>VS Code is a powerful coding text editor that helps you</p>]]></description><link>https://www.initialapps.com/building-your-first-app-part-4-intro-to-vscode/</link><guid isPermaLink="false">5d1f805efe2b6338c510f447</guid><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Mon, 08 Jul 2019 16:19:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1489641024260-20e5cb3ee4aa?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1489641024260-20e5cb3ee4aa?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Teach Yourself To Code Part 4 - Intro To VS Code"><p>If you&apos;ve followed us up until now, you&apos;ve probably found <em>nano</em> a bit frustrating to work with. &#xA0;That&apos;s where Visual Studios Code comes in to make your life a bit easier. &#xA0;</p><p>VS Code is a powerful coding text editor that helps you write software, work the command line, and more. &#xA0;This post will give a very brief intro to some of the features in VS Code.</p><p></p><h2 id="prerequisites">Prerequisites </h2><ul><li>We&apos;ll be using command line tools - <a href="https://www.initialapps.com/building-your-first-app-hacking-the-command-line/">See Part 1 for an intro</a>!</li><li>We&apos;ll demo this using the <a href="https://www.initialapps.com/building-your-first-app-getting-started-with-git/">simple website we created in Part 2</a>.</li><li>You&apos;ll want some basic knowledge of html, css, and javascript. &#xA0;Try <a href="https://www.freecodecamp.org/">freeCodeCamp</a> or <a href="https://www.udemy.com/">Udemy</a> to learn more.</li></ul><p></p><h2 id="installing-vs-code">Installing VS Code</h2><p>Head over to the <a href="https://code.visualstudio.com/">Visual Studio Code website</a> and follow the installation instructions for your system.</p><p></p><p></p><h2 id="setting-up-a-workspace">Setting Up A Workspace</h2><p>VS code is super powerful, but here are the basic essentials that every developer uses. We&apos;ll demo this on the webpage that we made in <a href="https://www.initialapps.com/building-your-first-app-getting-started-with-git/">Part 2</a> of this series.</p><p></p><p>Start by opening VSCode clicking <strong>Add workspace folder...</strong></p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-1.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p>Find the <strong>webpage</strong> directory that you made from Part 2. Highlight it and click the <strong>Add</strong> button.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-2.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p>You can now see your files in the left sidebar.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-3.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p>You can save this workspace from the file dropdown in the top left corner. &#xA0;Click <strong>Save workspace as...</strong> and create a file name. &#xA0;Then click the <strong>Save</strong> button.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-4.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p></p><h2 id="editing-a-file">Editing A File</h2><p>This is pretty self explanatory, but you can edit any file by clicking on it from the left sidebar. </p><p>To save your edits, hit <code>&#x2318; (or ctrl) + s</code> or from the file dropdown menu in the top left, click <strong>save</strong>.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-5.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p></p><h2 id="using-the-command-line">Using The Command Line</h2><p>VS Code has a built in command line! &#xA0;You can access it by clicking <code>&#x2318; (or ctrl) + `</code> or from the view dropdown menu at the top of the screen, click <strong>Terminal</strong>.</p><figure class="kg-card kg-image-card"><img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/vs-code-6.png" class="kg-image" alt="Teach Yourself To Code Part 4 - Intro To VS Code" loading="lazy"></figure><p></p><p>Now when ready to commit your changes to git and push to GitHub, simply enter into the terminal:</p><pre><code>git add --all
git commit -m &quot;my vscode updates&quot;
git push origin master</code></pre><p></p><p></p><h2 id="final-thoughts">Final Thoughts</h2><p>Visual Studios Code is a super powerful web development tool. &#xA0;In this article, we&apos;ve only scratched the surface for all that it can do, but have laid the foundation for tutorials to come. &#xA0;</p><p>One other final thought - don&apos;t discount the power of <em>nano</em> as a powerful minimalist text editor. &#xA0;There will be times during development when you will need a command line text editor, for troubleshooting or other reasons. &#xA0;I worked with only <em>nano</em> for 3 years, and became faster using nano than other editors! <a href="https://emojipedia.org/nerd-face/">?</a></p><p>If you&apos;ve selected <em>nano</em> as your default terminal editor, it will pop up from time to time. &#xA0;So be familiar with how to use it, and when lost just google how to use it.</p><p></p><h2 id="this-article-is-part-of-an-ongoing-series-subscribe-below-if-this-content-helps-you-"><strong><strong><strong>This article is part of an ongoing series. &#xA0;Subscribe below if this content helps you!</strong></strong></strong></h2>]]></content:encoded></item><item><title><![CDATA[Ghost Bootstrap Starter Theme]]></title><description><![CDATA[A super simple and minimal ghost starter theme using Bootstrap. 
Ghost is world's most popular modern open source publishing platform.  This starter theme is developed to help organizations create customized blogs using Ghost and Bootstrap.]]></description><link>https://www.initialapps.com/ghost-bootstrap-starter-theme/</link><guid isPermaLink="false">5d1f92b9fe2b6338c510f4bf</guid><category><![CDATA[Civic Projects]]></category><dc:creator><![CDATA[Andrew Akagawa]]></dc:creator><pubDate>Fri, 05 Jul 2019 18:42:10 GMT</pubDate><media:content url="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/ghost-bootstrap.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://s3-us-west-1.amazonaws.com/initialapps/2019/07/ghost-bootstrap.jpg" alt="Ghost Bootstrap Starter Theme"><p>A super simple and minimal <a href="https://ghost.org/">Ghost</a> starter theme using Bootstrap. </p><p>Ghost is world&apos;s most popular modern open source publishing platform. &#xA0;This starter theme is developed in efforts to help organizations create customized blogs using Ghost and Bootstrap.</p><h5 id="get-this-theme-from-github"><a href="https://github.com/initialapps/ghost-bootstrap">Get this theme from GitHub</a></h5><h2 id="this-theme-uses-">This theme uses:</h2><ul><li><a href="https://getbootstrap.com/">Twitter Bootstrap - The most popular HTML, CSS, and JS library in the world.</a></li><li><a href="https://fontawesome.com/?from=io">Font Awesome - The web&apos;s most popular icon set and toolkit.</a></li><li><a href="https://www.initialapps.com/ghost-bootstrap-starter-theme/Animate.css">Animate.css - A great toolkit for CSS animations.</a></li><li><a href="https://wowjs.uk/">Wow.js &#x2014; The js counterpart to animate.css that reveals animations when scrolling</a></li></ul><h2 id="using-this-theme-">Using this theme:</h2><p>This theme is intended as a starter kit to develop a customized site. &#xA0;It is recommended to start by using &#xA0;<a href="https://docs.ghost.org/install/local/" rel="nofollow">Ghost Local</a> to develop the theme&apos;s front end as you like. &#xA0;Head over to the &#xA0;<a href="https://docs.ghost.org/install/local/" rel="nofollow">Ghost Local</a> site for installation instructions.</p><h3 id="installing-the-theme-for-development">Installing the theme for development</h3><p>From the directory that you&apos;ve installed ghost local, move into the themes folder:</p><pre><code>cd conent/themes
</code></pre><p>Clone the repo:</p><pre><code>git clone git@github.com:initialapps/ghost-bootstrap.git
</code></pre><p>Go back to the directory that you&apos;ve installed ghost local, and restart ghost local:</p><pre><code>cd - // if needed, or navigat to the main ghost directory however you please
ghost restart
</code></pre><p>Finally, navigate to the ghost local link provided in the terminal. When logged into ghost, click <strong>Settings &gt; Design</strong>. Scroll to the bottom and activate the <strong>ghost-bootstrap</strong> theme.</p><h3 id="setting-up-your-routes">Setting up your routes</h3><p>This theme supports a home page, and separate blog page. In order to do this in ghost, you must upload a custom <strong>routes.yml</strong> file. You can download your current file, when logged into ghost local, by clicking <strong>Settings &gt; Design</strong> in the left sidebar and scrolling to <strong>Routes</strong> at the bottom.</p><p>You should edit this file to look like this:</p><pre><code>routes:
  /:
    controller: channel
    template: home

collections:
  /blog/:
    permalink: /{slug}/
    template: index

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/
</code></pre><p>Once complete, upload back to your ghost local blog by clicking the upload button just above where you downloaded.</p><h3 id="developing">Developing</h3><ul><li>You can add to the theme css by editing the css theme file at <strong>assets&gt;css&gt;screen.css</strong></li><li>You can add to the theme js by editing the js theme file at <strong>assets&gt;js&gt;main.js</strong></li><li>For further theme development visit the <a href="https://docs.ghost.org/api/handlebars-themes/">ghost theme building documentation (click here)</a>.</li></ul><h2 id="deployment">Deployment</h2><p>Vendor css and js files are built using gulp and output as <strong>assets/css/build.css</strong> and <strong>assets/js/build.js</strong>. Feel free to add your custom scripts to the gulpfile (gulpfile.js) and use <a href="https://gulpjs.com/" rel="nofollow">gulp</a>.</p><p>The easiest way to deploy the theme is to just download the zip file from <strong>Settings &gt; Design</strong> area when running ghost locally. Then uploading the theme to your live site. Be sure to add your <strong>routes.yml</strong> file as well.</p><p></p><h5 id="see-a-demo-of-this-theme"><a href="https://ghost-bootstrap.initialapps.com/">See a demo of this theme</a></h5>]]></content:encoded></item></channel></rss>