My Altschool Blog Project Documentation
Tags: altschool, webdev, javascript, beginners
Published: 2023-01-28 20:37:42

Altschool Africa is an institution that takes a non-traditional approach to learning by teaching courses directly connected with a selected track.

I am a student of Altschool Africa enrolled in their school of engineering with the main focus of Backend engineering using javascript. We were given an assignment to practice the skills we had learned so far, the task was to build a blogging API with the following functionalities:

1. Users should have their first name, last name, email, and password.
2. The user should be able to sign up and sign in to the blog app
3. Use JWT as an authentication strategy and expire the token after 1 hour
4. A blog can be in two states: draft and published
5. Logged-in and not logged in users should be able to get a list of published blogs created
6. Logged-in and not logged in users should be able to get a published blog
7. Logged-in users should be able to create a blog.
8. When a blog is created, it is in a draft state
9. The owner of the blog should be able to update the state of the blog to publish
10. The owner of the blog should be able to edit the blog in a draft or published state
11. The owner of the blog should be able to delete the blog in a draft or published state
12. The owner of the blog should be able to get a list of their blogs.
13. The endpoint should be paginated and filterable by state
14. When a single blog is requested, the API should return the user information(the author) with the blog. The read_count of the blog too should be updated by 1.

After successfully building the API we were told to document the process, which is what I will be doing in this article.

## Requirements

To understand or follow along with this article, you will need:

- Knowledge of JavaScript
- Familiarity with nodejs
- An IDE such as VScode
- MongoDB either local or on the cloud

## Setting up a basic server

I started by creating a basic server using express and nodemon with the following steps:

Navigate to the folder where you want to build the app and run `npm init -y` in the terminal to create the package.json file, the `-y` flag is to accept all default options.

Install the required packages for the basic server by running `npm install express nodemon` in the terminal, express is for creating the server and nodemon will be used to monitor the codebase for any changes.

Create an `index.js` file and input the following code:

``` JavaScript
// Require express module
const express = require('express');

// Create an app using the express module
const app = express();

// Add a simple endpoint
app.get('/', (req, res) => {
    res.send("Hello World")
})

// Listen for requests made to the server
app.listen(4000, (req, res) => {
    console.log(`Server is running on port: 4000`)
})
```

- Run `nodemon index.js` in the terminal, using nodemon automatically restarts the server whenever any changes are made to it. Open your browser and go to `localhost:4000`, you should see the text "Hello World", this means the server is functioning properly.

### MVC Pattern

For this project I made use of the MVC architecture, this is a system design practice that divides the codebase into three parts which contain code for distinct activities. MVC stands for Models, Views, and Controllers, which contain code for the Database, User Interface, and Business Logic respectively.

## Models

Models are used to interact with the database, they help to design the structure and format of database objects by creating schemas. I created one for both the users and the articles.

### User Model

The user model contains all the required fields:

- first name
- last name
- email
- password
- articles

Create a `Models` folder and create a file called `userModel.js` inside it, then input the following code:

``` JavaScript
// Require the mongoose package
const mongoose = require('mongoose');

// Instantiate the schema class from the mongoose package
const Schema = mongoose.Schema;

// Create a user schema with the mongoose schema
const UserSchema = new Schema({
    firstName: {
        type: String,
        required: [true, "Please enter your first name"]
    },
    lastName: {
        type: String,
        required: [true, "Please enter your last name"]
    },
    email: {
        type: String,
        required: [true, "Please enter an email"],
        unique: [true, "This email is already registered, sign in!"],
        lowercase: true,
    },
    password: {
        type: String,
        required: true,
        minLength: [5, "Password must be at least 5 characters"]
    },
    articles: {
        type: Array,
    }
}, { timestamps: true });

// This is a function for logging a user in

// more details in the  userController section
UserSchema.statics.login = async function(email, password) {
    const user = await this.findOne({ email });
    if(!user){
        throw Error('Incorect email, try again or sign up!');
    }

    const auth = await bcrypt.compare(password, user.password);
    if(!auth){
        throw Error('Incorrect password');
    }

    return user
}

// Create a user model with the user schema
const User = mongoose.model('users', UserSchema);

// Export the user Schema
module.exports = User;
```

The texts in the square brackets are error messages which are thrown in case certain validation is not met when filling in the fields. The `timestamps: true` is used to automatically generate timestamps at the points where the object is created or updated. The login function is a static function created in the user model this makes it more secure and less prone to tampering.

