Built at: 2024-10-16T09:55:25.150Z Skip to content

part4a

Part4a Structure of backend application, introduction to testing

Project structure

├── index.js
├── app.js
├── dist
│ └── ...
├── controllers
│ └── notes.js
├── models
│ └── note.js
├── package-lock.json
├── package.json
├── utils
│ ├── config.js
│ ├── logger.js
│ └── middleware.js

move info or error log together:

utils/logger.js
const info = (...params) => {
console.log(...params)
}
const error = (...params) => {
console.error(...params)
}
module.exports = {
info, error
}
utils/config.js
require('dotenv').config()
const PORT = process.env.PORT
const MONGODB_URI = process.env.MONGODB_URI
module.exports = {
MONGODB_URI,
PORT
}
index.js
const app = require('./app') // the actual Express application
const config = require('./utils/config')
const logger = require('./utils/logger')
app.listen(config.PORT, () => {
logger.info(`Server running on port ${config.PORT}`)
})
controllers/notes.js
const notesRouter = require('express').Router()
const Note = require('../models/note')
notesRouter.get('/', (request, response) => {
Note.find({}).then(notes => {
response.json(notes)
})
})
notesRouter.get('/:id', (request, response, next) => {
Note.findById(request.params.id)
.then(note => {
if (note) {
response.json(note)
} else {
response.status(404).end()
}
})
.catch(error => next(error))
})
notesRouter.post('/', (request, response, next) => {
const body = request.body
const note = new Note({
content: body.content,
important: body.important || false,
})
note.save()
.then(savedNote => {
response.json(savedNote)
})
.catch(error => next(error))
})
notesRouter.delete('/:id', (request, response, next) => {
Note.findByIdAndDelete(request.params.id)
.then(() => {
response.status(204).end()
})
.catch(error => next(error))
})
notesRouter.put('/:id', (request, response, next) => {
const body = request.body
const note = {
content: body.content,
important: body.important,
}
Note.findByIdAndUpdate(request.params.id, note, { new: true })
.then(updatedNote => {
response.json(updatedNote)
})
.catch(error => next(error))
})
module.exports = notesRouter

A router object is an isolated instance of middleware and routes. You can think of it as a “mini-application,” capable only of performing middleware and routing functions. Every Express application has a built-in app router.

app.js
const config = require('./utils/config')
const express = require('express')
const app = express()
const cors = require('cors')
const notesRouter = require('./controllers/notes')
const middleware = require('./utils/middleware')
const logger = require('./utils/logger')
const mongoose = require('mongoose')
mongoose.set('strictQuery', false)
logger.info('connecting to', config.MONGODB_URI)
mongoose.connect(config.MONGODB_URI)
.then(() => {
logger.info('connected to MongoDB')
})
.catch((error) => {
logger.error('error connecting to MongoDB:', error.message)
})
app.use(cors())
app.use(express.static('dist'))
app.use(express.json())
app.use(middleware.requestLogger)
app.use('/api/notes', notesRouter)
app.use(middleware.unknownEndpoint)
app.use(middleware.errorHandler)
module.exports = app
utils/middelware.js
const logger = require('./logger')
const requestLogger = (request, response, next) => {
logger.info('Method:', request.method)
logger.info('Path: ', request.path)
logger.info('Body: ', request.body)
logger.info('---')
next()
}
const unknownEndpoint = (request, response) => {
response.status(404).send({ error: 'unknown endpoint' })
}
const errorHandler = (error, request, response, next) => {
logger.error(error.message)
if (error.name === 'CastError') {
return response.status(400).send({ error: 'malformatted id' })
} else if (error.name === 'ValidationError') {
return response.status(400).json({ error: error.message })
}
next(error)
}
module.exports = {
requestLogger,
unknownEndpoint,
errorHandler
}
models/note.js
const mongoose = require('mongoose')
const noteSchema = new mongoose.Schema({
content: {
type: String,
required: true,
minlength: 5
},
important: Boolean,
})
noteSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
}
})
module.exports = mongoose.model('Note', noteSchema)

check the structure again:

