├── .gitignore ├── components ├── InteractiveButtons.js ├── Layout.js └── Photo.js ├── data └── db.json ├── nodemon.json ├── package-lock.json ├── package.json ├── pages ├── index.js └── photo.js ├── readme.md ├── server ├── index.js ├── models │ └── photoModel.js └── routes │ └── index.js ├── static ├── art │ ├── 10547074_1533921633486350_110615324_n.jpg │ ├── 10584559_835284339817092_1481087900_n.jpg │ ├── 10593244_721194281250987_220088259_n.jpg │ ├── 10597456_1519924261553666_1863286978_n.jpg │ ├── 10616485_291151261072849_1966044802_n.jpg │ ├── 10643907_752060824852022_992571927_n.jpg │ ├── 10706924_571714519595911_1377858612_n.jpg │ ├── 11015533_614181608712678_1890813502_n.jpg │ ├── 11055766_1625987057630945_1001931112_n.jpg │ ├── 11078620_964175566947957_1173683403_n.jpg │ ├── 11193031_846606082098782_2076006692_n.jpg │ ├── 11246813_1444527059175905_846391623_n.jpg │ ├── 11248015_1439340809700961_1166378400_n.jpg │ ├── 12317818_935715123165411_1906227833_n.jpg │ └── 927756_283684128492129_838664181_n.jpg ├── ca2caa1bc6a6d99c333acdfa26d17fc4.jpg └── profile.png ├── thumb1.png └── thumb2.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless 75 | 76 | .DS_Store 77 | tutorial.md -------------------------------------------------------------------------------- /components/InteractiveButtons.js: -------------------------------------------------------------------------------- 1 | import { MdModeComment, MdFavoriteBorder } from 'react-icons/md' 2 | export default ({likes, LikesEntry, commentsNum}) => ( 3 |
4 | 5 |

{ commentsNum }

6 | 33 |
34 | ) 35 | 36 | -------------------------------------------------------------------------------- /components/Layout.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import Head from 'next/head' 3 | 4 | export default ({ children, title = 'This is the default title' }) => ( 5 |
6 | 7 | { title } 8 | 9 | 10 | 11 |
12 |
13 |

gallery

14 |

Original art

15 |
16 | { children } 17 |
18 | 19 |
20 | 43 | 81 |
82 |
83 | ) -------------------------------------------------------------------------------- /components/Photo.js: -------------------------------------------------------------------------------- 1 | import CommentsFunctionality from './InteractiveButtons' 2 | import Link from 'next/link' 3 | 4 | export default (props) => { 5 | return ( 6 |
7 |
8 | 9 | 10 | 11 |
12 |

{props.data.tagline}

13 | props.LikesEntry(props.data.id)} 15 | commentsNum={props.data.comments.length} 16 | likes={props.data.likes} /> 17 |
18 |
19 | 43 |
44 | ) 45 | } -------------------------------------------------------------------------------- /data/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "photos": [ 3 | { 4 | "id": "1110", 5 | "tagline": "You're looking at me punk?", 6 | "image": "927756_283684128492129_838664181_n", 7 | "likes": 4, 8 | "comments": [ 9 | { 10 | "user": "rex2018", 11 | "body": "Hey this is dope! xxx" 12 | }, 13 | { 14 | "user": "grasSHopper", 15 | "body": "This has given me few ideas" 16 | }, 17 | { 18 | "user": "TheReal2pac", 19 | "body": "Thug 4 life, that's all I have to say" 20 | }, 21 | { 22 | "user": "AUREL KURTULA", 23 | "body": "THIS IS ABOUT KURTULA" 24 | } 25 | ] 26 | }, 27 | { 28 | "id": "002", 29 | "tagline": "They say big heads are cleaver #smartass", 30 | "image": "10547074_1533921633486350_110615324_n", 31 | "likes": 2, 32 | "comments": [ 33 | { 34 | "user": "pantherXXX", 35 | "body": "#smartass indeed baby xxx" 36 | }, 37 | { 38 | "user": "shiningRod", 39 | "body": "Talk about magical" 40 | }, 41 | { 42 | "user": "TheReal2pac", 43 | "body": "I ain't feelin' this one homeboy" 44 | } 45 | ] 46 | }, 47 | { 48 | "id": "003", 49 | "tagline": "tongue and hand! You'll get it!", 50 | "image": "10584559_835284339817092_1481087900_n", 51 | "likes": 0, 52 | "comments": [ 53 | { 54 | "user": "johnDope", 55 | "body": "My brother did anyone tell you you've got talent" 56 | }, 57 | { 58 | "user": "aurelkurtula", 59 | "body": "@johnDope dad! Is that you?" 60 | }, 61 | { 62 | "user": "johnDope", 63 | "body": "no son. By the way, come down for dinner" 64 | }, 65 | { 66 | "user": "bla bla bla", 67 | "body": "bli bli bli" 68 | } 69 | ] 70 | }, 71 | { 72 | "id": "004", 73 | "tagline": "You f*ed my nose man, get that rubber and fix it #shitartist. add more content here", 74 | "image": "10593244_721194281250987_220088259_n", 75 | "likes": 0, 76 | "comments": [ 77 | { 78 | "user": "rex2018", 79 | "body": "Why you gone and messed his nose man" 80 | }, 81 | { 82 | "user": "grasSHopper", 83 | "body": "#shitartist my behind, you' good boy" 84 | }, 85 | { 86 | "user": "aurelkurtula", 87 | "body": "Ma? is that you?" 88 | } 89 | ] 90 | }, 91 | { 92 | "id": "005", 93 | "tagline": "What's so funny", 94 | "image": "10597456_1519924261553666_1863286978_n", 95 | "likes": 0, 96 | "comments": [ 97 | { 98 | "user": "rex2018", 99 | "body": "Hey this is dope! xxx" 100 | }, 101 | { 102 | "user": "grasSHopper", 103 | "body": "This has given me few ideas" 104 | }, 105 | { 106 | "user": "TheReal2pac", 107 | "body": "Thug 4 life, that's all I have to say" 108 | }, 109 | { 110 | "user": "Loop", 111 | "body": "This is the comment" 112 | } 113 | ] 114 | } 115 | ] 116 | } -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "ignore": ["node_modules", ".next"], 4 | "watch": ["server/**/*", "index.js", "data/**/*"], 5 | "ext": "js json" 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "insta-next", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon server/index.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.18.3", 13 | "express": "^4.16.3", 14 | "isomorphic-unfetch": "^2.1.1", 15 | "json-server": "^0.14.0", 16 | "mongoose": "^5.2.9", 17 | "next": "^6.1.1", 18 | "react": "^16.4.2", 19 | "react-dom": "^16.4.2", 20 | "react-icons": "^3.0.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import fetch from 'isomorphic-unfetch' 3 | import Layout from '../components/Layout'; 4 | import Photo from '../components/Photo'; 5 | 6 | export default class extends Component { 7 | static async getInitialProps() { 8 | const res = await fetch('http://localhost:3000/api/photos') 9 | const images = await res.json() 10 | return { images } 11 | } 12 | componentWillMount() { 13 | this.setState({ 14 | images: this.props.images 15 | }) 16 | } 17 | LikesEntry(id) { 18 | const images = this.state.images 19 | let image = images.find(image => image.id === id) 20 | image.likes = parseInt(image.likes) + 1 21 | // add changes to state 22 | this.setState({ 23 | images 24 | }) 25 | fetch(`http://localhost:3000/api/photos/${id}`, { 26 | method: 'PUT', 27 | headers: { 28 | 'Accept': 'application/json', 29 | 'Content-Type': 'application/json' 30 | }, 31 | body: JSON. stringify(image) 32 | }) 33 | } 34 | render() { 35 | return ( 36 | 37 | { 38 | this.state.images.map((image, key) => 39 | ) 44 | } 45 | 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pages/photo.js: -------------------------------------------------------------------------------- 1 | import react, { Component } from 'react' 2 | import fetch from 'isomorphic-unfetch' 3 | import Layout from '../components/Layout'; 4 | import Photo from '../components/Photo'; 5 | import CommentsFunctionality from '../components/InteractiveButtons' 6 | 7 | export default class extends Component { 8 | static async getInitialProps({query}) { 9 | const {id} = {...query} 10 | const res = await fetch(`http://localhost:3000/api/photos/${id}`) 11 | const image = await res.json() 12 | return { image } 13 | } 14 | componentWillMount() { 15 | this.setState({ 16 | image: this.props.image 17 | }) 18 | } 19 | submitComments(e) { 20 | e.preventDefault(); 21 | const user = this.refs.author.value 22 | const body = this.refs.comment.value 23 | const comments = this.state.image.comments 24 | comments.push({user, body}) 25 | this.setState({comments}) 26 | fetch(`http://localhost:3000/api/photos/${this.state.image._id}`, { 27 | method: 'PUT', 28 | headers: { 29 | 'Accept': 'application/json', 30 | 'Content-Type': 'application/json' 31 | }, 32 | body: JSON.stringify(this.state.image) 33 | }) 34 | } 35 | render(){ 36 | return( 37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 |

{this.state.image.tagline}

45 | { 46 | this.state.image.comments.map((comment, key) =>

{comment.user}:{comment.body}

) 47 | } 48 |
this.submitComments(e) }> 49 | 50 | 51 | 52 |
53 |
54 |
55 | 118 |
119 | ) 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #### About the project 2 | 3 | This repository is dedicated to exploring next.js. Each branch corresponds to a tutorial I have writen and the master branch will always have code from the latest tutorial. 4 | 5 | #### Setup 6 | 7 | ```sh 8 | npm install # install dependencies 9 | npm run api # run json-server 10 | npm start # run next.js 11 | ``` 12 | 13 | #### Tutorials so far: 14 | 15 | - [Introduction to the basics of Next.js](https://dev.to/aurelkurtula/introduction-to-the-basics-of-nextjs-1loa) - branch : [part1](https://github.com/aurelkurtula/basics-of-nextJS/tree/part1) 16 | - [Retrieving data from an external API](https://dev.to/aurelkurtula/introduction-to-the-basics-of-nextjs---part-two-pad) 17 | 18 | 19 | To illustrate the features I have decided to create a prototype that resembles instagram 20 | 21 | Home page: 22 | 23 | ![](./thumb1.png) 24 | 25 | Photo page: 26 | 27 | ![](./thumb2.png) -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const next = require('next') 3 | const bodyParser = require('body-parser') 4 | const PORT = process.env.PORT || 3000 5 | const dev = process.env.NODE_DEV !== 'production' //true false 6 | const nextApp = next({ dev }) 7 | const handle = nextApp.getRequestHandler() //part of next config 8 | const mongoose = require('mongoose') 9 | 10 | const db = mongoose.connect('mongodb://localhost:27017/Photos') 11 | 12 | nextApp.prepare().then(() => { 13 | // express code here 14 | const app = express() 15 | app.use(bodyParser.json()); 16 | app.use(bodyParser.urlencoded({ extended: true })); 17 | app.use('/api/photos', require('./routes/index')) 18 | app.get('*', (req,res) => { 19 | return handle(req,res) // for all the react stuff 20 | }) 21 | app.listen(PORT, err => { 22 | if (err) throw err; 23 | console.log(`ready at http://localhost:${PORT}`) 24 | }) 25 | }) -------------------------------------------------------------------------------- /server/models/photoModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const schema = mongoose.Schema 3 | 4 | const photoModel = new schema({ 5 | tagline: { type: String} , 6 | image: { type: String}, 7 | likes: { type: String}, 8 | comments: { type: Array, default: [] } 9 | }) 10 | 11 | module.exports = mongoose.model('photos', photoModel) -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const Photos = require('../models/photoModel') 4 | 5 | router.get('/', (req, res) => { 6 | Photos.find({}, (err, photos) => { 7 | res.json(photos) 8 | }) 9 | }) 10 | router.use('/:id', (req, res, next) => { 11 | Photos.findById(req.params.id, (err, photo) => { 12 | if(err) 13 | res.status(500).send(err) 14 | else 15 | req.photo = photo 16 | next() 17 | }) 18 | }) 19 | router 20 | .get('/:id', (req, res) => { 21 | return res.json( req.photo ) 22 | }) 23 | .put('/:id', (req, res) =>{ 24 | 25 | Object.keys(req.body).map(key=>{ 26 | req.photo[key] = req.body[key] 27 | }) 28 | req.photo.save() 29 | res.json(req.photo) 30 | }) 31 | module.exports = router; -------------------------------------------------------------------------------- /static/art/10547074_1533921633486350_110615324_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10547074_1533921633486350_110615324_n.jpg -------------------------------------------------------------------------------- /static/art/10584559_835284339817092_1481087900_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10584559_835284339817092_1481087900_n.jpg -------------------------------------------------------------------------------- /static/art/10593244_721194281250987_220088259_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10593244_721194281250987_220088259_n.jpg -------------------------------------------------------------------------------- /static/art/10597456_1519924261553666_1863286978_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10597456_1519924261553666_1863286978_n.jpg -------------------------------------------------------------------------------- /static/art/10616485_291151261072849_1966044802_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10616485_291151261072849_1966044802_n.jpg -------------------------------------------------------------------------------- /static/art/10643907_752060824852022_992571927_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10643907_752060824852022_992571927_n.jpg -------------------------------------------------------------------------------- /static/art/10706924_571714519595911_1377858612_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/10706924_571714519595911_1377858612_n.jpg -------------------------------------------------------------------------------- /static/art/11015533_614181608712678_1890813502_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11015533_614181608712678_1890813502_n.jpg -------------------------------------------------------------------------------- /static/art/11055766_1625987057630945_1001931112_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11055766_1625987057630945_1001931112_n.jpg -------------------------------------------------------------------------------- /static/art/11078620_964175566947957_1173683403_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11078620_964175566947957_1173683403_n.jpg -------------------------------------------------------------------------------- /static/art/11193031_846606082098782_2076006692_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11193031_846606082098782_2076006692_n.jpg -------------------------------------------------------------------------------- /static/art/11246813_1444527059175905_846391623_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11246813_1444527059175905_846391623_n.jpg -------------------------------------------------------------------------------- /static/art/11248015_1439340809700961_1166378400_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/11248015_1439340809700961_1166378400_n.jpg -------------------------------------------------------------------------------- /static/art/12317818_935715123165411_1906227833_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/12317818_935715123165411_1906227833_n.jpg -------------------------------------------------------------------------------- /static/art/927756_283684128492129_838664181_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/art/927756_283684128492129_838664181_n.jpg -------------------------------------------------------------------------------- /static/ca2caa1bc6a6d99c333acdfa26d17fc4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/ca2caa1bc6a6d99c333acdfa26d17fc4.jpg -------------------------------------------------------------------------------- /static/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/static/profile.png -------------------------------------------------------------------------------- /thumb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/thumb1.png -------------------------------------------------------------------------------- /thumb2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelkurtula/basics-of-nextJS/ec103e9b770efd69a9baa9d9db51e2b575322153/thumb2.png --------------------------------------------------------------------------------