Welcome to part three of my exploration of next. In part one I covered the basics: How to structure and style a basic site. In part two I covered how we can fetch data from an API and inject it within the react component lifecycle.
Today I want to add express and mongo to the project. That means in the same project next.js allows us to run express and react in a pretty easy way.
Lets get started
As you're about to see, most of the code that we'll write is pure express stuff. I have covered how to build a restful API with react and mongodb already, all that is going to be the exact same thing.
Let's install the required packages for this part of the tutorial
npm i -S express mongoose body-parser
Now lets start by creating ./server/index.js
page and add the following code
const express = require('express')
const next = require('next')
const bodyParser = require('body-parser')
const PORT = process.env.PORT || 3000
const dev = process.env.NODE_DEV !== 'production' //true false
const nextApp = next({ dev })
const handle = nextApp.getRequestHandler() //part of next config
const mongoose = require('mongoose')
const db = mongoose.connect('mongodb://localhost:27017/Photos')
nextApp.prepare().then(() => {
// express code here
const app = express()
})
That's how easy it is to add express to our project.
There are only four lines of code that are next.js related and everything else is the usual express code:
- Require next.
- Initiate it by letting it know which environment we're using (I chose to call that
nextApp
and call the express instanceapp
- because I can come back next few months and read the express code with no problem.) - Next requires
getRequestHandler()
- Then we run
prepare
on thenextApp
, which I guess prepares everything (!) andthen
we run express.
Lets add the rest of the code that makes up ./server/index.js
nextApp.prepare().then(() => {
// express code here
const app = express()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/api/photos', require('./routes/index'))
app.get('*', (req,res) => {
return handle(req,res) // for all the react stuff
})
app.listen(PORT, err => {
if (err) throw err;
console.log(`ready at http://localhost:${PORT}`)
})
})
Again, nothing new there, you can learn how to build an express site here. However, the only important thing is the start in app.get('*', ...)
. That just says all routers that aren't specified already, let next.js deal with them - which of course, in turn, it lets us deal with them in our react code.
Let's create the API routes at ./server/routes/index.js
const express = require('express')
const router = express.Router()
const Photos = require('../models/photoModel')
router.get('/', (req, res) => {
Photos.find({}, (err, photos) => {
res.json(photos)
})
})
router.use('/:id', (req, res, next) => {
console.log(req.params.id)
Photos.findById(req.params.id, (err, photo) => {
if(err)
res.status(500).send(err)
else
req.photo = photo
next()
})
})
router
.get('/:id', (req, res) => {
return res.json( req.photo )
})
.put('/:id', (req, res) =>{
Object.keys(req.body).map(key=>{
req.photo[key] = req.body[key]
})
req.photo.save()
res.json(req.photo)
})
module.exports = router;
Above we basically created the API points that we know our react code requires. When /api/photos
is requested, we return all photos found in the database. If any type of request is made on /api/photos/:id
then we have to find the relevant object in the database. Then depending on whether the request is a get
or a put
we either respond with the object or edit the object depending on the required data we've received.
Finally we need to create ./server/models/photoModel.js
const mongoose = require('mongoose')
const schema = mongoose.Schema
const photoModel = new schema({
tagline: { type: String} ,
image: { type: String},
likes: { type: String},
comments: { type: Array, default: [] }
})
module.exports = mongoose.model('photos', photoModel)
And that's about it!
Running the app
Unlike in previous tutorials, now we want to run our entire application from ./server/index.js
node server/index.js
Of course if you are following along you need to do two more things:
In the react code, /pages/index.js
and /pages/photo.js
we need to edit the addresses to where we are fetching from, now we need to point to 3000/api/photos
. Also when fetching, we would change id
to _id
. The database we are using has to contain the data of course. You can import the data I've left in the ripository by running the following command:
mongoimport --jsonArray --db Photos --collection photos --file '/your-project-folder/data/db.json'
That imports the data into Photos
database, and inside a collection named photos
.
The project now should be run by using express as the starting point. Hence you'd run
node server/index.js
And due to the way we've configured the code, next takes over and runs the react code as well. So what we have is, anything in /api
is handled by express and everything else is handled by react.
Whilst developing your application it be more useful to run nodemon server/index.js
instead. nodemon
monitors your code and restarts the server when ever you change the code. Very useful. However, you don't want it to restart the server when you modify the react code. Amongst other things it might get caught in a loop. Where next reloads react, then nodemon
reloads the server due to file changes. To solve this you should create a folder called nodemon.json
and add the following:
{
"verbose": true,
"ignore": ["node_modules", ".next"],
"watch": ["server/**/*"],
"ext": "js json"
}
We're basically telling nodemon
to concern itself with files that are within the server directory and that are javascript and JSON, and especially ignore the specified folders.
nodemon
is a really useful package that you should install as a global package (npm install -g nodemon
). nodemon
basically monitors your node code and re-runs the server whenever files change.
The end.
That's it for this tutorial, hope you liked it. There are few more things that I want to cover in this series, so stay tuned and thanks for reading! The repository for this part of the code can be found in the branch labeled part3
Part One:
Part Two:
Top comments (5)
When
handle
is used, I can no longer use GET requests(router.get()
) in my express app.Ifhandle
function is removed,GET requests can be implements through POSTman,but then fetching data from the pages is stuck.How can I use get requests in express app with Next.js?Why did you skip over this part:
In the react code, /pages/index.js and /pages/photo.js we need to edit the addresses to where we are fetching from, now we need to point to 3000/api/photos. Also when fetching, we would change id to _id. The database we are using has to contain the data of course
I'm not sure what you mean here, and I cannot continue with the tutorial.
THANK YOU SO MUCH for creating this three part series. It was very helpful.
It's a great tutorial and it works on local development. However, uploading to Zeit Now doesn't seem to work because it doesn't support custom servers
That's too complicated for me most of this code makes no sense