Skip to content

Commit 1fd11a9

Browse files
authored
v2.0
Merge pull request #13 from iamsainikhil/v2.0
2 parents 0fae62d + 66d6c4e commit 1fd11a9

27 files changed

+1088
-231
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ node_modules
1818
.env.development.local
1919
.env.test.local
2020
.env.production.local
21-
/server/.env
21+
*/.env
2222

2323
npm-debug.log*
2424
yarn-debug.log*

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ There are so many weather-related applications out in the wild. So, the goal is
142142

143143
- First, I used the OpenWeatherMap API to fetch the weather forecast data. However, 5-day forecast data was not reliable i.e. _when a user on 14th March 2020 at 7:00 PM EST tries to fetch 5-day forecast data, will get forecast data starting 15th March 2020 at 12:00 AM UTC._ This posed a big problem of categorizing 5-day data into individual days since the data is not always consistent and is based on UTC and not based on the user timezone. Finally, I switched to Dark Sky API which is more reliable and provides a robust data model. However, there is a limit of `1000` calls/day.
144144

145-
- Dark Sky API needs a proxy server to send and receive a response which was easy in the development stage using a browser extension like **[this](https://chrome.google.com/webstore/detail/moesif-orign-cors-changer/digfbfaphojjndkpccljibejjbppifbc)** to enable CORS in the browser. However, I can't ask every user to install this extension in their browser to check the weather forecast. So, I overcome this issue temporarily for now using the `cors-anywhere` library which you can get more info by checking **[here](https://github.com/Rob--W/cors-anywhere)**.
145+
- Dark Sky API needs a proxy server to send and receive a response which was easy in the development stage using a browser extension like **[this](https://chrome.google.com/webstore/detail/moesif-orign-cors-changer/digfbfaphojjndkpccljibejjbppifbc)** to enable CORS in the browser. However, I can't ask every user to install this extension in their browser to check the weather forecast. So, I overcome this issue temporarily for now using the `cors-anywhere` library which you can get more info by checking **[here](https://github.com/Rob--W/cors-anywhere)**. However, I overcome the temporary solution and built a proxy server by deploying the _Node.js_ API functions on the **Vercel**'s serverless architecture and can be accessed **[here](https://weather-react-api.now.sh)**.
146146

147147
- Latest challenge I encountered is that **Teleport** API is temporarily shutdown and this led to broken autocomplete city search, and photos for favorited cities. Moreover, there is a tight coupling of code logic with this API. Now, I made a well thought highly scalable solution of using **Algolia Places** Rest API for fetching address based on city query as well as fetching city name based on latitude and longitude. I am very much happy about this change since it removed a lot of bad code and improved the application load times and performance.
148148

@@ -156,7 +156,7 @@ There are so many weather-related applications out in the wild. So, the goal is
156156

157157
## 🏎 Roadmap
158158

159-
- [ ] Build a proxy server using Express for Dark Sky API requests
159+
- [x] Build a proxy server using Express for Dark Sky API requests
160160

161161
- [ ] Unit Testing
162162

api/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# serverless node API functions deployed on Vercel (Zeit)
2+
3+
[_Production API_](https://weather-react-api.now.sh)

api/index.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
const express = require('express')
2+
const axios = require('axios')
3+
const port = process.env.PORT || 3001
4+
5+
// Configure app to use bodyParser to parse json data
6+
const app = express()
7+
8+
const server = require('http').createServer(app)
9+
require('dotenv').config()
10+
11+
// custom HTTP headers for authenticating requests sent to Algolia places server
12+
const HEADERS = {
13+
'X-Algolia-Application-Id': process.env.ALGOLIA_PLACES_APP_ID || '',
14+
'X-Algolia-API-Key': process.env.ALGOLIA_PLACES_API_KEY || '',
15+
}
16+
const DARKSKY_API_KEY = process.env.DARKSKY_API_KEY
17+
18+
// Test server is working (GET http://localhost:3001/)
19+
app.get('/', function (req, res) {
20+
res.send(`
21+
<!DOCTYPE html>
22+
<html lang="en">
23+
<head>
24+
<meta charset="UTF-8"/>
25+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
26+
<title>Weather React API</title>
27+
</head>
28+
<body>
29+
<h1>Welcome to <a href="https://iamsainikhil.github.io/weather-react" target="_blank" rel="noreferrer noopener">Weather React</a> application's proxy server</h1>
30+
</body>
31+
</html>
32+
`)
33+
})
34+
35+
// Fetch address based on latlong
36+
app.get('/address/coords/:latlong', (req, res) => {
37+
const {latlong} = req.params
38+
const url = `https://places-dsn.algolia.net/1/places/reverse?aroundLatLng=${latlong},&hitsPerPage=1&language=en`
39+
axios
40+
.get(url, {headers: HEADERS})
41+
.then((response) => {
42+
const {data} = response
43+
res.status(200)
44+
res.json(data)
45+
})
46+
.catch((err) => {
47+
res.status(err.response ? err.response.status : 500)
48+
res.send(err.message || 'Something went wrong! Please try again later.')
49+
})
50+
})
51+
52+
// Fetch weather forecast based on latlong
53+
app.get('/forecast/coords/:latlong', (req, res) => {
54+
const {latlong} = req.params
55+
const url = `https://api.darksky.net/forecast/${DARKSKY_API_KEY}/${latlong}?extend=hourly&exclude=minutely,flags`
56+
axios
57+
.get(url)
58+
.then((response) => {
59+
const {data} = response
60+
res.status(200)
61+
res.json(data)
62+
})
63+
.catch((err) => {
64+
res.status(err.response ? err.response.status : 500)
65+
res.send(err.message || 'Something went wrong! Please try again later.')
66+
})
67+
})
68+
69+
// Fetch address list based on query
70+
app.get('/places/query/:city/:latlong', (req, res) => {
71+
const {city, latlong} = req.params
72+
axios
73+
.request({
74+
url: 'https://places-dsn.algolia.net/1/places/query',
75+
method: 'post',
76+
data: {
77+
query: city,
78+
type: 'city',
79+
aroundLatLng: latlong,
80+
},
81+
headers: HEADERS,
82+
})
83+
.then((response) => {
84+
const {data} = response
85+
res.status(200)
86+
res.json(data)
87+
})
88+
.catch((err) => {
89+
res.status(err.response ? err.response.status : 500)
90+
res.send(err.message || 'Something went wrong! Please try again later.')
91+
})
92+
})
93+
94+
// Start the server
95+
server.listen(port)
96+
console.log('Server is listening on port ' + port)

api/now.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"version": 2,
3+
"builds": [{"src": "index.js", "use": "@now/node-server"}],
4+
"routes": [
5+
{
6+
"src": ".*",
7+
"methods": ["GET", "OPTIONS"],
8+
"headers": {
9+
"Access-Control-Allow-Origin": "https://iamsainikhil.github.io"
10+
},
11+
"dest": "/index.js",
12+
"continue": true
13+
},
14+
{
15+
"src": "/address/coords/(?<latlong>[^/]+)"
16+
},
17+
{
18+
"src": "/forecast/coords/(?<latlong>[^/]+)"
19+
},
20+
{
21+
"src": "/places/query/(?<city>[^/]+)/(?<latlong>[^/]+)"
22+
}
23+
]
24+
}

0 commit comments

Comments
 (0)