Ferdinand
Ferdinand

Reputation: 93

NodeJS Rest API - where is the right place to call external API


I'm writing Rest API in Node.JS which use MySQL database but also external API, where I need fetch some data.

I'm using Express and "Router, Middleware, Controller, Model" Architecture and I'n not sure what is the right solution to call an external API. In every request I'm sending token that is required for external API. I show what I have now and try describe the problem what I currently have (Read comments in code please.)

(Also if you have some articles or tutorials where is describe how right write Rest API in node thats uses Router, Middleware, Controller, Model architecture please let me know)

This is the main index.js

const express = require("express");
const dotenv = require('dotenv');
const cors = require("cors");
const HttpException = require('./utils/HttpException.utils');
const errorMiddleware = require('./middleware/error.middleware');
const userRouter = require('./routes/user.route');
const axios = require("axios");

// Init express
const app = express();
// Init environment
dotenv.config();
// parse requests of content-type: application/json
// parses incoming requests with JSON payloads
app.use(express.json());
// enabling cors for all requests by using cors middleware
app.use(cors());
// Enable pre-flight
app.options("*", cors());

const port = Number(process.env.PORT || 3331);

app.use(`/api/v1/users`, userRouter);

// This is an solution that works but I thinks is and nasty way how to do it
// You can see here how I need to call external API
app.get('/api/v1/test',  (req, res, next) => {
    const token = req.headers.token;
    
    const respond = await axios.get(EXTERNAL_API_ENDPOINT, {
        headers: {
            cookie: `token=${token}`
        }
    });
});

// 404 error
app.all('*', (req, res, next) => {
    const err = new HttpException(404, 'Endpoint Not Found');
    next(err);
});

// Error middleware
app.use(errorMiddleware);

// starting the server
app.listen(port, () =>
    console.log(`Server running on port ${port}!`));


module.exports = app;

user.route.js

const express = require('express');
const router = express.Router();
const userModel = require('../models/user.model');
const awaitHandlerFactory = require('../middleware/awaitHandlerFactory.middleware');

router.get('/currentUser', awaitHandlerFactory(userModel.getCurrentUser));

router.get('/logout');
module.exports = router;

I also have an Auth middleware to check token validation where i need to call external API which validate user.

Auth.middleware.js

const HttpException = require('../utils/HttpException.utils');
const UserModel = require('../models/user.model');
const dotenv = require('dotenv');
dotenv.config();

const auth = (roles) => {
    return async function (req, res, next) {
        try {
            const token = req.headers.token;

            if (!token) {
                throw new HttpException(401, 'Access denied. No credentials sent!');
            }

            /* here I need call the external API and think that I should call it from 
               UserModal?, but I can't because Modal doesn't have req (should I sent it 
               in function parmas? like this?)*/

            const user = await UserModel.getCurrentUser(token, params);

            if (!user) {
                throw new HttpException(401, 'Authentication failed!');
            }

            if(!user.active || user.active !== 'Y'){
                throw new HttpException(401, `User ${user.userName} is not active!`);
            }

            // if the user role don't have the permission to do this action.
            // the user will get this error
            if (roles.length && !roles.includes(user.role)) {
                throw new HttpException(401, 'Unauthorized');
            }

            // if the user has permissions
            req.currentUser = user;
            next();

        } catch (e) {
            e.status = 401;
            next(e);
        }
    }
}

module.exports = auth;

I`m not sure how to handle this. I have to provide token and some data to call external API. Im not sure, if I shloud call Model or do it by Controller (or middleware?). Where and how I should do it and why? Thanks!

Upvotes: 2

Views: 4012

Answers (1)

Spankied
Spankied

Reputation: 1895

Looks alright to me. Really depends on whether or not the API call needs to be made every request. Let say the user makes a request to your API, which then makes a request to an external API to authenticate that user, you're gonna need the auth token from every request.

You can organize your code a little better though. As example:

📦api

┣ 📂controllers

┃ ┗ 📜test.js

┣ 📂services

┃ ┗ 📜EXTERNAL_API_ENDPOINT.js

┗ 📜index.js

api/services/EXTERNAL_API_ENDPOINT.js

const axios = require("axios");

async function getService(token) {
    return axios.get(EXTERNAL_API_ENDPOINT, {
        headers: { cookie: `token=${token}` }
    });
}
module.exports = getService;

api/controllers/test.js

const getService = require("../services/EXTERNAL_API_ENDPOINT");

async function test(req, res, next) {
    const token = req.headers.token;
    const respond = await getService(token)
}
module.exports = test;

api/index.js

const test = require("./controllers/test");

app.get('/api/v1/test',  test);

Upvotes: 1

Related Questions