### Article Model

For the article model, I created the following fields

- title
- description
- author
- state
- read_count
- read_time
- tags
- body

Create a file called `articleModels` in the Models folder and input the following code:

```JavaScript
// Require the mongoose package
const mongoose = require('mongoose');

// Instantiate the schema class from the mongoose package
const Schema = mongoose.Schema;

// Create an article schema with the mongoose schema
const articleSchema = new Schema({
    title: {
      type: String,
      required: [true, "Please provide the title"],
      unique: [true, "The title name already exists"],
    },
    description: {
      type: String,
    },
    author: {
      type: String,
      required: [true, "Please provide the author"],
    },
    state: {
      type: String,
      enum: ["draft", "published"],
      default: "draft",
    },
    read_count: {
      type: Number,
      default: 0,
    },
    reading_time: {
      type: String,
      required: [true, "Please provide the reading time"],
    },
    tags: {
      type: String,
      required: [true, "Please provide the tags"],
    },
    body: {
      type: String,
      required: [true, "Please provide the body"],
    }
}, {timestamps: true});

// Create an article model with the user schema
const Article = mongoose.model('articles', articleSchema);

// Export the article model
module.exports = Article;
```

When an article is created its state is set to "draft" by default, this can later be updated to "published" by the user. The `read_count` property of the blog is also initially set to zero, other features like timestamps and id are auto-generated.

## Routes

Routes are also another essential part of the application, as they connect the endpoints to the controllers that carry out the blog functions. Adding all the routes to the main page can make things a little overcrowded, hence I created a "routes" folder that would contain the necessary routes.

### User routes

The `userRoutes` file will contain all the routes for all user-related functions such as:

- /signup => For registering a user into the database
- /login => For logging in an existing user
- /logout => For logging out a user

When a certain endpoint is called it redirects the request to a controller which handles the request. The "userRoutes" file will contain the following code:

``` JavaScript
const express = require('express')
const userRouter = express.Router();

userRouter.post('/signup', () => {
  // Function for signing a new user in goes here
  res.send('Signs in a new user')
});

userRouter.post('/login', () => {
  // Function for logging a user in goes here
  res.send('Logs in an existing user')
});

userRouter.post('/logout', () => {
  // Function for logging out a user goes here
  res.send('Signs out a user')
});

module.exports = userRouter;
```

The above code creates a router, `userRouter` using the `express.router()` method the `userRouter` will be connected to all the required endpoints and functions which will then be exported to the `app.js` file where it will be used by the server.

The routes above were tested using postman, the messages in the `res.send` help to identify that the routes are working properly.

> Notice how all the endpoints are POST requests, this is because all user functions send data to the server.

### Article routes

The `articleRoutes` file contains endpoints for all the article-related requests, it contains the following code:

``` Javascript
const express = require('express')
const blogRouter = express.Router();

blogRouter.get('/', () => {
  // Function for getting all available blogs
  res.send('Gets all available articles')
});

blogRouter.get('/user', () => {
  // Function for getting all the articles by a user
  res.send('Gets all articles by a user')
});

blogRouter.get('/:id', () => {
  // Function for getting an article by id
  res.send('Gets an article by id')
});

blogRouter.post('/', () => {
  // Function for creating a new article
  res.send('Creates a new article')
});

blogRouter.delete('/:id', () => {
  // Function for updating an article
  res.send('Updates an article')
});

blogRouter.patch('/:id', () => {
  // Function for deleting an article
  res.send('Deletes an article')
});

module.exports = blogRouter;
```

In this file, each endpoint signifies a single request which will be handled in the controller part. The texts in the brackets are simple placeholder messages to ensure that the endpoints are working properly when tested with postman.

The next step is to connect these routes to the server by updating the code in the index.js file to look like this:

``` JavaScript
...

// Require the routers
const userRouter = require('./routes/userRoutes')
const articleRouter = require('./routes/articleRoutes')

...

// Using the routers
app.use('/api/v1/user', userRouter)
app.use('/api/v1/blog', articleRouter)

...
```

The routers must come after the instantiation of the app, and they will be mapped using specific endpoints, for instance, a request to the `api/v1/user/<something>` would first be transferred to the `userRouter` in the `userRoutes` file.

## Controllers

The controllers contain the code for processing and handling requests, interacting with the database with the models, and sending responses back to the user.

Create a `controllers` folder that will contain the two controllers, the controllers will be connected to the server by linking them with their respective route files.

