In this blog, you will learn how to setup a fullStack or more specifially a mern stack application and deploy it to Vercel and Render.

How to setup and deploy a fullStack(mern) application on Vercel and Render

Setting up a full-stack project and deploying that can be tricky. Let me show you how to do that.

Video tutorial

I have already created a video about it on my youtube channel. Check that out for more details.

If you like this video, please like share, and Subscribe to my channel.

Setup server

We are going to build a mono repo. This means the server and the client-side code will live inside a single repository.

1. Create directories

1mkdir mern-app
2cd mern-app
3mkdir client server
4cd server

2. Initialize package.json and install dependencies

1npm init -y
2npm install cors dotenv express mongoose # dependencies
3npm install @babel/cli @babel/core @babel/node @babel/preset-env babel-plugin-module-resolver concurrently nodemon --save-dev #dev dependencies
4
5npm install cors dotenv express mongoose && npm install @babel/cli @babel/core @babel/node @babel/preset-env babel-plugin-module-resolver concurrently nodemon --save-dev

Or use a single command:

1npm init -y && npm install cors dotenv express mongoose && npm install @babel/cli @babel/core @babel/node @babel/preset-env babel-plugin-module-resolver concurrently nodemon --save-dev

Package Explanation:

  • Cors: To handle cors error
  • Express: Nodejs framework
  • Dotenv: For handling environment variables
  • Mongoose: An ODM for MongoDB. In simple words, It is an abstraction over vanilla MongoDB SDK. It simplifies the way you interact with MongoDB and gives you much more features.
  • Concurrently: It allows you to run multiple dev servers within a single terminal.
  • Nodemon: To restart the dev server automatically when we will change our files.
  • Babel: To compile our javascript code

3. Setup Babel for absolute import(optional)

Absolute Import vs Relative Import

  • Create a .babelrc file.
1{
2 "presets": ["@babel/preset-env"],
3 "plugins": [
4 [
5 "module-resolver",
6 {
7 "root": ["./src"]
8 }
9 ]
10 ]
11}

With this, we are saying that we want src as our root and we always import files relative to the src directory.

  • Create a jsconfig.json file inorder to have intelisense.
1{
2 "compilerOptions": {
3 "baseUrl": "./src"
4 }
5}

4. Create scripts

1{
2 "scripts": {
3 "dev": "nodemon --exec babel-node src/index.js",
4 "build": "babel src -d dist",
5 "start": "node dist/index.js",
6 "both-dev": "concurrently \"npm run dev\" \"npm --prefix ../client/ run dev\""
7 }
8}
  • dev: To run the dev server with nodemon and also we always want to compile the code with babel.
  • build: Compile the code with babel for production.
  • start: Start the node server with the compiled code.
  • both-dev: To run the both client and backend dev server in a single command.

6. Create a basic node-server

We are going to build a simple to-do application that you don't have to understand how it works.

  • First, let's create a Todo Model. With the model, we can interact with MongoDB collections.
1import { Schema, model } from 'mongoose'
2
3const todoSchema = new Schema({
4 title: String,
5})
6
7const todoModel = model('todo', todoSchema)
8export default todoModel
  • Create the basic server. We have just created some simple API for CRUD applications.
1import { config } from 'dotenv'
2import express from 'express'
3import { connect as mongoConnect } from 'mongoose'
4import cors from 'cors'
5
6import Todo from 'models/Todo'
7
8config()
9
10mongoConnect(process.env.MONGO_URI)
11 .then(() => console.log('db connected'))
12 .catch(err => console.log(err))
13
14const app = express()
15
16// To parse the request body
17app.use(express.urlencoded({ extended: true }))
18app.use(express.json())
19
20// To handle cors error
21app.use(cors())
22
23app.get('/hello', (_, res) => res.send('Hello from Cules Coding'))
24
25app.post('/addTodo', async (req, res) => {
26 const { body } = req
27
28 const newTodo = new Todo(body)
29 const savedtodo = await newTodo.save()
30
31 return res.send(savedtodo)
32})
33
34app.delete('/deleteTodo', async (req, res) => {
35 const {
36 body: { todoId },
37 } = req
38
39 const response = await Todo.findByIdAndDelete(todoId)
40 return res.send(response)
41})
42
43app.get('/getAllTodos', async (_, res) => {
44 const response = await Todo.find({})
45 return res.send(response)
46})
47
48const port = process.env.PORT || 8000
49
50app.listen(port, () => console.log(`Server is running on ${port}`))

Now, our server is ready.

Let's build our client application

1. Create nextjs application

You don't have to use nextjs. You can also use create-react-app.

Just go to the client directory and run the command:

1npx create-next-app@latest . --use-npm

