Lernen Sie Node + MongoDB, indem Sie ein URL Shortener-Projekt erstellen

Wenn Sie etwas lernen möchten, gibt es keinen besseren Weg, als ein Projekt um das zu erstellen, was Sie lernen möchten?

In diesem Blog-Beitrag lernen wir MongoDB, Mongoose, Node und andere Technologien kennen, indem wir eine einfache URL-Shortener-Anwendung erstellen.

URL-Shortener gibt es überall, von Links, die Sie auf Twitter teilen, bis zu beliebten Diensten wie bit.ly. Aber haben Sie sich jemals gefragt, wie Sie einen schnellen URL-Shortener für sich selbst erstellen können?

Wir werden also die praktische Praxis durchlaufen, einen URL-Shortener mit MongoDB als Backend-Lösung zu erstellen. Dieses Projekt gibt Ihnen Vertrauen in Ihr Wissen und festigt jedes Konzept, das Sie lernen. Lass uns anfangen.

Einführung in das Projekt

Wir werden dieses kostenlose URL-Shortener-Klassenzimmer von codedamn verwenden, um praktische Übungen zu erhalten und unsere Fortschritte im weiteren Verlauf zu bewerten.

Wir werden die folgenden Technologien verwenden:

  • Mungo als ORM
  • MongoDB als Backend-Datenbank
  • Node.js als Backend
  • Eine einfache eingebettete JS-Datei als Frontend

Wir werden dieses Projekt in 7 Schritten abschließen, die Sie von Anfang bis Ende begleiten. Beginnen wir jetzt mit den Labors.

Teil 1: Einrichten des Express-Servers

Lassen Sie uns zuerst unseren Knotenserver einrichten. Wir werden Express als Framework für diesen Teil verwenden, da es einfach zu bearbeiten ist. Hier ist der Link zu diesem Teil.

Wir können sehen, dass dies eine ziemlich einfache Übung ist. Die einzigen zwei Herausforderungen, die wir bewältigen müssen, sind die folgenden:

Die Lösung könnte folgendermaßen aussehen:

// Initialize express server on PORT 1337 const express = require('express') const app = express() app.get('/', (req, res) => { res.send('Hello World! - from codedamn') }) app.get('/short', (req, res) => { res.send('Hello from short') }) app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') })

Simpel und einfach. Wir erstellen eine andere GET-Route mit app.getund sie sollte die Arbeit erledigen.

Teil 2: Einrichten unserer View Engine

Nachdem wir mit der Express-Installation vertraut sind, werfen .ejswir einen Blick auf die Vorlage, die wir haben. Hier ist der Link zu diesem Teil.

Mit der EJS-Engine können Sie Variablen mit dem Code Node.js an Ihren HTML-Code übergeben und diese iterieren oder anzeigen, bevor Sie eine tatsächliche Antwort an den Server senden.

Schauen Sie sich die views/index.ejsDatei kurz an. Es sieht ähnlich aus wie eine normale HTML-Datei, außer dass Sie Variablen verwenden können.

Hier ist unsere aktuelle index.jsDatei:

Jetzt können Sie sehen, dass index.jswir in der Datei die Zeile haben app.set('view engine', 'ejs'). Es weist Express an, es ejsals Standard-Template-Engine zu verwenden.

Stellen Sie schließlich sicher, dass wir res.render verwenden und nur den Dateinamen übergeben, nicht den vollständigen Pfad. Dies liegt daran, dass Express automatisch im Ansichtsordner nach verfügbaren .ejsVorlagen sucht .

Wir übergeben Variablen als zweites Argument, auf das wir dann in der EJS-Datei zugreifen können. Wir werden diese Datei später verwenden, aber jetzt gehen wir eine kurze Herausforderung durch.

Um diese Herausforderung abzuschließen, müssen wir nur den Namen von Mehulin etwas anderes ändern .

Um diese Herausforderung zu bestehen, zeigen Sie zuerst die index.ejsDatei an und aktualisieren Sie dann Ihren Namen auf einen beliebigen anderen Namen. Hier ist eine gute Lösung:

const express = require('express') const app = express() app.set('view engine', 'ejs') app.get('/', (req, res) => { res.render('index', { myVariable: 'My name is John!' }) }) app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') })

Teil 3: MongoDB einrichten

Nachdem wir ein wenig Frontend- und Backend-Verständnis haben, können wir MongoDB einrichten. Hier ist der Link zu diesem Teil.

Wir werden Mongoose verwenden, um eine Verbindung zu MongoDB herzustellen. Mongoose ist ein ORM für MongoDB.

MongoDB ist einfach gesagt eine sehr lockere Datenbank und ermöglicht alle Arten von Operationen an allem.

Während es für unstrukturierte Daten gut ist, wissen wir die meiste Zeit tatsächlich, wie die Daten aussehen werden (wie Benutzeraufzeichnungen oder Zahlungsaufzeichnungen). Daher können wir mit Mongoose ein Schema für MongoDB definieren . Dies erleichtert uns viele Funktionen.