### User Controller

The `userController` will contain functions that the sign-up, sign-in, and sign-out requests. The file contains the following code:

``` Javascript
const User = require('../models/userModel');
const bcrypt = require('bcrypt')
```

The `User` object is required from the `userModel` and it will be used to interact with the database. The `bcrypt` library is used for hashing and decrypting passwords, it will be used in the login and logout sections.

``` Javascript
// Signup function
const signup = async (req, res) => {

  // Checks if user already exists
  const user = await User.findOne({ email: req.body.email })
  if(user){
      console.log("This user already exists, you have been logged in!")
      // Redirect them to log in page
      return res.redirect('/api/v1/user/login')
  }

  try{
      // Creates new user and hashes the password
      const user = new User(req.body);
      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(user.password, salt);
      await user.save();

      // Returns the user data
      return res.status(201).json({
          status: "success",
          message: "Sign up successful!, you have been automatically logged in",
          data: {
              firstName: user.firstName,
              lastName: user.lastName,
              email: user.email,
              articles: user.articles,
              id: user._id
          }
      })
  }
  // Throws error if any
  catch(err){
      res.status(400).send(err.message)
  }
}
```

When a new user is created via sign up, information like the name, email, and password, is taken from the request body and used to create a new user in the database. Once the user has been created successfully, the details are sent back as a response to the user.

``` Javascript
// Login function
const login = async (req, res) => {

    // Obtains email and password from request body
    const { email, password } = req.body;

    try{
      // Uses login static function
        const user = await User.login(email, password);
        
        // Returns user data
        res.status(201).json({
            status: "success",
            message: "You logged in successfully",
            data: {
                firstName: user.firstName,
                lastName: user.lastName,
                email: user.email,
                articles: user.articles,
                id: user._id
            }
        });
    }
    // Throws error if any
    catch(err){
        res.status(400).send(err.message)
    }
}
```

The login function takes two parameters from the request body: "email" and "password". These details are used to log the data into the database using the static login function in the `userModel` file, but this only works if the user is already registered in the database.

> Note that the user password is not sent as part of the response, this is a basic security measure.

``` Javascript
const logout = (req, res) => {
    // Implement log-out function later
    res.send('Logged out successfully')
}

// Exports all the functions
module.exports = {
    signup,
    login,
    logout
}
```

The `logout` function will be implemented later as it has to do with clearing the JWT tokens, this will be discussed in the JWT section.

All three functions for the user controller are then exported so they can be required in the routes folder where they will be connected to their respective endpoints.

### Blog controller

The blog controller file will contain all the code for handling all requests made to the blog object. The following code will go into this file:

``` Javascript
// Require the article model
const Article = require('../models/articleModel');

// External functions required
const { checkUser } = require('../middleware/jwt');
const { readingTime } = require('../middleware/utils')
```

The `readingTime` function is used to calculate the time it will take to read an article using the average reading speed of 180 words/minute. The `checkUser` function is used to check the current logged-in user by decoding the JWT token and using the data to search for the user in the database.

> JWT tokens will be discussed in the JWT authentication section.

``` Javascript
const getAllArticles = async (req, res) => {
    // Get pagination details from the query
    const limit = parseInt(req.query.limit)
    const offset = parseInt(req.query.skip)

    // Get all published articles from database
    const articles = await Article.find()
        .where({ state: "published"})
        .skip(offset)
        .limit(limit)

    // Error message, if there are no published blogs
    if(!articles.length){
        return res.json({
            status: "failed",
            message: "There are no published articles, check Drafts!"
        })
    }
    // Apply pagination
    const articleCount = articles.length
    const currentPage = Math.ceil(articleCount % offset)
    const totalPages = Math.ceil(articleCount / limit)

    // Return published articles
    res.status(200).json({
        status: "success",
        message: "All published articles",
        total: articleCount,
        page: currentPage,
        pages: totalPages,
        data: articles
    })

}
```

The `getAllArticles` function returns all the existing articles in the database. Pagination is implemented here as all the articles can be very large and it will be bad practice to return everything at once. All the articles will be returned page by page and the parameters for the pagination are passed in the URL, e.g `https://localhost/api/v1/blog/?limit=2&offset=5`. The limit and offset properties are query parameters that will be parsed into the URL.