├── index.js
├── app.js
├── dist
│ └── ...
├── controllers
│ └── notes.js
├── models
│ └── note.js
├── package-lock.json
├── package.json
├── utils
│ ├── config.js
│ ├── logger.js
│ └── middleware.js

Exercises 4.1.-4.2.

4.1 Blog List, step 1

Turn the application(codes below) into a functioning npm project.

const express = require('express')
const app = express()
const cors = require('cors')
const mongoose = require('mongoose')
const blogSchema = new mongoose.Schema({
title: String,
author: String,
url: String,
likes: Number
})
const Blog = mongoose.model('Blog', blogSchema)
const mongoUrl = 'mongodb://localhost/bloglist'
mongoose.connect(mongoUrl)
app.use(cors())
app.use(express.json())
app.get('/api/blogs', (request, response) => {
Blog
.find({})
.then(blogs => {
response.json(blogs)
})
})
app.post('/api/blogs', (request, response) => {
const blog = new Blog(request.body)
blog
.save()
.then(result => {
response.status(201).json(result)
})
})
const PORT = 3003
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})

My solutions

My solutions:
(Please complete your own solutions before click here.)

Q: How to turn a .js application into a npm package?
A:

Turning a JavaScript application into an npm package involves several steps. Here's a general guide to help you through the process:
1. Initialize a Node.js Project:
If you haven't already, create a new directory for your project and initialize it with npm by running:
bash
npm init -y
This command creates a package.json file with default values.
2. Develop Your Application:
Build your application as you normally would. Make sure it works correctly.
3. Prepare Your Package:
- Ensure your code is well-structured and modular.
- Write a README.md file that explains how to install and use your package.
- Include a LICENSE file if you want to specify how your package can be used.
- Write tests to ensure your package works as expected.
4. Update package.json:
Modify your package.json file to include:
- name: A unique name for your package.
- version: A version number following semantic versioning (e.g., 1.0.0).
- main: The entry point to your module (e.g., index.js).
- scripts: Commands for testing, building, etc.
- dependencies: Any packages your package needs to run.
- devDependencies: Any packages needed for development.
- files: An array of files that should be included when your package is published.
- bin: If your package includes a command-line tool, specify the binary files here.
- keywords: Keywords that help users find your package.
5. Test Locally:
Before publishing, test your package locally:
bash
npm pack
This will create a tarball of your package. You can then install it locally to test:
bash
npm install ./package-name-1.0.0.tgz
6. Publish to npm:
To publish your package, you need to log in to npm:
bash
npm login
After logging in, you can publish your package:
bash
npm publish
Note that you can also publish a package privately if you have an npm account with private packages enabled.
7. Versioning:
Always remember to update the version number in your package.json file before each new release.
8. Documentation:
Provide comprehensive documentation for your package. This can be in the README file or a separate documentation site.
9. Maintenance:
After publishing, you'll need to maintain your package by fixing bugs, adding features, and updating dependencies.
Remember, once you publish a package, you cannot change its name or version numbers for that same package. If you need to make changes, you'll need to publish a new version with an updated version number.

Also ref: https://philna.sh/blog/2019/01/10/how-to-start-a-node-js-project/

tierney cyren
@bitandbang
How to start any new Node.js project:
$ npx license mit > LICENSE
$ npx gitignore node
$ npx covgen YOUR_EMAIL_ADDRESS
$ npm init -y
You're ready to start coding.

issue on windows when run npx license mit: https://stackoverflow.com/questions/76188312/npx-license-mit-license-throwing-an-error-license-is-not-recognized-as-an-i

I can also confirm that npx license does not work out-of-the-box on Windows, but if you first do npm i -g license and then follow up with `npx license MIT` then you get the results you expect, however that seems to defeat the purpose of npx doesn't it? – hsbatra Commented Jan 19 at 23:59

Actual action: ignore the third command(the covgen) as it looks too verbose for this exercise.

Add scripts and dependencies and eslint.config.mjs from exercises in part3.

4.2 Blog List, step 2

Refactor the application into separate modules as shown earlier in this part of the course material.

TODO