Express + Typescript + Mongo – Part 2 – Connecting to Mongo
In the previous article, we got our Express project setup and our endpoints defined. In this article, we’ll connect it to an existing Mongo server.
The first thing you’ll need to do is install the new dependencies that we’ll need:
npm install --save mongodb npm install --save-dev @types/mongodb
Next, we need to enable some new features of TypeScript by creating a “tsconfig.json” file in the root of the project with the following contents:
{ "compilerOptions": { "target": "esnext" } }
This will allow us to use async / await and Promises. We could have used “es2015” instead of “esnext”, but “esnext” allows us to take advantage of the latest proposed features should we choose to do so.
Now, let’s create a new file in src, called ‘mongo.helper.ts’ with the following contents:
import * as mongo from 'mongodb'; export class MongoHelper { public static client: mongo.MongoClient; constructor() { } public static connect(url: string): Promise<any> { return new Promise<any>((resolve, reject) => { mongo.MongoClient.connect(url, {useNewUrlParser: true}, (err, client: mongo.MongoClient) => { if (err) { reject(err); } else { MongoHelper.client = client; resolve(client); } }); }); } public disconnect(): void { MongoHelper.client.close(); } }
This is the class we’ll use to manage our connection to Mongo.
Next, open up main.ts and change the “server.on” block to the following:
server.on('listening', async () => { console.info(`Listening on port ${PORT}`); try { await MongoHelper.connect(`mongodb://10.0.0.129:27017/todo`); console.info(`Connected to Mongo!`); } catch (err) { console.error(`Unable to connect to Mongo!`, err); } });
Now, instead of having the server just print out a message, we’ll also try connecting to our instance of Mongo.
Let’s organize our source code a bit more by moving the API methods out into a separate file. First, we’ll change our app.ts file so the end of it looks like this:
app.use(requestLoggerMiddleware); app.use(todoRoutes); export { app };
You will need to add the import statement at the top of the file:
import { todoRoutes } from './todo.controller';
Now, let’s define our todoRoutes export by creating a “todo.controller.ts” file in the src folder with the following contents:
import * as express from 'express'; import * as mongodb from 'mongodb'; import { MongoHelper } from './mongo.helper'; const todoRoutes = express.Router(); const getCollection = () => { return MongoHelper.client.db('todo').collection('todos'); } todoRoutes.get('/todo', (req: express.Request, resp: express.Response, next: express.NextFunction) => { const collection: any = getCollection(); const result = collection.find({}).toArray((err, items) => { if (err) { resp.status(500); resp.end(); console.error('Caught error', err); } else { items = items.map((item) => { return { id: item._id, description: item.description}}); resp.json(items); } }); }); todoRoutes.post('/todo', (req: express.Request, resp: express.Response, next: express.NextFunction) => { const description = req.body['description']; const collection: any = getCollection(); collection.insert({description: description}); resp.end(); }); todoRoutes.put('/todo/:id', (req: express.Request, resp: express.Response, next: express.NextFunction) => { const id = req.params['id']; const description = req.body['description']; const collection: any = getCollection(); collection.findOneAndUpdate({"_id": new mongodb.ObjectId(id)}, {$set: {"description": description}}); resp.end(); }); todoRoutes.delete('/todo/:id', (req: express.Request, resp: express.Response, next: express.NextFunction) => { const id = req.params['id']; const collection: any = getCollection(); collection.remove({"_id": new mongodb.ObjectId(id)}); resp.end(); }); export { todoRoutes }
So, here, we’re first creating a utility method that will fetch our “todo” data collection within Mongo. After that, we define our endpoints as we did before except now we’re interacting with our Mongo collection.
The first endpoint, ‘GET /todo’, will find and return all records in our collection and remap the “_id” field to “id”. The reason we do this is because the old AngularJS client expects the field to be called “id”.
The second endpoint, ‘POST /todo’, simply adds a new record to Mongo.
The third endpoint, ‘PUT /todo/:id’, will update an existing record in Mongo. To do this, we use the findOneAndUpdate method, which takes a couple of parameters in this example. The first is a hash containing our filter (i.e. which object are we going to update). Our filter is pretty simple since we just want to update one record. Instead of passing in the “id” directly from the request, we have to convert it into an ObjectId, which is what Mongo uses for its _id fields. The second parameter to the findOneAndUpdate method is a hash containing our update operation. Again, we have a very simple one. In our case, we just want to update a single field so we use the “$set” operation and pass in the field we want to update as well as the value we’re updating it to.
The final endpoint, ‘DELETE /todo/:id’, removes a record. Just like the findOneAndUpdate, the first parameter is our filter for what we’re wanting to remove from our Mongo collection.
Once you have all of that in, start up your server and client application then try it out. Everything should be working now.
If you are having issues, are confused, have tips, or any suggestions, please leave them in the comment section below!
Source code is available here: