Skip to content

Commit bb4585c

Browse files
committed
replace express with hono
1 parent f3f13b9 commit bb4585c

File tree

5 files changed

+3718
-2939
lines changed

5 files changed

+3718
-2939
lines changed

api/index.ts

Lines changed: 109 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,26 @@
1-
import express, {
2-
Express,
3-
Request,
4-
Response,
5-
NextFunction,
6-
RequestHandler,
7-
} from "express";
1+
import { Hono } from "hono";
2+
import { cors } from "hono/cors";
83
import { ALL_PLACES } from "../data/geoData.js";
94
import { getPlace, findPlace, getTimes } from "../api_src/calculator.js";
105
import {
116
getCommonTimeRequestParameters,
127
getParamsForPlaceSearch,
138
isInRange,
149
} from "../api_src/util.js";
10+
import { getPlaceSuggestionsByText, getNearbyPlaces, getPlaceById } from "irem";
11+
1512
import path from "path";
1613
import { fileURLToPath } from "url";
1714
import { dirname } from "path";
18-
import { getPlaceSuggestionsByText, getNearbyPlaces, getPlaceById } from "irem";
19-
20-
export const app: Express = express();
15+
import { Context } from "hono";
2116

22-
/** use this function like `app.use(allowOriginForAll);` for an express app
23-
* Make API accessible for all clients. Not for only clients from a specific domain.
24-
*/
25-
const allowOriginForAll: RequestHandler = (
26-
_: Request,
27-
res: Response,
28-
next: NextFunction,
29-
) => {
30-
res.setHeader("Access-Control-Allow-Credentials", "true");
31-
res.setHeader("Access-Control-Allow-Origin", "*");
32-
// another common pattern
33-
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
34-
res.setHeader(
35-
"Access-Control-Allow-Methods",
36-
"GET,OPTIONS,PATCH,DELETE,POST,PUT",
37-
);
38-
res.setHeader(
39-
"Access-Control-Allow-Headers",
40-
"X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version",
41-
);
42-
next();
43-
};
17+
const app = new Hono();
4418

45-
app.use(allowOriginForAll);
46-
app.use(express.static("public"));
47-
app.use(logIPAdress);
19+
// Middleware
20+
app.use("*", cors());
4821

4922
app.get("/api/searchPlaces", searchPlaces);
50-
app.get("/api/nearbyPlaces", nearByPlaces);
23+
app.get("/api/nearByPlaces", nearByPlaces);
5124
app.get("/api/timesForGPS", getTimesForGPS);
5225
app.get("/api/timesForPlace", getTimesForPlace);
5326
app.get("/api/timesFromCoordinates", getTimesFromCoordinates);
@@ -71,120 +44,137 @@ app.post("/api/ip", getIPAdress);
7144

