Welcome folks today in this blog post we will be building a meteor.js react crud rest api in mongodb
using jsx in browser. All the full source code of the application is shown below.
Get Started
In order to get started you need to initialize a new meteor.js project on the command line using the below commands as shown below
npm i -g meteor
This is will install meteor inside your system globally. Make sure you have node.js installed before you install it using the above command.
Making the Meteor Project
meteor create ##nameofproject##
Here you need to replace the name of the project in the above command. It will look something like this
meteor create sampleapp
By default if you execute the above command it will use the react.js as the default frontend. But you can even use different frontends such as svelte, vue etc.
You can see the series of frontends by using the below command
meteor create -h
Now after this you will get the Minimal React.js JSX Meteor Full Stack App With MongoDB Database in Memory. If you move to the project folder as shown below
cd sampleapp
Run the meteor app
Now to execute the app now you need to run the meteor app using the below command as shown below
meteor
As you can see we have started the meteor app at port 3000
Meteor App Structure
Now if we see the meteor app structure it will look something as shown below
Now we will be writing first of all App.jsx
file and copy paste the below code
imports/ui/App.jsx
1 2 3 4 5 6 7 8 9 |
import React from 'react'; import { Info } from './Info.jsx'; export const App = () => ( <div> <h1>Welcome to Meteor!</h1> <Info/> </div> ); |
As you can see we are including the Info.jsx
component. Now you need to create a new file info.jsx
inside the imports/ui/info.jsx
file and copy paste the following code
Connection to MongoDB API in Browser
Now we will be connecting to MongoDB in the browser using the API. So inside your meteor.js project we need to copy paste the below code in imports/api/links.js
imports/api/links.js
1 2 3 |
import { Mongo } from 'meteor/mongo'; export const LinksCollection = new Mongo.Collection('links'); |
As you can see we are first of all importing Mongo client from meteor/mongo
library. And then we are exporting the MongoDB Collection which is links and storing it in LinksCollection variable.
Making the Info Component
Now we will be writing the code inside the info component which we have included it inside the App.jsx
file and copy paste the below code
imports/ui/info.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { Meteor } from 'meteor/meteor'; import React, { useState } from 'react'; export const Info = () => { const [formTarget, setFormTarget] = useState(null); const [error, setError] = useState(null); return ( <div> <h2>Learn Meteor!</h2> </div> ); }; } |
As you can see we are importing Meteor Library and also we are using the useState Hook of react to declare some state variables for the application. Here the first state variable will be the formTarget. Then we have error state variable for setting the error inside the variable.
Rendering All Data from MongoDB Table
Now we will look how we will render all the table inside the MongoDB Table. The code for this as shown below
1 2 3 4 5 6 |
import { useTracker } from 'meteor/react-meteor-data'; import { LinksCollection } from '../api/links'; const links = useTracker(() => { return LinksCollection.find().fetch(); }); |
As you can see we are importing the links from mongodb api
that we have defined inside the links.js
And also we are using the useTracker hook we are importing the react-meteor-data
And then we are defining a function where we are returning all the data inside the LinksCollection Table. Here we are using the find() method and then fetch() method to convert to Array of Objects.
Looping the Records and Displaying it in JSX
And now guys we will see how to loop through all the data inside the mongodb collection using the map method in jsx. This code is shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
return ( <div> <h2>Learn Meteor!</h2> <ul>{links.map( link => <li key={link._id}> <a href={link.url} target="_blank">{link.title}</a> <button onClick={() => setFormTarget({ type: 'update', doc: link })}>Update</button> <button onClick={() => remove(link._id)}>Delete</button> </li> )}</ul> {renderLinkForm()} {renderError()} <button onClick={() => setFormTarget({ type: 'insert' })}>Create new</button> </div> ); |
As you can see first of all we are using the map method for looping through all the records inside the links array. For each record or link we are first of all rendering the href link and also rendering the title of the link also as the label of the hyperlink. And then we have two buttons which are used for update and delete. And then lastly we are displaying the create link button. Also guys you can see we are calling two more methods here which are renderLinkForm() and renderError() methods which are used for displaying the form and also displaying any error which has taken place.
As you can see when we click the update and create buttons. We are directly modifying the state hook variable of formTarget passing the object which contain a single property called as type which can be either insert or update.
As you can see in the above figure we have this interface. Now we need to define the above two methods to render out the forms depending upon which button was clicked. Whether the create button is clicked then we will show the create form and if update button is clicked then update form will be shown.
Now we will define the renderLinkForm() Method in jsx as shown below
1 2 3 4 5 |
const renderLinkForm = () => { return formTarget ? (<LinkForm onSubmitted={onSubmitted} onError={onError} doc={formTarget.doc} type={formTarget.type} />) : null }; |
As you can see inside this arrow function we are checking whether the state hook variable formTarget is null or not. If it is not null in that case then we will know that user has pressed either create or update button. For that we need to define another component which is LinkForm passing some information such as onSubmitted function and onError for error. And also we are passing the actual document which is clicked. And also checking which button is clicked update or create in the type parameter.
Making the LinkForm Component
Now we will be making the linkForm Component inside the same file info.jsx where we will be having a simple html5 form for either creating a new record or updating a existing record depending upon which button is clicked by the user
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const LinkForm = ({ type, doc, onSubmitted, onError }) => { const [title, setTitle] = useState(doc?.title ?? ''); const [url, setUrl] = useState(doc?.url ?? ''); return ( <form onSubmit={onSubmit}> <label> <span>Title</span> <input type="text" value={title} onChange={e => setTitle(e.target.value)} /> </label> <label> <span>URL</span> <input type="text" value={url} onChange={e => setUrl(e.target.value)} /> </label> <input type="submit" value="Submit" /> </form> ) } |
As you can see inside this functional component we are receiving some props such as type parameter and then we receiving the actual doc which was clicked and then we are receiving form submitted and error callback function props.
Inside this functional component we are first of all defining two useState hook variables which are the title and url of the app. The initial values will be the same of doc which we received.
And then we are returning a simple html5 form which contains two input fields which are for title of the link and the actual href link. Then we have the button to submit the form. We have also attached the onChange event handler to both the input fields such that when we change the value of input field it will automatically change the value of field. Also we have defined the onSubmit event handler which will be called when we submit the form. Now we will be define the onSubmit custom function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const onSubmit = e => { e.preventDefault() if (type === 'insert') { Meteor.call('links.create', { title, url }, (err, res) => { if (err) { return onError(err); } onSubmitted(res); }) } if (type === 'update') { Meteor.call('links.update', { _id: doc._id, title, url }, (err, res) => { if (err) { return onError(err); } onSubmitted(res); }) } }; |
As you can see inside this event handler function of onSubmit we are first of all we are preventing the auto submission of the form by using e.preventDefault() method. Then we are checking the type parameter which is passed inside the component. If it’s equal to insert then we are calling a method to create a new record inside the mongodb collection and passing the title and url as an second argument in an object. And in the callback function we received error and response. If error takes place then we display error and return. If no error takes place then we are calling the onSubmitted function to clear out or reset all form fields after submitting the form. And also if type parameter is update then we are modifying the existing record taking the id and the object as arguments.
1 2 3 4 |
const onSubmitted = () => { setFormTarget(null); setError(null); }; |
As you can see in the above function we are making both the state hook variables to null and clearing or resetting the input fields after we submit the form.
If any error takes place inside this app. Guys we will be defining a function which will print the error in the console as shown below
1 |
const onError = (err) => setError(err); |
Now for displaying that error inside the browser we can make a separate function as shown below
1 2 3 4 5 |
const renderError = () => { return error ? (<div>{error.message}</div>) : null } |
Lastly when we click the delete button we need to define the remove()
method which will be getting the id of the record which is clicked that we define inside the onClick event handler. The code is shown below
1 2 3 |
const remove = _id => { Meteor.call('links.delete', { _id }, (err) => setError(err || null)) } |
As you can see we are calling a meteor function which is used to delete the record from the mongodb using the id that we got of the link.
Full info.jsx Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
import { Meteor } from 'meteor/meteor'; import React, { useState } from 'react'; import { useTracker } from 'meteor/react-meteor-data'; import { LinksCollection } from '../api/links'; export const Info = () => { const [formTarget, setFormTarget] = useState(null); const [error, setError] = useState(null); const links = useTracker(() => { return LinksCollection.find().fetch(); }); const onSubmitted = () => { setFormTarget(null); setError(null); }; const onError = (err) => setError(err); const renderLinkForm = () => { return formTarget ? (<LinkForm onSubmitted={onSubmitted} onError={onError} doc={formTarget.doc} type={formTarget.type} />) : null }; const renderError = () => { return error ? (<div>{error.message}</div>) : null } const remove = _id => { Meteor.call('links.delete', { _id }, (err) => setError(err || null)) } return ( <div> <h2>Learn Meteor!</h2> <ul>{links.map( link => <li key={link._id}> <a href={link.url} target="_blank">{link.title}</a> <button onClick={() => setFormTarget({ type: 'update', doc: link })}>Update</button> <button onClick={() => remove(link._id)}>Delete</button> </li> )}</ul> {renderLinkForm()} {renderError()} <button onClick={() => setFormTarget({ type: 'insert' })}>Create new</button> </div> ); }; const LinkForm = ({ type, doc, onSubmitted, onError }) => { const [title, setTitle] = useState(doc?.title ?? ''); const [url, setUrl] = useState(doc?.url ?? ''); const onSubmit = e => { e.preventDefault() if (type === 'insert') { Meteor.call('links.create', { title, url }, (err, res) => { if (err) { return onError(err); } onSubmitted(res); }) } if (type === 'update') { Meteor.call('links.update', { _id: doc._id, title, url }, (err, res) => { if (err) { return onError(err); } onSubmitted(res); }) } }; return ( <form onSubmit={onSubmit}> <label> <span>Title</span> <input type="text" value={title} onChange={e => setTitle(e.target.value)} /> </label> <label> <span>URL</span> <input type="text" value={url} onChange={e => setUrl(e.target.value)} /> </label> <input type="submit" value="Submit" /> </form> ) } |
Now guys we will define the methods to create a new record, delete a record and update an existing record as shown below inside your server/main.js file.
This is the server side code which is executed in Meteor. All the server side code resides in this main.js file
server/main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import { Meteor } from 'meteor/meteor'; import { LinksCollection } from '/imports/api/links'; Meteor.methods({ 'links.create': function ({ title, url }) { return LinksCollection.insert({ title, url }) }, 'links.update': function ({ _id, title, url }) { const $set = {} if (title) { $set.title = title } if (url) { $set.url = url } return LinksCollection.update({ _id }, { $set }) }, 'links.delete': function ({ _id }) { return LinksCollection.remove({ _id }) } }) |
As you can see we have defined all the methods by using Meteor.Methods() passing array of objects which is pure methods that we defined in the react frontend. Each object is basically a function which has a name property. This name has to be the same where we called inside the frontend like for example to create a new record we are using links.create it is taking two arguments title and url and it is returning a new record by inserting using insert() method in MongoDB. For updating we are using the update() method passing the id and the actual object of the record. And for delete we are using the remove() method passing the id of the record that needs to be deleted.