# Next.js Backend

I started making an app called OpenJira, which is kinda like a clone of [Jira Software](https://www.atlassian.com/es/software/jira). If you don't know about it, it is an app for following tasks and helps when working with a team and stuff.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672177939486/c6cc2fa5-a612-47db-b455-c24b38e05373.png align="center")

  
You can drag and drop entries between different columns depending on the status of the task. It's still in its early stages but you get the point.

I am not very interested in Jira but the purpose of the app was to learn more about setting up endpoints with Next.  
In the project, I am using the react context API to manage the main state of the application. And using my backend(MongoDB) to hydrate the state with entries.

## Backend with Next

Developing your backend with next takes a lot less code than if you wanted to make it with Node. Plus a lot of stuff is very similar so if you learned how to make a backend with node, transiting to Next shouldn't be too complicated. Let's see how to make a simple backend! (my method anyway).

## Making a Seed

Create a folder named database or db.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672178292062/617b6003-40d8-479d-9af9-514e19c9fe92.png align="center")

In db.ts you will have the `connect()` and `disconnect()` functions, these do what you expect them to do and will be called every time we make HTTP requests.

```javascript
import mongoose from 'mongoose';
mongoose.set("strictQuery", false);

/** 
 * 0 = disconected
 * 1 = connected
 * 2 = connecting
 * 3 = disconnecting
 */

const mongoConnection = {
    isConnected: 0
}

export const connect = async() => {
    if (mongoConnection.isConnected === 1){
        console.log('Already connected');
        return;
    }

    if (mongoose.connections.length > 0){
        mongoConnection.isConnected = mongoose.connections[0].readyState;

        if (mongoConnection.isConnected === 1){
            console.log('Using prev connection');
        }

        await mongoose.disconnect();
    }

    await mongoose.connect(process.env.MONGO_URL || '');
    mongoConnection.isConnected = 1;
    console.log('Connected to MongoDB', process.env.MONGO_URL);

}

export const disconnect = async() => {

    if (process.env.NODE_ENV === 'development') return;

    if (mongoConnection.isConnected !== 0) return;
    await mongoose.disconnect();
    console.log('Disconnected to MongoDB');
}
```

The `mongoose.connect()` the function can take a URL as an argument and depending on what happens will return a number. So basically what we are doing is determining what to do and what to output in the console whenever we connect to the database.

# Models

My favorite part about backend development. Here we specify the Schemas of our models in the database or the structure of the data that we want to store.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672178758764/a84f58cc-8d83-4467-80f0-fa6e67a95350.png align="center")

```javascript
import mongoose, {Model, Schema} from "mongoose"; 
import { Entry } from "../interfaces";

export interface IEntry extends Entry{
    //Extra properties if wanted
}

const entrySchema = new Schema({
    description: {
        type: String, 
        required: true
    },
    createdAt: {
        type: Number
    },
    status: {
        type: String,
        enum: {
            values: ['pending', 'in-progress', 'finished'],
            message: '{VALUE} not a valid state'
        },
        default: 'pending'
    }
});

const EntryModel: Model<IEntry> = mongoose.models.Entry || mongoose.model('Entry', entrySchema);

export default EntryModel;
```

# The nitty-gritty

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672178905661/aeac3554-4bd4-40c2-8317-244eb0e87367.png align="center")

Inside the API directory, we define the paths to our endpoints the **same** way we define the paths to our pages (it's brilliant). In the entries directory. I have defined 2 paths, the `index` (which is just the default one) and the `[id]` path.

Outside of it, there is the `bad-request` an endpoint created just so that if there is an **error** or **bad request**, it will redirect the request to that endpoint.

### Index

In this path, I defined the `getEntries()` and `createEntry()` functions, they are the most basic ones and don't require special information.

```javascript
import type { NextApiRequest, NextApiResponse } from 'next'
import { db } from '../../../database';
import { Entry, IEntry } from '../../../models';

type Data =
    | {message: string }
    | IEntry[]
    | IEntry

export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {

    switch (req.method) {
        case 'GET':
            return getEntries( res );
        
        case 'POST':
            return createEntry(req, res);

        default:
            res.status(400).json({ message: 'Endpoint does not exist' })
    }

    
}

const getEntries = async ( res: NextApiResponse<Data> ) => {

    await db.connect();
    const entries = await Entry.find().sort({createdAt: 'ascending'})
    await db.disconnect();

    res.status(200).json(entries);
}

const createEntry = async ( req:NextApiRequest , res: NextApiResponse<Data> ) => {
    const { description = ''} = req.body;
    const newEntry = new Entry({
        description,
        createdAt: Date.now(),
    });
    
    try {
        await db.connect();
        await newEntry.save();
        await db.disconnect();
        
        return res.status(200).json(newEntry);
        
    } catch (error) {

        await db.disconnect();
        console.log(error);
        return res.status(500).json({
            message: 'Something went wrong'
        })
    }
}
```

If you are working with TypeScript, you should define a `type` at the beginning of the file that defines the type of data that these functions will output. We also have a `handler()` function which depending on the request method received will return one of the 2 functions.

### \[id\]

I love this one, whenever you want to make a function that needs a certain id or something that you want to send on the URL path, you can make it in here.

Here's an example of a function that gets a certain entry by the id passed in with the url.

```javascript
const getEntry = async (req: NextApiRequest, res: NextApiResponse<Data>) => {
    const { id } = req.query;
    await db.connect();
    
    const entry = await Entry.findById(id);

    if (!entry) {
        res.status(400).json({ message: 'Entry non-existent with id: ' + id });
    }

    try {
        await db.disconnect();
        res.status(200).json(entry!);
        
    } catch (error: any) {
        console.log(error);
        await db.disconnect()
        res.status(400).json({message: error.errors.status.message})
    }  

}
```

# Finishing up...

That's a lot of information and I hope you found it useful. That's all I can say for now. In the future, I might post about SSR, since I need to generate pages from the server for every entry on the database. See ya later!!😁😁