Sobald wir beispielsweise ein Schema haben, können wir sicher sein, dass die Datenvalidierung und alle erforderlichen Überprüfungen von Mongoose automatisch durchgeführt werden. Mungo gibt uns auch eine Reihe von Hilfsfunktionen, um unser Leben leichter zu machen. Lassen Sie es uns jetzt einrichten.

Um diesen Teil abzuschließen, müssen wir uns um folgende Punkte kümmern:

  • Das Mongoose NPM-Paket wurde bereits für Sie installiert. Sie können es direkt require.
  • Stellen Sie mongodb://localhost:27017/codedamnmit der mongoose.connectMethode eine Verbindung zur URL her .

Hier ist unsere aktuelle index.js-Datei:

const express = require('express') const app = express() const mongoose = require('mongoose') app.set('view engine', 'ejs') app.get('/', (req, res) => { res.render('index') }) app.post('/short', (req, res) => { const db = mongoose.connection.db // insert the record in 'test' collection res.json({ ok: 1 }) }) // Setup your mongodb connection here // mongoose.connect(...) // Wait for mongodb connection before server starts app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') }) 

Füllen wir die entsprechenden Platzhalter mit dem entsprechenden Code aus:

const express = require('express') const app = express() const mongoose = require('mongoose') app.set('view engine', 'ejs') app.get('/', (req, res) => { res.render('index') }) app.post('/short', (req, res) => { const db = mongoose.connection.db // insert the record in 'test' collection db.collection('test').insertOne({ testCompleted: 1 }) res.json({ ok: 1 }) }) // Setup your mongodb connection here mongoose.connect('mongodb://localhost/codedamn', { useNewUrlParser: true, useUnifiedTopology: true }) mongoose.connection.on('open', () => { // Wait for mongodb connection before server starts app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') }) })

Beachten Sie, wie wir unseren HTTP-Server nur starten, wenn unsere Verbindung mit MongoDB geöffnet ist. Dies ist in Ordnung, da wir nicht möchten, dass Benutzer unsere Routen treffen, bevor unsere Datenbank bereit ist.

Wir verwenden die db.collectionMethode hier schließlich, um einen einfachen Datensatz einzufügen, aber wir werden bald eine bessere Möglichkeit haben, mit der Datenbank mithilfe von Mungo-Modellen zu interagieren.

Teil 4: Einrichten eines Mungo-Schemas

Nachdem wir im letzten Abschnitt praktische Erfahrungen mit der MongoDB-Implementierung gesammelt haben, zeichnen wir das Schema für unseren URL-Shortener. Hier ist der Link für diesen Teil.

A Mongoose schema allows us to interact with the Mongo collections in an abstract way. Mongoose's rich documents also expose helper functions like .save which are enough to perform a full DB query to update changes in your document.

Here's how our schema for the URL shortener will look:

const mongoose = require('mongoose') const shortId = require('shortid') const shortUrlSchema = new mongoose.Schema({ full: { type: String, required: true }, short: { type: String, required: true, default: shortId.generate }, clicks: { type: Number, required: true, default: 0 } }) module.exports = mongoose.model('ShortUrl', shortUrlSchema)

We'll store this file in the models/url.js file. Once we have the schema, we can pass this part of the exercise. We have to do the following two things:

  1. Create this model in the models/url.js file. (We did that.)
  2. A POST request to /short should add something to the database to this model.

In order to do that, we can generate a new record using the following code:

app.post('/short', async (req, res) => { // insert the record using the model const record = new ShortURL({ full: 'test' }) await record.save() res.json({ ok: 1 }) })

You'll see that we can omit the clicks and short field because they already have a default value in the schema. This means Mongoose will populate them automatically when the query runs.

Our final index.js file to pass this challenge should look like this:

const express = require('express') const app = express() const mongoose = require('mongoose') // import the model here const ShortURL = require('./models/url') app.set('view engine', 'ejs') app.get('/', (req, res) => { res.render('index', { myVariable: 'My name is John!' }) }) app.post('/short', async (req, res) => { // insert the record using the model const record = new ShortURL({ full: 'test' }) await record.save() res.json({ ok: 1 }) }) // Setup your mongodb connection here mongoose.connect('mongodb://localhost/codedamn') mongoose.connection.on('open', () => { // Wait for mongodb connection before server starts app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') }) })

Part 5: Linking the frontend, backend, + MongoDB

Now that we have a handle on the backend part, let’s get back to the frontend and setup our webpage. There we can use the Shrink button to actually add some records to the database. Here's the link to this part.

If you look inside the views/index.ejs file, you’ll see that we have already passed the form data on the backend /short route. But right now we are not grabbing it.

  • You can see that there’s a new line called app.use(express.urlencoded({ extended: false })) on line 8, which allows us to read the response of the user from the form.
  • In the index.ejs file, you can see that we set name=”fullURL” which is how we can receive the URL on the backend.

Here's our index.ejs file:

       codedamn URL Shortner Project 

URL Shrinker

URL Shrink This! { %>
Full URLShort URLClicks

This is a simple challenge, because we just have to put this code in to complete it:

app.use(express.urlencoded({ extended: false })) app.post('/short', async (req, res) => { // Grab the fullUrl parameter from the req.body const fullUrl = req.body.fullUrl console.log('URL requested: ', fullUrl) // insert and wait for the record to be inserted using the model const record = new ShortURL({ full: fullUrl }) await record.save() res.redirect('/') })

First of all, we grab the sent URL by HTML using the req.body.fullUrl. To enable this, we also have app.use(express.urlencoded({ extended: false })) which allows us to get the form data.

Then we create and save our record just like we did the last time. Finally, we redirect the user back to the homepage so that the user can see the new links.

Tip: You can make this application more interesting by performing an Ajax request to the backend API instead of typical form submission. But we'll leave it here as it focuses more on MongoDB + Node setup instead of JavaScript.

Part 6: Displaying short URLs on the frontend

Now that we’re storing shortened URLs in MongoDB, let’s go ahead and show them on the frontend as well.

Remember our variables passed down to the ejs template from before? Now we’ll be using them.

The template loop for ejs has been done for you in the index.ejs file (you can see that loop above). However, we have to write the Mongoose query to extract the data in this section.

If we see the template, we'll see that in index.js we have the following code:

app.get('/', (req, res) => { const allData = [] // write a mongoose query to get all URLs from here res.render('index', { shortUrls: allData }) }) 

We already have a model defined with us to query data from Mongoose. Let's use it to get everything we need.

Here's our solution file:

const express = require('express') const app = express() const mongoose = require('mongoose') // import the model here const ShortURL = require('./models/url') app.set('view engine', 'ejs') app.use(express.urlencoded({ extended: false })) app.get('/', async (req, res) => { const allData = await ShortURL.find() res.render('index', { shortUrls: allData }) }) app.post('/short', async (req, res) => { // Grab the fullUrl parameter from the req.body const fullUrl = req.body.fullUrl console.log('URL requested: ', fullUrl) // insert and wait for the record to be inserted using the model const record = new ShortURL({ full: fullUrl }) await record.save() res.redirect('/') }) // Setup your mongodb connection here mongoose.connect('mongodb://localhost/codedamn', { useNewUrlParser: true, useUnifiedTopology: true }) mongoose.connection.on('open', async () => { // Wait for mongodb connection before server starts // Just 2 URLs for testing purpose await ShortURL.create({ full: '//google.com' }) await ShortURL.create({ full: '//codedamn.com' }) app.listen(process.env.PUBLIC_PORT, () => { console.log('Server started') }) })

You can see that it was as easy as doing await ShortURL.find() in the allData variable. The next part is where things get a bit tricky.

Part 7: Making the redirection work

We’re almost done! We have the full URL and short URL stored in the database now, and we show them on the frontend too.

But you’ll notice that the redirection does not work right now and we get an Express error.

Let’s fix that. You can see in the index.js file there’s a new dynamic route added at the end which handles these redirects:

app.get('/:shortid', async (req, res) => { // grab the :shortid param const shortid = '' // perform the mongoose call to find the long URL // if null, set status to 404 (res.sendStatus(404)) // if not null, increment the click count in database // redirect the user to original link })

Our challenges for this part looks like this:

Alright. First things first, we have to extract out the full URL when we visit a short URL. Here's how we'll do that:

app.get('/:shortid', async (req, res) => { // grab the :shortid param const shortid = req.params.shortid // perform the mongoose call to find the long URL const rec = await ShortURL.findOne({ short: shortid }) // ... }) 

Now, if we see that our result is null, we'll send a 404 status:

app.get('/:shortid', async (req, res) => { // grab the :shortid param const shortid = req.params.shortid // perform the mongoose call to find the long URL const rec = await ShortURL.findOne({ short: shortid }) // if null, set status to 404 (res.sendStatus(404)) if (!rec) return res.sendStatus(404) res.sendStatus(200) })

This passes our first challenge. Next, if we in fact have a link, let's redirect the user and increment the click count too in the database.

app.get('/:shortid', async (req, res) => { // grab the :shortid param const shortid = req.params.shortid // perform the mongoose call to find the long URL const rec = await ShortURL.findOne({ short: shortid }) // if null, set status to 404 (res.sendStatus(404)) if (!rec) return res.sendStatus(404) // if not null, increment the click count in database rec.clicks++ await rec.save() // redirect the user to original link res.redirect(rec.full) })

This way, we can increment and store the result in the database again. And that should pass all of our challenges.

Conclusion

Congratulations! You just built a full working URL shortener by yourself using Express + Node + MongoDB. Give yourself a pat on back!

The final source code is available on GitHub.

If you have any feedback on this article or codedamn classrooms, feel free to reach out to me on Twitter. Let's discuss :)