Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ FACEBOOK_SECRET=41860e58c256a3d7ad8267d3c1939a4a
# FB Pixel ID is optional if you are trying to do customer rtracking
FACEBOOK_PIXEL_ID=

FOURSQUARE_ID=2STROLSFBMZLAHG3IBA141EM2HGRF0IRIBB4KXMOGA2EH3JG
FOURSQUARE_SECRET=UAABFAWTIHIUFBL0PDC3TDMSXJF2GTGWLD3BES1QHXKAIYQB
FOURSQUARE_APIKEY=foursquare-service-key

GITHUB_ID=cb448b1d4f0c743a1e36
GITHUB_SECRET=815aa4606f476444691c5f1c16b9c70da6714dc6
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,15 @@ Obtain SMTP credentials from a provider for transactional emails. Set the SMTP_U

<img src="https://imgur.com/2P4UMvC.png" height="75">

- Go to <a href="https://developer.foursquare.com" target="_blank">Foursquare for Developers</a> and log in
- Click on **My Apps** in the top menu
- Click the **Create A New App** button
- Enter _App Name_, _Welcome page url_,
- For **Redirect URI**: your BASE_URL value followed by /auth/foursquare/callback (i.e. `http://localhost:8080/auth/foursquare/callback` )
- Click **Save Changes**
- Copy and paste _Client ID_ and _Client Secret_ keys into `.env` file
- Go to <a href="https://foursquare.com/developers" target="_blank">Foursquare for Developers</a> and log in

- Click on **Create a new project** button
- Enter your _Organization_ and _Project Name_
- Click **Create**
- Navigate to your project
- Click **Settings** in the left-hand-side menu
- Generate a Service API Key
- Copy and paste the Service API Key as `FOURSQUARE_APIKEY` in your `.env` file

<hr>

