Tanulja meg a Node + MongoDB-t egy URL-rövidítő projekt létrehozásával

Ha meg akarsz tanulni valamiről, mi a jobb módja annak, ha projektet építesz a megtanulni kívánt dolog köré?

Ebben a blogbejegyzésben megismerhetjük a MongoDB-t, a Mongoose-ot, a Node-ot és más technológiákat egy egyszerű URL-rövidítő alkalmazás felépítésével.

Az URL-rövidítők mindenhol megtalálhatók, a twitteren megosztott linkektől az olyan népszerű szolgáltatásokig, mint a bit.ly. De vajon elgondolkodott már azon, hogyan hozhat létre magának gyors URL-rövidítőt?

Tehát átéljük az URL-rövidítők kiépítésének gyakorlati gyakorlatát a MongoDB-vel háttér-megoldásként. Ez a projekt bizalmat fog adni a tudásában és megszilárdítja az egyes elsajátított koncepciókat. Kezdjük el.

Bevezetés a projektbe

A codedamn ingyenes URL-rövidítő tantermét arra használjuk, hogy gyakorlati gyakorlatot szerezzünk és értékeljük az előrehaladást, ahogy haladunk.

A következő technológiákat fogjuk használni:

  • Mongúz, mint ORM
  • MongoDB háttér-adatbázisként
  • A Node.js háttérképként
  • Egy egyszerű beágyazott JS fájl, mint frontend

Ezt a projektet 7 lépésben fejezzük be, amelyek végigvezetik Önt a kezdetektől a befejezésig. Indítsuk el most a laborokat.

1. rész: Az Express kiszolgáló beállítása

Először állítsuk be a Node szerverünket. Ehhez a részhez az Express-t fogjuk használni, mivel könnyen kezelhető. Itt található a link erre a részre.

Láthatjuk, hogy ez egy meglehetősen könnyű gyakorlat. Csak a következő két kihívást kell leküzdenünk:

A megoldás így nézhet ki:

// 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') })

Egyszerű és könnyű. Egy újabb GET útvonalat hozunk létre a használatával app.get, és ezzel el kell végeznie a munkát.

2. rész: Nézet motorunk beállítása

Most, hogy ismerkedünk az Express telepítésével, vessünk egy pillantást a rendelkezésünkre álló .ejssablonra. Itt található a link erre a részre.

Az EJS motor lehetővé teszi, hogy a változókat a Node.js kóddal továbbítsa a HTML-be, és iterálja vagy megjelenítse őket, mielőtt tényleges választ küldene a szervernek.

Vessen egy gyors pillantást a views/index.ejsfájlra. Hasonló lesz a szokásos HTML fájl kinézetéhez, azzal a különbséggel, hogy használhat változókat.

Itt van a jelenlegi index.jsfájlunk:

Most láthatja, hogy a index.jsfájlban megvan a sor app.set('view engine', 'ejs'). Azt mondja az Express-nek ejs, hogy alapértelmezett sablonmotorként használja.

Végül nézze meg, hogy a res.render fájlt használjuk, és csak a fájl nevét adjuk át, a teljes elérési útját nem. Az Express ugyanis automatikusan megkeresi a nézetek mappáját az elérhető .ejssablonok után.

Második argumentumként változókat adunk át, amelyekhez az EJS fájlban férhetünk hozzá. Ezt a fájlt később fogjuk használni, de egyelőre menjünk át egy gyors kihíváson.

A kihívás teljesítéséhez csak át kell változtatnunk a nevet Mehulbármi másra.

A kihívás teljesítéséhez index.ejselőször tekintse meg a fájlt, majd frissítse a nevét bármi másra, ami tetszik. Íme egy jó megoldás:

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') })

3. rész: A MongoDB beállítása

Most, hogy van egy kis megértésünk a frontendről és a backendről, folytassuk és állítsuk be a MongoDB-t. Itt található a link erre a részre.

A Mongoose-t fogjuk használni a MongoDB-hez való csatlakozáshoz. A Mongoose a MongoDB ORM-je.

Egyszerűen fogalmazva: a MongoDB egy nagyon laza adatbázis, és mindenféle műveletet megenged bármire.

Bár strukturálatlan adatokra jó, legtöbbször valóban tisztában vagyunk azzal, hogy mi lesz az adat (például felhasználói vagy fizetési nyilvántartás). Így meghatározhatunk egy sémát a MongoDB számára a Mongoose használatával. Ez sok funkciót megkönnyít számunkra.

Például, ha rendelkezünk egy sémával, biztosak lehetünk abban, hogy az adatok érvényesítését és a szükséges ellenőrzéseket a Mongoose automatikusan elvégzi. A mongúz egy csomó segítő funkciót is ad nekünk, hogy megkönnyítsük az életünket. Most állítsuk be.

Ennek a résznek a teljesítéséhez a következő pontokra kell figyelnünk:

  • A Mongoose NPM csomag már telepítve van az Ön számára. Közvetlenül megteheti require.
  • Csatlakozzon az mongodb://localhost:27017/codedamnURL-hez a mongoose.connectmódszerrel.

Itt található a jelenlegi index.js fájlunk:

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') }) 

Töltsük ki a megfelelő helyőrzőket a megfelelő kóddal:

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') }) })

Csak akkor vegye figyelembe, hogyan indítjuk el a HTTP szerverünket, ha a MongoDB-vel való kapcsolatunk meg van nyitva. Ez rendben van, mert nem akarjuk, hogy a felhasználók elérjék az útvonalunkat, mielőtt az adatbázisunk elkészülne.

Végül az db.collectionitt használt módszert használjuk egy egyszerű rekord beszúrására, de hamarosan jobb módunk lesz az adatbázissal való interakcióra a mongúz modellek segítségével.

4. rész: Mongúz séma beállítása

Most, hogy az utolsó szakaszban gyakorlati tapasztalataink vannak a MongoDB megvalósításával kapcsolatban, rajzoljuk ki az URL-rövidítő sémáját. Itt található a link erre a részre.

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 URL Short URL Clicks

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 :)