```Javascript
const getMyArticles = async (req, res) => {
    // Get pagination details
    const limit = parseInt(req.query.limit)
    const offset = (req.query.limit)

    // Check for the current user
    const user = await checkUser(req, res)

    // Get data from database
    const userArticles = Article.find({ author: user.firstName})
        .skip(offset)
        .limit(limit)

    // Throw error message if there are no blogs
    if(!userArticles.length){
        return res.json({
            status: "failed",
            message: "This user does not have any published articles"
        })
    }

    // Apply pagination
    const articleCount = userArticles.length
    const totalPages = Math.ceil(articleCount / limit)
    const currentPage = Math.ceil(articleCount % offset)

    // Return article data
    res.status(200).json({
        status: "success",
        message: `All articles, published by ${user.firstName}`,
        total: articleCount,
        page: currentPage,
        pages: totalPages,
        data: userArticles
    })
}
```

The `getMyArticles` function returns all the articles by a particular user, by checking through the database for articles where the author corresponds with the name of the logged-in user, the data is then paginated and sent to the user.

```Javascript
const getArticle = async (req, res) => {
    // Get article from database with Id
    const article = await Article.findById(req.params.id)
        .where({ state: "published" })

    // Throw error message if article is not found
    if(!article){
        return res.status(404).send("The Article you requested was not found")
    }

    // Increase read count
    article.read_count++
    article.save()

    // Return data
    res.status(200).json({
        status: "success",
        message: `Single article post: "${article.title}"`,
        data: {
            article
        }
    })
}
```

The `getArticle` function gets a single article by searching through the database with the `id` that is passed in the request parameter. It also increases the `read_count` property every time a blog is requested.

```Javascript
const createArticle = async (req, res) => {
    
    try{
        // Get details from the request body
        const { title, description, state, tags, body } = req.body;

        // Check if article with that title exists
        const exists = await Article.findOne({ title })
        if(exists){
            return res.json({
                status: "failed",
                message: "Article with that title already exists"
            })
        }

        // Check for the current user
        const user = await checkUser(req, res)

        if(!user){
            return res.json({
                status: "failed",
                message: "You need to be logged in to create a articlepost"
            })
        }

        // Name of user is set to author of article
        const article = new Article({
            title,
            description,
            author: user.firstName,
            state,
            reading_time: readingTime(body),
            tags: tags,
            body,
        })

        // Save article to list of user articles
        user.articles.push(article.title)
        await user.save()
        await article.save()
    
        // Return article data
        return res.json({
            status: "success",
            message: `${user.firstName} ${user.lastName} created "${article.title}"`,
            data: article
        });
    }
    catch(err){
        res.status(500).send(err.message)
    }
}
```

To create a new article details like the title, description, body, and tags are obtained from the request body and used to create an article with the respective fields. Other fields like the `reading_time`, `author`, and `id` are auto-generated with the `readingTime` function, `checkUser` function, and the mongoose library respectively.

```Javascript
const deleteArticle = async (req, res) => {

    // Get article from database
    const article = await Article.findOne({ __id: req.params.id })
    const user = await checkUser(req, res)
    
    // Check if current user is the author of the article
    if(user.firstName !== article.author){
        return res.status(401).send({
            message: "You are not authorized to delete this article"
        })
    }

    // Remove article from list of user articles
    const userArticles = user.articles
    for(let i = 0; i < userArticles.length; i++){
        if(userArticles[i] == article.title){
            userArticles.splice(i, 1)
        }
    }

    await user.save()
    await Article.findByIdAndDelete(req.params.id)

    // Return success message
    res.json({
        status: "success",
        message: `${article.title} was deleted`,
    })
}
```

The `deleteArticle` function deletes a single article by searching through the database with the given id and removing it. It also removes the article from the array of the user's articles, it returns a success message when all this is completed.

```Javascript
const updateArticle = async (req, res) => {
    // Get details from request body
    const { title, description, state, tags, body } = req.body;

    const user = await checkUser(req, res)
    const article = await Article.findById(req.params.id)

    // check if current user is the author of the article
    if(user.firstName !== article.author){
        return res.send("You are not authorised to update this article")
    }

    // Update article
    const updatedArticle = await Article.findByIdAndUpdate({ _id: req.params.id }, {
        $set: {
            title,
            description,
            state,
            tags,
            body
        }
    }, { new: true })

    // Return updated article
    res.status(200).json({
        status: "success",
        message: `${updatedArticle.title} was updated`,
        data: {
            updatedArticle
        }
    })
}
```