You can also optionally install chakra-UI which is an awesome React UI framework. You can learn about Chakra-UI from my crash course.

I am going to use it so that the UI doesn't look terrible. Also, install Axios to make requests to our backend server.

1npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6 axios

2. Add env variables

1NEXT_PUBLIC_API_URL=<backend_api_url>

3. Add the code for todo

1// pages/_app.jsx
2
3// This is only needed if you are using chakra-UI
4
5import { ChakraProvider } from '@chakra-ui/react'
6
7function MyApp({ Component, pageProps }) {
8 return (
9 <ChakraProvider>
10 <Component {...pageProps} />
11 </ChakraProvider>
12 )
13}
14
15export default MyApp
1// pages/index.jsx
2
3import {
4 Heading,
5 Center,
6 Button,
7 Box,
8 Input,
9 FormControl as Form,
10} from '@chakra-ui/react'
11import axios from 'axios'
12import { useEffect, useState } from 'react'
13
14const axiosInstance = axios.create({
15 baseURL: process.env.NEXT_PUBLIC_API_URL,
16})
17
18const Home = () => {
19 const [todos, setTodos] = useState([])
20 const [inputVal, setInputVal] = useState('')
21 const [refresh, setRefresh] = useState(false)
22
23 const getTodos = () => {
24 axiosInstance.get('/getAllTodos').then(res => setTodos(res.data))
25 }
26
27 useEffect(() => {
28 getTodos()
29 }, [])
30
31 useEffect(() => {
32 if (refresh) {
33 getTodos()
34
35 setTimeout(() => {
36 setRefresh(false)
37 })
38 }
39 }, [refresh])
40
41 const deleteTodo = todoId => () => {
42 axiosInstance
43 .delete('/deleteTodo', {
44 data: { todoId },
45 })
46 .then(() => setRefresh(true))
47 }
48
49 const addTodo = e => {
50 e.preventDefault()
51 axiosInstance.post('/addTodo', { title: inputVal }).then(() => {
52 setRefresh(true)
53 setInputVal('')
54 })
55 }
56
57 return (
58 <>
59 <Heading mb={12}>MERN</Heading>
60
61 <Form mb={10} as='form' onSubmit={addTodo}>
62 <Input
63 onChange={e => setInputVal(e.target.value)}
64 width='300px'
65 placeholder='New Todo'
66 size='md'
67 value={inputVal}
68 />
69 <Button type='submit'>Add</Button>
70 </Form>
71
72 {todos.map(({ _id, title }) => (
73 <Box key={_id} mb={10}>
74 <Center w='180px' h='80px' bg='red.200'>
75 {title}
76 </Center>
77 <Button onClick={deleteTodo(_id)}>Delete</Button>
78 </Box>
79 ))}
80 </>
81 )
82}
83
84export default Home

The above code will just get all the todos and render them to the UI. You can also add a new todo.

So our full-stack app is now done. Now we can deploy our application. First, we will deploy our backend to Render

I would highly recommend watching the deployment part of the video to understand things clearly.

Deploy on Render

  1. Go and sign up at https://render.com/
  2. Go to the dashboard and create a new web service
  3. If you haven't connected your account with GitHub, please do so. And give access to Render to your repo.
  4. Select the repo that you want to deploy and add the following information(required):
    • Name: Whatever you want
    • root directory: ./server
    • environment: Node
    • build command: npm install && npm run build
    • start command: npm start
  5. Add an environment variable from the advanced settings

Now you will be able to deploy the application.

If your deployment is successful then you should have a log like this:

1Dec 14 09:35:19 AM > server@1.0.0 start /opt/render/project/src/server
2Dec 14 09:35:19 AM > node dist/index.js
3Dec 14 09:35:19 AM
4Dec 14 09:35:26 AM Server is running on 10000
5Dec 14 09:35:29 AM DB connected

Deploy on vercel

  1. Go and sign up at https://vercel.com/
  2. Go to the dashboard and create a project
  3. If you haven't connected your account with GitHub, please do so. And give access to vercel to your repo.
  4. Select the repo that you want to deploy and add the following information(required):
    • Name: Whatever you want
    • framework preset: Nextjs(or Whatever you are using)
    • root directory: select client from the options
  5. Add the necessary environment variables like NEXT_PUBLIC_API_URL

Now you will be able to deploy the application.

Shameless Plug

I have made an Xbox landing page clone with React and Styled components. I hope you will enjoy it. Please consider like this video and subscribe to my channel.

That's it for this blog. I have tried to explain things simply. If you get stuck, you can ask me questions.

Contacts

Blogs you might want to read:

Videos might you might want to watch:

Previous PostBuild better forms with React hook form | Everything you need to know
Next PostUpload images from React and Node app to Cloudinary