Expand Down
46 changes: 29 additions & 17 deletions controllers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,43 @@ exports.getApi = (req, res) => {
* GET /api/foursquare
* Foursquare API example.
*/
exports.getFoursquare = async (req, res, next) => {
exports.getFoursquare = async (req, res) => {
try {
const headers = {
Authorization: `${process.env.FOURSQUARE_APIKEY}`,
const options = {
method: 'GET',
headers: {
accept: 'application/json',
'X-Places-Api-Version': '2025-06-17',
authorization: `Bearer ${process.env.FOURSQUARE_APIKEY}`,
},
};

const fetchJson = async (url, fetchOptions, label) => {
const res = await fetch(url, fetchOptions);
if (!res.ok) {
const text = await res.text().catch(() => '<unable to read body>');
throw new Error(`${label} failed: ${res.status} ${res.statusText} - ${text}`);
}
return res.json();
};

const [trendingVenuesRes, venueDetailRes, venuePhotosRes] = await Promise.all([
fetch('https://api.foursquare.com/v3/places/search?ll=47.609657,-122.342148&limit=10', {
headers,
}).then((res) => res.json()),
fetch('https://api.foursquare.com/v3/places/427ea800f964a520b1211fe3', {
headers,
}).then((res) => res.json()),
fetch('https://api.foursquare.com/v3/places/427ea800f964a520b1211fe3/photos', {
headers,
}).then((res) => res.json()),
const [trendingVenuesRes, venueDetailRes] = await Promise.all([
fetchJson('https://places-api.foursquare.com/places/search?ll=47.609657,-122.342148&limit=10', options, 'Foursquare search'),
fetchJson('https://places-api.foursquare.com/places/427ea800f964a520b1211fe3', options, 'Foursquare venue detail'),
]);
res.render('api/foursquare', {
title: 'Foursquare API (v3)',
trendingVenues: trendingVenuesRes.results,
title: 'Foursquare Places API',
trendingVenues: trendingVenuesRes.results || [],
venueDetail: venueDetailRes,
venuePhotos: venuePhotosRes.slice(0, 9), // Limit the photos to 9
});
} catch (error) {
next(error);
console.error('Foursquare API Error:', error);
return res.status(500).render('api/foursquare', {
title: 'Foursquare Places API',
trendingVenues: [],
venueDetail: null,
error: 'Failed to fetch Foursquare data',
});
}
};

Expand Down
84 changes: 38 additions & 46 deletions views/api/foursquare.pug
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,48 @@ block content
| Foursquare API

.btn-group.mb-4.d-flex(role='group')
a.btn.btn-primary.w-100(href='https://location.foursquare.com/developer', target='_blank')
a.btn.btn-primary.w-100(href='https://foursquare.com/developer', target='_blank')
i.far.fa-check-square.fa-sm.me-2
| Developer Info
a.btn.btn-primary.w-100(href='https://docs.foursquare.com/', target='_blank')
i.fas.fa-code-branch.fa-sm.me-2
| API Docs
h3.text-primary Trending Venues
p Near longitude: -122.342148, latitude: 47.609657
table.table.table-striped.table-bordered
thead
tr
th.d-xs
th Name
th.d-xs.d-sm Category
th.d-xs Address
th.d-xs Distance (meters)
th.d-xs Open Now ?
tbody
each venue in trendingVenues
if error
.alert.alert-danger.mt-3 #{ error }
else
h3.text-primary Trending Venues
p Near longitude: -122.342148, latitude: 47.609657
table.table.table-striped.table-bordered
thead
tr
td.d-xs
if venue.categories && venue.categories.length > 0
img(src=venue.categories[0].icon.prefix + '32' + venue.categories[0].icon.suffix, alt=venue.categories[0].name, width='32', height='32')
else
| N/A
td= venue.name
td.d-xs.d-sm= venue.categories && venue.categories.length > 0 ? venue.categories[0].name : 'N/A'
td.d-xs= venue.location.formatted_address || 'N/A'
td.d-xs= venue.distance
td.d-xs= venue.closed_bucket || 'Unknown'
br
h3.text-primary Venue Details
p
i
u #{ venueDetail.name }
if venueDetail.categories && venueDetail.categories.length > 0
th.d-xs
th Name
th.d-xs.d-sm Category
th.d-xs Address
th.d-xs Distance (meters)
tbody
each venue in trendingVenues
tr
td.d-xs
if venue.categories && venue.categories.length > 0
img(src=venue.categories[0].icon.prefix + '32' + venue.categories[0].icon.suffix, alt=venue.categories[0].name, width='32', height='32')
else
| N/A
td= venue.name
td.d-xs.d-sm= venue.categories && venue.categories.length > 0 ? venue.categories[0].name : 'N/A'
td.d-xs= venue.location.formatted_address || 'N/A'
td.d-xs= venue.distance
br
h3.text-primary Venue Details
p
i
u #{ venueDetail.name }
if venueDetail.categories && venueDetail.categories.length > 0
|
| is a #{ venueDetail.categories[0].name }
|
| is a #{ venueDetail.categories[0].name }
|
| located at #{ venueDetail.location.address || 'N/A' }, #{ venueDetail.location.locality || 'N/A' }, #{ venueDetail.location.region || 'N/A' }. (longitude: #{ venueDetail.geocodes.main.longitude }, latitude: #{ venueDetail.geocodes.main.latitude })
if venueDetail.related_places
if venueDetail.related_places.children && venueDetail.related_places.children.length > 0
p Related venues or businesses to #{ venueDetail.name }, which are mostly in the same building or the immediate area are:
p(style='margin-left: 20px; white-space: pre-wrap') #{ venueDetail.related_places.children.map(place => place.name).join(', ') }
if venuePhotos && venuePhotos.length > 0
h4.text-secondary Photo Gallery
| #{ venueDetail.name }
.row
each photo in venuePhotos
.col-md-4
img.img-thumbnail(src=photo.prefix + '400x400' + photo.suffix, alt=venueDetail.name, width='100%')
else
p No photos available.
| located at #{ venueDetail.location.address || 'N/A' }, #{ venueDetail.location.locality || 'N/A' }, #{ venueDetail.location.region || 'N/A' }. (longitude: #{ venueDetail.longitude }, latitude: #{ venueDetail.latitude })
if venueDetail.related_places
if venueDetail.related_places.children && venueDetail.related_places.children.length > 0
p Related venues or businesses to #{ venueDetail.name }, which are mostly in the same building or the immediate area are:
p(style='margin-left: 20px; white-space: pre-wrap') #{ venueDetail.related_places.children.map(place => place.name).join(', ') }
Loading