reactdto2
reactdto2

Reputation: 123

Default string value after call the object in JavaScript

I have a js object in which I return my endpoint addresses from api. This is a very nice solution for me, it looks like this:

export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';

export default {
  users: {
    checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
    notifications: `${API_BASE_URL}${USERS}/notifications`,
    messages: `${API_BASE_URL}${USERS}/messages`,
  },
};

Now I can call this address in my redux-saga to execute the xhr query:

import { api } from 'utils';

const requestURL = api.users.notifications; 

But I'm a bit stuck because now I have a problem - base path is missing here: '/users'.

Now when I call api.users, then I get a object. I would like to have a default value after calling the object like:

import { api } from 'utils';

const requestURL = api.users; // http://localhost:3000/Users
const requestURL2 = api.users.notifications; // http://localhost:3000/Users/notifications

I know that I could add a new string with the name 'base' to the object and add '/Users' there, but I don't like this solution and I think, there is a better solution.

Upvotes: 2

Views: 152

Answers (3)

rockTheWorld
rockTheWorld

Reputation: 501

You can extend prototype to achieve this behaviour:

export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';

const users = `${API_BASE_URL}${USERS}`

const baseUrls = {
  checkEmail: (email) => `${users}/${email}/checkEmail`,
  notifications: `${users}/notifications`,
  messages: `${users}/messages`,
}

Object.setPrototypeOf(users.__proto__, baseUrls);

export default {
  users
};

Upvotes: 1

ehab
ehab

Reputation: 8044

You could do one of the following:

extend the String class

const API_BASE_URL = "http://localhost:3000"
const USERS = "/Users"

class UsersEndpoints extends String {
  constructor(base) {
    super(base)
  }
  // this is still a proposal at stage 3 to declare instance variables like this
  // if u want a truly es6 way you can move them to the constructor
  checkEmail = (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
  notifications = `${API_BASE_URL}${USERS}/notifications`
  messages = `${API_BASE_URL}${USERS}/messages`
}

// you can use userEndpoints itself as a string everywhere a string is expected
const userEndpoints = new UsersEndpoints(API_BASE_URL)
export default {
   users: userEndpoints
}

The previous is just actually equivalent to

...
const userEndpoints = new String(API_BASE_URL)
userEndpoints.notifications = `${API_BASE_URL}${USERS}/notifications`
...

Obviously this is not recommended: you should not extend native classes, there are many disadvantages to this approach. An obvious example is that there could be a conflict between the properties you use and the properties that might be brought by the native class

override the toString method

...
export default {
  users: {
    checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
    notifications: `${API_BASE_URL}${USERS}/notifications`,
    messages: `${API_BASE_URL}${USERS}/messages`,
    toString: () => API_BASE_URL
  },
};
// this is actually not much different than the previous method, since a String is an objet with an overridden toString method.
// That said this method is also not recommended since toString is used in many places in native code, and overriding it just to substitute a string value will make information get lost in such places, error stacks for example

Achieve what u want using the language features intended for such a use case

What you are asking is to make the same variable to have different values in the same time, which is not possible in the language syntax, and it makes sense because it makes it hard to reason about code.

that being said i recommend something of the following nature

// it is also better to use named exports
export const getUsersEndpoint = ({
  path = "",
  dynamicEndpointPayload = {},
} = {}) => {
  switch (path) {
    case "notifications":
      return `${API_BASE_URL}${USERS}/notifications`
    case "messages":
      return `${API_BASE_URL}${USERS}/messages`
    case "checkEmail":
      return `${API_BASE_URL}${USERS}/${dynamicEndpointPayload.email}/checkEmail`
    // you still can do checkEmail like this, but the previous is more consistent
    // case "checkEmail":
    //   return (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
    default:
      return `${API_BASE_URL}`
  }
}

// you can use it like this
getUsersEndpoint() // returns the base
getUsersEndpoint({path: 'notifications'})

Upvotes: 3

LogicBlower
LogicBlower

Reputation: 1350

Try having object will all user endpoint and a function that return a value of a end point

    const user = {
    default: '/users',
    notification: '/notification',
    profile: '/profile',
    getEndPoint(prop) {
    if(this[prop] === 'default' ){
        return this[prop];
     }   else {
        if(this[prop]) {
            return  this.default + this[prop];
        }
     }

     }
   }

So you can have more end points that come under user and you can simply call

const requestURL = api.user.getEndPoint('default'); // http://localhost:3000/Users
const requestURL2 = api.user.getEndPoint('notifications'); // http://localhost:3000/Users/notification

Upvotes: 0

Related Questions