How to Serve Both API Website and API Under the Same URL

Wenhe Qi
3 min readJul 24, 2020

I’m currently working on an API website that provides users with APIs to retrieve movie information in JSON format. The very first challenge I met when designing the overall architecture was how can I serve my front-end web pages together with backend APIs under the same URL, i.e. if a user enters

https://www.dummy.com/

the server will return the website home page so user can signup for a free API key/ browse document to learn how to use the API; if a user enters

https://www.dummy.com/?key=API_KEY&field1=value1&field2=value2

the server will return corresponding JSON data/error message based on the query strings user supplies.

My backend uses ExpressJS. After going through the official documentation and messing with different methods/variables, I finally accomplished this feature.

First, we can NOT simply specify a ? (question mark) in the mount path and expect it to work like a charm. To be more specific, following code doesn’t work.

// serve the static website from public folder
app.use("/", express.static(path.join(__dirname, "public")));
app.get("/?", (req, res) => { // retrieve user requested data and send back using res})

The reason is that ? (question mark) in URL signifies the beginning of query strings, not for routing purpose, while using in mount path in ExpressJS, it acts as a regex operator which means “once or none” in path pattern matching. BTW, even if you escape the question mark with a \(backslash), it still won’t work as you expect.

Then how can we achieve this? The answer is middleware function.

Quoting from the ExpressJS website:

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

After mount the middleware function to a path in app/route, it is executed every time the app/route receives a request on the specified path. If the middleware doesn’t end the request-response cycle, it will (actually it’s a MUST) call next() to pass control to the next middleware function.

Now the idea should be pretty straightforward: use middleware function to check if user has specified some query strings in the URL, if yes, retrieve corresponding JSON data or return an error message base on query string and end the request-response cycle. Otherwise call next() to serve the web pages.

Let’s see what we can get with above code.

Sweet! It works!

Final Thoughts

As I mentioned in above code, the order matters: the middleware function should come before serving the static web pages. Otherwise the middleware function won’t work.

Another thing I want to mention is that I used router(mini application) instead of working on app directly to implement this feature because it’s easier to maintain the code if I decide to mount the API on a different path later.

--

--

Wenhe Qi

A lifelong learner who has enthusiasm for sharing knowledge; a developer who loves bringing ideas to life.