In the last tutorial, we were able to setup our client using React. In this part, we gonna be focusing on the backend/server building the API’s for our Todo App.
Setting up Mongodb
Let’s install
lerna add 'mongoose' --scope '@fullstackopenjs/server'
lerna add -D '@types/mongoose' --scope '@fullstackopenjs/server'
This will install the dependencies
In order to use mongodb, we first need to setup our environment variables for db connection:
DB_NAME=fullstackopenjs_db
DB_HOST=localhost
DB_PORT=27017
DB_USER=fullstackopenjs_user
DB_USER_PW=mypass123
We will import this in our env.ts file and export the mongodbConfig:
/**
* Set mongdb details
*/
const mongodbConfig = {
name: process.env.DB_NAME,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_USER_PW,
};
export {mongodbConfig};
Let’s now create our mongodb connection, create a new directory db, inside it create index.ts:
db/index.ts
import mongoose from 'mongoose';
import { mongodbConfig } from '../env';
import logger from '../lib';
// Build the Mongodb connection string
// const dbURI = `mongodb://${mongodbConfig.user}:${mongodbConfig.password}@${mongodbConfig.host}:${mongodbConfig.port}/${mongodbConfig.name}`;
const localdbURI = `mongodb://${mongodbConfig.host}:${mongodbConfig.port}/${mongodbConfig.name}`;
const dbOptions = {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false,
autoIndex: true,
poolSize: 10, // Maintain up to 10 socket connections
connectTimeoutMS: 10000, // Give up initial connection after 10 seconds
socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
};
logger.debug(localdbURI);
// create the db connection object
mongoose
.connect(localdbURI, dbOptions)
.then(() => {
logger.info('Mongodb connected successfully');
})
.catch((e) => {
logger.info('Mongodb Connection Error');
logger.error(e);
});
// Connection events
mongoose.connection.on('connected', () => {
logger.info(`Mongoose default connection open to ${localdbURI}`);
});
// If the connection throws an error
mongoose.connection.on('error', (err) => {
logger.error(`Mongoose default connection error${err}`);
});
// When the connection is disconnected
mongoose.connection.on('disconnected', () => {
logger.info('Mongoose default connection disconnected');
});
// If the Node process ends, close the mongoose connection
process.on('SIGINT', () => {
mongoose.connection.close(() => {
logger.info('Mongoose default connection disconnected through app termination');
process.exit(0);
});
});
We are using mongoose to connect to our local mongodb also using logger to log for errors, if anything goes wrong.
Let’s import our Db config in app.ts:
import './db';
This will check for mongodb connection automatically, when server is started/restarted.
Creating Todo API’s
We need to create a model using mongoose to define the schema of our todo document.
create a new folder inside src named “model”. Inside model create a todo.ts with the following:
import mongoose from 'mongoose';
interface ITodo {
title: string;
description: string;
status: boolean;
}
interface TodoModelInterface extends mongoose.Model<TodoDoc> {
build(attr: ITodo): TodoDoc;
}
interface TodoDoc extends mongoose.Document {
title: string;
description: string;
status: boolean;
}
const todoSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
status: {
type: Boolean,
required: true,
},
});
// builder to create a new instance of Todo
todoSchema.statics.build = (attr: ITodo) => {
return new Todo(attr);
};
const Todo = mongoose.model<any, TodoModelInterface>('Todo', todoSchema);
export { Todo };
Let’s now create the controllers for our APIs. Inside src directory, create controllers,
Inside controllers, create todos/index.ts:
import { Request, Response } from 'express';
import { Todo } from '../../model/todo';
// get all todos
const getTodos = async (req: Request, res: Response): Promise<void> => {
try {
const todos = await Todo.find({});
res.status(200).json({ todos });
} catch (err) {
throw err;
}
};
// get single todo
const getSingleTodo = async (req: Request, res: Response): Promise<void> => {
try {
const id = req.params.id;
const singleTodo = await Todo.find({ _id: id });
res.status(200).json({ singleTodo });
} catch (err) {
throw err;
}
};
// add todo
const addTodo = async (req: Request, res: Response): Promise<void> => {
try {
const { title, description, status } = req.body;
const todo = Todo.build({ title, description, status });
const newTodo = await todo.save();
res.status(201).json({ message: 'Todo Added', todo: newTodo });
} catch (err) {
throw err;
}
};
// update todo
const updateTodo = async (req: Request, res: Response): Promise<void> => {
try {
const id = req.params.id;
const body = req.body;
const updatedTodo = await Todo.findByIdAndUpdate({ _id: id }, body);
const allTodos = await Todo.find();
res.status(200).json({ message: 'Todo updated', todo: updatedTodo, todos: allTodos });
} catch (err) {
throw err;
}
};
//delete todo
const deleteTodo = async (req: Request, res: Response): Promise<void> => {
try {
const id = req.params.id;
const deletedTodo = await Todo.findByIdAndRemove({ _id: id });
const allTodos = await Todo.find();
res.status(200).json({ message: 'Todo deleted', todo: deletedTodo, todos: allTodos });
} catch (err) {
throw err;
}
};
export { getTodos, getSingleTodo, addTodo, updateTodo, deleteTodo };
Now we are ready to consume our Controllers using routes, Let’s define our routes inside src directory src/routes/todo.ts.
import { Router } from 'express';
import { addTodo, deleteTodo, getSingleTodo, getTodos, updateTodo } from '../controllers/todos';
const router: Router = Router();
router.get('/api/v1/todos', getTodos);
router.get('/api/v1/todos/:id', getSingleTodo);
router.post('/api/v1/todos', addTodo);
router.put('/api/v1/edit-todo/:id', updateTodo);
router.delete('/api/v1/delete-todo/:id', deleteTodo);
export default router;
Great we have now our route’s setup, let’s consume the routes in our app.ts:
import todoRouter from './routes/todo';
.
.
.
/**
* use routes
*/
this.app.use(todoRouter);
This ensures that whenever we hit http://localhost:3001/api/v1/todos using GET, we will get all todos and the rest of the apis.
Great ! we setup our API’s for Todo, that’s it for now 🙂