Sharing API endpoints across client and server
Recently I was working on a demo project with React/TypeScript as front-end (running on port 5173) and Express as backend (running on port 5000). Front end codes is built into a static folder and served by Express.
I’m using Vite, and I’ve set up a proxy inside Vite, so front end codes can just use /api/2fa/enable
instead of needing to pass the whole server url like http://localhost:5000
.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api": {
target: "http://localhost:5000",
changeOrigin: true,
},
},
},
});
Although there is still a tiny issue I want to improve, that is a single API endpoint will first be defined by server and then called by client, something like this:
// Two-Factor Authentication
app.post('/api/2fa/enable', enableTwoFactorAuth);
app.post('/api/2fa/verify', verifyTwoFactorAuth);
app.post('/api/2fa/disable', disableTwoFactorAuth);
app.post('/api/2fa/login', loginWithTwoFactorAuth);
// ...
const fetchQRCode = async () => {
const res = await fetch('/api/2fa/enable', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username: user.username }),
});
// ...
};
It works ok, but it would be nice if we can creat a shared apiRoutes.json
file to store all API routes which then can be consumed by client and server, and that is exactly what I did, and it does make things a bit easier whenever I want to update the endpoint as I only need to update apiRoutes.json
file once.
{
"TWO_FACTOR_AUTH_ENABLE": "/api/2fa/enable",
"TWO_FACTOR_AUTH_VERIFY": "/api/2fa/verify",
"TWO_FACTOR_AUTH_DISABLE": "/api/2fa/disable",
"TWO_FACTOR_AUTH_LOGIN": "/api/2fa/login"
// ... other routes
}
// As of the time I wrote this blog, importing JSON modules is an experimental
// feature and might change at any time
import apiRoutes from "./shared/apiRoutes.json" assert { type: "json" };
// Two-Factor Authentication
app.post(apiRoutes.TWO_FACTOR_AUTH_ENABLE, enableTwoFactorAuth);
app.post(apiRoutes.TWO_FACTOR_AUTH_VERIFY, verifyTwoFactorAuth);
app.post(apiRoutes.TWO_FACTOR_AUTH_DISABLE, disableTwoFactorAuth);
app.post(apiRoutes.TWO_FACTOR_AUTH_LOGIN, loginWithTwoFactorAuth);
// ...
import apiEndpoints from "../../shared/apiRoutes.json";
const fetchQRCode = async () => {
const res = await fetch(apiEndpoints.TWO_FACTOR_AUTH_ENABLE, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username: user.username }),
});
// ...
};