Rishi Prasad
Rishi Prasad

Reputation: 69

Node.js - TypeError: res.json is not a function (working in some routes and not in others)

Working in the MERN stack (Mongo, Express, React, Node) and running into an error in my API.

Here is my plaid.js file where one of my routes is throwing this error. Of course, I've removed all of my secret token variables but assume everything works until the res.json error (which it does).

        const express = require("express");
        const plaid = require("plaid");
        const router = express.Router();
        const jwt = require("jsonwebtoken");
        const keys = require("../../config/keys");
        const passport = require("passport");
        const moment = require("moment");
        const mongoose = require("mongoose");

        // Load Account and User models
        const Account = require("../../models/Account");
        const User = require("../../models/User");

        // Replaced my actual keys with empty strings for sake of this post
        const PLAID_CLIENT_ID = "";
        const PLAID_SECRET = "";
        const PLAID_PUBLIC_KEY = "";

        const client = new plaid.Client(
          PLAID_CLIENT_ID,
          PLAID_SECRET,
          PLAID_PUBLIC_KEY,
          plaid.environments.sandbox,
          { version: "2018-05-22" }
        );

        var PUBLIC_TOKEN = null;
        var ACCESS_TOKEN = null;
        var ITEM_ID = null;

        // @route POST api/plaid/accounts/add
        // @desc Trades public token for access token and stores credentials in database
        // @access Private
        router.post(
          "/accounts/add",
          passport.authenticate("jwt", { session: false }),
          (req, res) => {
            PUBLIC_TOKEN = req.body.public_token;

            const userId = req.user.id;
            const institution = req.body.metadata.institution;
            const { name, institution_id } = institution;

            if (PUBLIC_TOKEN) {
              client
                .exchangePublicToken(PUBLIC_TOKEN)
                .then(res => {
                  ACCESS_TOKEN = res.access_token;
                  ITEM_ID = res.item_id;

                  // Check if account already exists for specific user
                  Account.findOne({
                    userId: req.user.id,
                    institutionId: institution_id
                  })
                    .then(account => {
                      if (account) {
                        console.log("Account already exists");
                      } else {
                        const newAccount = new Account({
                          userId: userId,
                          publicToken: PUBLIC_TOKEN,
                          accessToken: ACCESS_TOKEN,
                          itemId: ITEM_ID,
                          institutionId: institution_id,
                          institutionName: name
                        });

                        // TO:DO fix error, res.json is not a function
                        newAccount.save().then(account => res.json(account));
                      }
                    })
                    .catch(err => console.log(err)); // Mongo Error
                })
                .catch(err => console.log(err)); // Plaid Error
            }
          }
        );
    module.exports = router;

The newAccount.save() executes just fine, but the subsequent res.json throws an error. This is the error that I am getting returned. Namely, res.json is not a function.

[0] (node:23413) UnhandledPromiseRejectionWarning: TypeError: res.json is not a function
[0]     at newAccount.save.then.account (/Users/rishi/plaid-auth/routes/api/plaid.js:97:55)
[0]     at process._tickCallback (internal/process/next_tick.js:68:7)

There are quite a few posts on here with the res.json is not a function error, but none of the proposed solutions worked for me. I am quite confused why this error is being thrown since I use the same convention in another part of the app and res.json works just fine. See the below example of where res.json works.

// @route POST api/posts
// @desc Create a post
// @access Private
router.post(
  "/",
  passport.authenticate("jwt", { session: false }),
  (req, res) => {
    const { errors, isValid } = validatePostInput(req.body);

    // Check validation
    if (!isValid) {
      return res.status(400).json(errors);
    }

    const newPost = new Post({
      text: req.body.text,
      name: req.body.name,
      avatar: req.body.avatar,
      user: req.user.id // current logged in user
    });

    // res.json works just fine
    newPost.save().then(post => res.json(post));
  }
);

Upvotes: 0

Views: 4865

Answers (1)

It's because this piece of code

if (PUBLIC_TOKEN) {
          client
            .exchangePublicToken(PUBLIC_TOKEN)
            .then(res => {
              ACCESS_TOKEN = res.access_token;

When you get to execute this line newAccount.save().then(account => res.json(account)); the res is not longer the one on the function router.post('/accounts/add', (req, res) => {})

So, the solution is simple, change the res from the promise exchangePublicToken into something else like the example below.

You are naming the parameter from the promise callback the same as the parameter on the callback from the router.post above

if (PUBLIC_TOKEN) {
          client
            .exchangePublicToken(PUBLIC_TOKEN)
            .then(exchangeResponse => {
              ACCESS_TOKEN = exchangeResponse.access_token;

Upvotes: 1

Related Questions