The `updateArticle` updates a single article in the database with the information passed into the request body. This action is only allowed by the owner of the article, so before carrying out the procedure we first check if the name of the current user is the same as the author of the article.

``` Javascript
// Export all the functions
module.exports = {
    getAllArticles,
    getMyArticles,
    getArticle,
    createArticle,
    deleteArticle,
    updateArticle,
}
```

After all the functions are completed, they are exported so they can be connected to their respective endpoints in the routes folder.

After checking that these functions are working properly by connecting them to a database and testing them with postman, we will proceed to implement JWT authentication.

## JWT authentication

As the API is now, users will need to log in every time they want to use the application, but with JSON Web Tokens(JWT) we can save the user information and allow users to access the website by simply checking if they have the token.

For this API I used cookies to store the JWT in the browser, to do this we will use the `jsonwebtoken` package. A file named `jwt` will house the code for the jwt implementation:

``` Javascript
// Required packages
const jwt = require('jsonwebtoken');
const User = require('../models/userModel')
require('dotenv').config();
```

We start by requiring the `userModel` object as this is what the JWT interacts with for signing in and logging in, lastly, we will require the `dotenv` package, which allows us to work with environmental variables

> Notice how the `dotenv` package is not assigned to a variable, it is just declared with the `.config` attachment. This means the file is configured to accept environmental variables, which are called with the `process.env` method. E.g `process.env.JWT_SECRET` calls the `JWT_SECRET` variable from the `.env` file.

``` Javascript
const maxAge = 60 * 60;

// Function for creating a token
const createToken = (id) => {
    return jwt.sign({ id }, process.env.JWT_SECRET, {
        expiresIn: maxAge
    });
}
```

The `createToken` function returns a jwt token which is set to expire in the time set by the `maxage` variable, the `jwt.sign()` function takes in two parameters: the information to be signed `id`, and a secret which we will need when decoding the token.

``` Javascript
// Function for authenticating a route
const requireAuth = (req, res, next) => {
    const token = req.cookies.jwt;

    if(!token){
        console.log("No token")
        return res.json({
            status: "failed",
            message: "You need to be logged in to continue this action"
        })
    }

    jwt.verify(token, process.env.JWT_SECRET, (err, decodedToken) => {
        if(err){
            console.log(err.message)
            return res.redirect('/api/v1/login');
        } else {
            next();
        }
    })   
}
```

Some endpoints will require authentication, hence I created a `requireAuth` function as a middleware which makes an endpoint only accessible to logged-in users. The `requireAuth` function decodes the token which is stored in a cookie in the request object, and then verifies it, if successful the request proceeds to the endpoint, else the user is redirected to the login route.

``` Javascript
// Function for checking the user
const checkUser = async (req, res) => {
    const token = req.cookies.jwt;
    
    if(!token){
        console.log('No token, please login');
        return null
    }

    const decodedToken = jwt.verify(token, process.env.JWT_SECRET)
    
    const user = await User.findById(decodedToken.id)
    return user
}
```

The `checkUser` function decodes the jwt token obtains the decoded id and uses it to check through the database and obtain the current user. This information can then be used to carry out many things, for example, it can be used to automatically get the name of the current user and assign it to the author property whenever the user creates a blog. It can also be used to display the current author.

``` Javascript
module.exports = {
    createToken,
    requireAuth,
    checkUser
};
```

The jwt functions are then exported out of the file so they can be used wherever they are needed.

Implementing the jwt functions in the `userControllers` file:

