My project code it’s actually working well. If you test manually, it can do what the challenge tells to do. From POST a URL and GET a ShortURL.
But when I tried to submit the solution, it always failed.
Here’s the code (or you can go to my project’s link below):
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const app = express();
const bodyParser = require('body-parser');
const validUrl = require('valid-url');
const mongoose = require('mongoose');
// Basic Configuration
const port = process.env.PORT || 3000;
app.use(cors());
app.use('/public', express.static(`${process.cwd()}/public`));
app.get('/', function(req, res) {
res.sendFile(process.cwd() + '/views/index.html');
});
// Your first API endpoint
app.get('/api/hello', function(req, res) {
res.json({ greeting: 'hello API' });
});
// Get our cluster URI
const MONGO_URI = process.env.MONGO_URI;
// Connect our mongoose
mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
// Create a URL schema
let shortUrlSchema = new mongoose.Schema({
original_url: {
type: String,
required: true
},
short_url: {
type: Number,
required: true
}
})
// Create shortUrl Model from shorUrl schema
const ShortUrl = mongoose.model('ShortUrl', shortUrlSchema);
// Create counter schema; to handle increment sequence value
const counterSchema = new mongoose.Schema({
id: {
type: String
},
sequence_value: {
type: Number
}
})
// Create counter model
const Counter = mongoose.model('Counter', counterSchema);
// Function to update our counter record
const updateCounter = () => {
return new Promise((resolve, reject) => {
// Find by id, increment sequence_value field by 1
Counter.findOneAndUpdate(
{id:"counterid"},
{"$inc": {"sequence_value": 1}},
{new:true}, async (err, data) => {
// Declare variable to store increment value from record
let sequenceId;
// IF there's no document in counter record yet, set and save it. Which sequence_value to 1
// ELSE just get sequence_value field, and save to sequenceId variable above
if (data == null) {
const firstRecord = new Counter({id: "counterid", sequence_value: 1});
await firstRecord.save();
sequenceId = 1;
} else {
sequenceId = data.sequence_value;
}
// Resolve by return sequenceId variable
resolve(sequenceId);
}
)
})
}
// Validate POST URL using RegEx
const isValidUrl = (url) => {
var urlPattern = new RegExp("((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)");
/*
The URL must start with either http or https and
then followed by :// and
then it must contain www. and
then followed by subdomain of length (2, 256) and
last part contains top level domain like .com, .org etc.
*/
return !!urlPattern.test(url);
}
// Parser body to post
app.use(bodyParser.urlencoded({extended: false}));
// Get shorturl key method to redirect to it's url
app.get('/api/shorturl/:shorturl', async(req, res) => {
// Get key from /shorturl/<shorturl>
const shortid = req.params.shorturl;
// Find record by shorturl key
const record = await ShortUrl.findOne({ short_url: shortid })
// If doesn't exist yet, send Not Found
if (!record) return res.sendStatus(404);
// Go to original url
res.redirect(record.original_url);
})
// Post url method
app.post('/api/shorturl', async(req, res) => {
// Get post url
const url = req.body.url;
let urlRecordData;
// First, validate URL format
// IF not, send Invalid URL. Otherwise, saved it as new record
if (!isValidUrl(url)) {
res.send({error: 'Invalid URL'});
} else {
// First, check if POST URL already exist in record
const checkRecord = await ShortUrl.findOne({original_url: url});
// If NOT exist yet, increment sequence_value field in counter record to use it as short url
if(!checkRecord) {
await updateCounter()
.then(async (res) => {
// Create new record with res which sequenceId for shortUrl
const record = new ShortUrl({original_url: url, short_url: res});
await record.save();
})
}
// If URL already in record, respond the data
// OR also after create it
urlRecordData = await ShortUrl.findOne({original_url: url}, {original_url: 1, short_url: 1, _id: 0});
res.send(urlRecordData);
}
})
app.listen(port, function() {
console.log(`Listening on port ${port}`);
});
exports.shortUrlModel = ShortUrl;
exports.counterModel = Counter;
exports.updateCounter = updateCounter;
And this is the message error that shows in replit console whenever I submit the solution link. This message error doesn’t show when I RUN the project manually:
/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:4780
const castError = new CastError();
^
CastError: Cast to Number failed for value "undefined" (type string) at path "short_url" for model "ShortUrl"
at model.Query.exec (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:4780:21)
at model.Query.Query.then (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:4879:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
messageFormat: undefined,
stringValue: '"undefined"',
kind: 'Number',
value: 'undefined',
path: 'short_url',
reason: AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(!isNaN(val))
at castNumber (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/cast/number.js:27:10)
at SchemaNumber.cast (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/schema/number.js:376:12)
at SchemaNumber.SchemaType.applySetters (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/schematype.js:1188:12)
at SchemaNumber.SchemaType._castForQuery (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/schematype.js:1622:15)
at SchemaNumber.castForQuery (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/schema/number.js:430:14)
at SchemaNumber.SchemaType.castForQueryWrapper (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/schematype.js:1589:20)
at cast (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/cast.js:344:32)
at model.Query.Query.cast (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:5202:12)
at model.Query.Query._castConditions (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:2176:10)
at model.Query.<anonymous> (/home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/query.js:2475:8) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '=='
},
valueType: 'string'
}
[nodemon] app crashed - waiting for file changes before starting
I get stucked. Cause I found nothing wrong in my code. And I hope you guys can give the solution. Thank You.
Here’s my project link(s)
Link to the challenge:
Your browser information:
User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Challenge: Back End Development and APIs Projects - URL Shortener Microservice