7245
const __dirname = dirname(fileURLToPath(import.meta.url));
7346
// if not starting with "/api" return index.html page as response so that routes in the static page will work
74-
app.get(/^\/(?!api).*/, (_, res) => {
75-
res.sendFile(path.resolve(__dirname, "..", "public", "index.html"));
47+
app.get("*", async (c) => {
48+
const url = new URL(c.req.url);
49+
if (!url.pathname.startsWith("/api")) {
50+
const fs = await import("fs/promises");
51+
try {
52+
const content = await fs.readFile(
53+
path.resolve(__dirname, "..", "public", "index.html"),
54+
"utf-8",
55+
);
56+
return c.html(content);
57+
} catch {
58+
return c.text("index.html not found", 404);
59+
}
60+
}
61+
return c.text("index.html not found", 404);
7662
});
7763

78-
const PORT = process.env["PORT"] || 3000;
79-
export const httpServer = app.listen(PORT);
64+
// For compatibility with some localdev setups, but purely optional for Hono + Workers
65+
if (
66+
process.env["NODE_ENV"] !== "production" &&
67+
!process.env["Generic_Worker"]
68+
) {
69+
// try to serve with @hono/node-server if installed, or just log
70+
console.log(
71+
`To run locally, use 'npm run dev' which uses nodemon/tsx. For production use 'wrangler dev'`,
72+
);
73+
}
8074

8175
/** get a list of countries
82-
* @param {} _
83-
* @param {} res
8476
*/
85-
function getCountries(_: Request, res: Response) {
77+
function getCountries(c: Context) {
8678
try {
8779
const r = [];
88-
for (const c in ALL_PLACES) {
89-
r.push({ code: ALL_PLACES[c].code, name: c });
80+
for (const place in ALL_PLACES) {
81+
r.push({ code: ALL_PLACES[place].code, name: place });
9082
}
91-
res.send(r.sort((a, b) => a.name.localeCompare(b.name)));
83+
return c.json(r.sort((a, b) => a.name.localeCompare(b.name)));
9284
} catch (e) {
9385
console.log("error! ", e);
94-
res.send("error: " + e);
95-
} finally {
96-
res.send("error: ");
86+
return c.json({ error: String(e) });
9787
}
9888
}
9989

100-
function getRegionsOfCountry(req: Request, res: Response) {
101-
const country = req.query["country"] as string;
90+
function getRegionsOfCountry(c: Context) {
91+
const country = c.req.query("country") as string;
10292
if (ALL_PLACES[country]) {
103-
res.send(
93+
return c.json(
10494
Object.keys(ALL_PLACES[country].regions).sort((a, b) =>
10595
a.localeCompare(b),
10696
),
10797
);
10898
} else {
109-
res.send({ error: "NOT FOUND!" });
99+
return c.json({ error: "NOT FOUND!" });
110100
}
111101
}
112102

113-
function getCitiesOfRegion(req: Request, res: Response) {
114-
const country = req.query["country"] as string;
115-
const region = req.query["region"] as string;
103+
function getCitiesOfRegion(c: Context) {
104+
const country = c.req.query("country") as string;
105+
const region = c.req.query("region") as string;
116106
if (ALL_PLACES[country] && ALL_PLACES[country].regions[region]) {
117-
res.send(
107+
return c.json(
118108
Object.keys(ALL_PLACES[country].regions[region]).sort((a, b) =>
119109
a.localeCompare(b),
120110
),
121111
);
122112
} else {
123-
res.send({ error: "NOT FOUND!" });
113+
return c.json({ error: "NOT FOUND!" });
124114
}
125115
}
126116

127-
function getCoordinateData(req: Request, res: Response) {
128-
const country = req.query["country"] as string;
129-
const region = req.query["region"] as string;
130-
const city = req.query["city"] as string;
117+
function getCoordinateData(c: Context) {
118+
const country = c.req.query("country") as string;
119+
const region = c.req.query("region") as string;
120+
const city = c.req.query("city") as string;
131121
const coords = getPlace(country, region, city);
132122
if (coords) {
133-
res.send(coords);
123+
return c.json(coords);
134124
} else {
135-
res.send({ error: "NOT FOUND!" });
125+
return c.json({ error: "NOT FOUND!" });
136126
}
137127
}
138128

139129
/**
140130
* DEPRECATED, use `getTimesForGPS`
141131
*/
142-
function getTimesFromCoordinates(req: Request, res: Response) {
143-
const lat = Number(req.query["lat"] as string);
144-
const lng = Number(req.query["lng"] as string);
132+
function getTimesFromCoordinates(c: Context) {
133+
const lat = Number(c.req.query("lat") as string);
134+
const lng = Number(c.req.query("lng") as string);
145135
const { date, days, tzOffset, calculateMethod } =
146-
getCommonTimeRequestParameters(req);
136+
getCommonTimeRequestParameters(c);
147137
if (
148138
isNaN(lat) ||
149139
isNaN(lng) ||
150140
!isInRange(lat, -90, 90) ||
151141
!isInRange(lng, -180, 180)
152142
) {
153-
res.send({ error: "Invalid coordinates!" });
143+
return c.json({ error: "Invalid coordinates!" });
154144
} else if (days > 1000) {
155-
res.send({ error: "days can be maximum 1000!" });
145+
return c.json({ error: "days can be maximum 1000!" });
156146
} else {
157147
const place = findPlace(lat, lng);
158148
const times = getTimes(lat, lng, date, days, tzOffset, calculateMethod);
159-
res.send({ place, times });
149+
return c.json({ place, times });
160150
}
161151
}
162152

163-
async function getTimesForGPS(req: Request, res: Response) {
164-
const { lat, lng, lang } = getParamsForPlaceSearch(req);
153+
async function getTimesForGPS(c: Context) {
154+
const { lat, lng, lang } = getParamsForPlaceSearch(c);
165155
const { date, days, tzOffset, calculateMethod } =
166-
getCommonTimeRequestParameters(req);
156+
getCommonTimeRequestParameters(c);
167157
if (
168158
isNaN(lat) ||
169159
isNaN(lng) ||
170160
!isInRange(lat, -90, 90) ||
171161
!isInRange(lng, -180, 180)
172162
) {
173-
res.send({ error: "Invalid coordinates!" });
163+
return c.json({ error: "Invalid coordinates!" });
174164
} else if (days > 1000) {
175-
res.send({ error: "days can be maximum 1000!" });
165+
return c.json({ error: "days can be maximum 1000!" });
176166
} else {
177167
const [place] = await getNearbyPlaces(lat, lng, lang, 1);
178168
const times = getTimes(lat, lng, date, days, tzOffset, calculateMethod);
179-
res.send({ place, times });
169+
return c.json({ place, times });
180170
}
181171
}
182172

183-
async function searchPlaces(req: Request, res: Response) {
184-
const q = (req.query["q"] ?? "") as string;
173+
async function searchPlaces(c: Context) {
174+
const q = (c.req.query("q") ?? "") as string;
185175
const { lat, lng, lang, resultCount, countryCode } =
186-
getParamsForPlaceSearch(req);
187-
res.send(
176+
getParamsForPlaceSearch(c);
177+
return c.json(
188178
await getPlaceSuggestionsByText(
189179
q,
190180
lang,
@@ -196,88 +186,88 @@ async function searchPlaces(req: Request, res: Response) {
196186
);
197187
}
198188

199-
async function nearByPlaces(req: Request, res: Response) {
200-
const { lat, lng, lang, resultCount } = getParamsForPlaceSearch(req);
201-
202-
res.send(await getNearbyPlaces(lat, lng, lang, resultCount));
189+
async function nearByPlaces(c: Context) {
190+
try {
191+
const { lat, lng, lang, resultCount } = getParamsForPlaceSearch(c);
192+
const places = await getNearbyPlaces(lat, lng, lang, resultCount);
193+
return c.json(places);
194+
} catch (e) {
195+
console.error("nearByPlaces error:", e);
196+
return c.json({ error: String(e) }, 500);
197+
}
203198
}
204199

205-
function getPlaceData(req: Request, res: Response) {
206-
const lat = Number(req.query["lat"] as string);
207-
const lng = Number(req.query["lng"] as string);
200+
function getPlaceData(c: Context) {
201+
const lat = Number(c.req.query("lat") as string);
202+
const lng = Number(c.req.query("lng") as string);
208203
if (lat === undefined || lng === undefined || isNaN(lat) || isNaN(lng)) {
209-
res.send({ error: "INVALID coordinates!" });
204+
return c.json({ error: "INVALID coordinates!" });
210205
} else {
211-
res.send(findPlace(lat, lng));
206+
return c.json(findPlace(lat, lng));
212207
}
213208
}
214209

215-
async function getTimesForPlace(req: Request, res: Response) {
216-
const placeId = Number(req.query["id"]);
210+
async function getTimesForPlace(c: Context) {
211+
const placeId = Number(c.req.query("id"));
217212
if (Number.isNaN(placeId)) {
218-
res.send({ error: "Id should be a positive integer!" });
219-
return;
213+
return c.json({ error: "Id should be a positive integer!" });
220214
}
221215
const { date, days, tzOffset, calculateMethod } =
222-
getCommonTimeRequestParameters(req);
223-
const { lang } = getParamsForPlaceSearch(req);
216+
getCommonTimeRequestParameters(c);
217+
const { lang } = getParamsForPlaceSearch(c);
224218
const place = await getPlaceById(placeId, lang);
225219

226220
if (!place) {
227-
res.send({ error: "Place cannot be found!" });
221+
return c.json({ error: "Place cannot be found!" });
228222
} else if (days > 1000) {
229-
res.send({ error: "days can be maximum 1000!" });
223+
return c.json({ error: "days can be maximum 1000!" });
230224
} else {
231225
const lat = place.latitude;
232226
const lng = place.longitude;
233227
const times = getTimes(lat, lng, date, days, tzOffset, calculateMethod);
234-
res.send({ place, times });
228+
return c.json({ place, times });
235229
}
236230
}
237231

238-
async function placeById(req: Request, res: Response) {
239-
const placeId = Number(req.query["id"]);
232+
async function placeById(c: Context) {
233+
const placeId = Number(c.req.query("id"));
240234
if (Number.isNaN(placeId)) {
241-
res.send({ error: "Id should be a positive integer!" });
242-
return;
235+
return c.json({ error: "Id should be a positive integer!" });
243236
}
244-
const { lang } = getParamsForPlaceSearch(req);
237+
const { lang } = getParamsForPlaceSearch(c);
245238
const place = await getPlaceById(placeId, lang);
246239

247240
if (!place) {
248-
res.send({ error: "Place cannot be found!" });
241+
return c.json({ error: "Place cannot be found!" });
249242
} else {
250-
res.send({ ...place });
243+
return c.json({ ...place });
251244
}
252245
}
253246

254247
/**
255248
* DEPRECATED, use `getTimesForPlace`
256249
*/
257-
function getTimesFromPlace(req: Request, res: Response) {
258-
const country = req.query["country"] as string;
259-
const region = req.query["region"] as string;
260-
const city = req.query["city"] as string;
250+
function getTimesFromPlace(c: Context) {
251+
const country = c.req.query("country") as string;
252+
const region = c.req.query("region") as string;
253+
const city = c.req.query("city") as string;
261254
const place = getPlace(country, region, city);
262255
const { date, days, tzOffset, calculateMethod } =
263-
getCommonTimeRequestParameters(req);
256+
getCommonTimeRequestParameters(c);
264257
if (!place) {
265-
res.send({ error: "Place cannot be found!" });
258+
return c.json({ error: "Place cannot be found!" });
266259
} else if (days > 1000) {
267-
res.send({ error: "days can be maximum 1000!" });
260+
return c.json({ error: "days can be maximum 1000!" });
268261
} else {
269262
const lat = place.latitude;
270263
const lng = place.longitude;
271264
const times = getTimes(lat, lng, date, days, tzOffset, calculateMethod);
272-
res.send({ place, times });
265+
return c.json({ place, times });
273266
}
274267
}
275268

276-
function getIPAdress(req: Request, res: Response) {
277-
res.send({ IP: req.headers["x-forwarded-for"] });
269+
function getIPAdress(c: Context) {
270+
return c.json({ IP: c.req.header("x-forwarded-for") });
278271
}
279272

280-
function logIPAdress(_req: Request, _: Response, next: NextFunction) {
281-
next();
282-
}
283273
export default app;

0 commit comments

Comments
 (0)