``` Javascript
const User = require('../models/userModel');
const { createToken } = require('../middleware/jwt');
const bcrypt = require('bcrypt')

const signup = async (req, res) => {

    /**
     * Function to create a new user
     * 
     * If the user already exists, log them in
     * 
     * Creates a token with a maxAge of 1 hour
     * 
     * Saves the token into a cookie called "jwt"
     * 
     * 
     * If any error occurs send the error message to the user
     */
    const user = await User.findOne({ email: req.body.email })
    if(user){
        console.log("This user already exists, you have been logged in!")
        return res.redirect('/api/v1/user/login')
    }

    try{
        const user = new User(req.body);
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(user.password, salt);
        await user.save();

        const token = createToken(user._id);
        res.cookie('jwt', token, { maxAge: 60 * 60 * 1000 });

        return res.status(201).json({
            status: "success",
            message: "Sign up successful!, you have been automatically logged in",
            data: {
                firstName: user.firstName,
                lastName: user.lastName,
                email: user.email,
                articles: user.articles,
                id: user._id
            }
        })
    }
    catch(err){
        res.status(400).send(err.message)
    }
}

const login = async (req, res) => {
    
    /**
     * Function to log in a registered user
     * 
     * Creates a token with a maxAge of 1 hour
     * 
     * Saves the token into a cookie called "jwt"
     * 
     * 
     * If any error occurs send them to the user because they are the user's problems now 
     */
    const { email, password } = req.body;

    try{
        const user = await User.login(email, password);

        const token = createToken(user._id);
        
        res.cookie('jwt', token, { maxAge: 60 * 60 * 1000 });
        res.status(201).json({
            status: "success",
            message: "You logged in successfully",
            data: {
                firstName: user.firstName,
                lastName: user.lastName,
                email: user.email,
                articles: user.articles,
                id: user._id
            }
        });
    }
    catch(err){
        res.status(400).send(err.message)
    }
}
/**
 * Function to log out a user
 * 
 * Sets the jwt cookie to an empty string that expires in 1 millisecond
 */
const logout = (req, res) => {
    res.cookie('jwt', "", { maxAge: 1 });
    res.send('Logged out successfully')
}

module.exports = {
    signup,
    login,
    logout
}
```

The `signup` function now adds a user to the database and redirects them to the `login` route, which has a function that creates a jwt and also saves it to a cookie. For the `logout` function, the jwt is removed by setting the value of the cookie to an empty string that expires instantly, this is because cookies cannot be deleted directly.

Implementing the authentication in the `userRoutes` file:

``` Javascript
// USER ROUTES

const express = require('express')
const userController = require('../controllers/userController');
const { requireAuth } = require('../middleware/jwt')

const userRouter = express.Router();

userRouter.post('/signup', userController.signup);

userRouter.post('/login', userController.login);

userRouter.post('/logout', requireAuth, userController.logout); // Secured

module.exports = userRouter;
```

Here only the logout function is authenticated, as a user will need to be logged in, to be logged out, ironic right?

In the article routes file the following routes will be authenticated:

``` Javascript
const express = require('express')
const blogController = require('../controllers/blogController');
const { requireAuth } = require('../middleware/jwt');

const blogRouter = express.Router();

blogRouter.get('/', blogController.getAllArticles); // Not secured

blogRouter.get('/user', requireAuth, blogController.getMyArticles); // Secured

blogRouter.get('/:id', blogController.getArticle); // Not secured

blogRouter.post('/', requireAuth, blogController.createArticle); // Secured

blogRouter.delete('/:id', requireAuth, blogController.deleteArticle); // Secured

blogRouter.patch('/:id', requireAuth, blogController.updateArticle); // Secured

module.exports = blogRouter;
```

These routes need to be secured as requested by the project instructions, the only exception is the `/user` route which obtains all the articles by the logged-in user. I protected this route because it uses the jwt token to obtain the results, and it would throw an error if the user was not logged in.

## Problems I faced

### Jwt auth

I faced a lot of problems with the jwt authentication as this was my first time using it, I tried following a lot of articles but I had trouble because my project structure was different from the ones in the tutorial, and navigating through the documentation was a tough thing for a beginner.

### Code architecture

I constantly had to change my codebase to match the new features I was trying to implement. For instance, for me to write tests I would need to export the entire server to be able to properly test all the endpoints. So I had to create a new file, `server.js` which would import the app connect to the database, and then listen on the port.

```Javascript
const app = require ("./src/app");

const mongoose = require('mongoose');
require("dotenv").config()

const PORT = process.env.PORT || 4000
const URL = process.env.DB_URL

//Connect to database
mongoose.connect(URL, { useNewUrlParser: true })
.then(() => console.log('Connected to database.....'))
.catch((err) => console.log('An error occured:', err.message))

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})
```

### Conclusion

The major takeaway I had is how there are multiple ways to do the same thing, from analyzing the projects of my peers to the codebase I saw in the various tutorials I watched, even though they were doing the same thing (building a simple blog API) they used varying methods to achieve different things.

The project was an experience to remember, and I am looking forward to building more projects in the future and sharing my journey.

Check out the [GitHub repo](https://github.com/Teejay128/altschool-blog-api) to view the entire source code for the project

> The code in the GitHub repo may differ from the ones in the article, as I am constantly improving the API and working toward applying views in the project.

## Thank you for reading