2

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.

7
  • have you consider using a library like axios? npmjs.com/package/axios this will help there to set a base url. Commented May 23, 2020 at 15:37
  • Would you be amicable to changing the interface up a bit? Let's say making users a function that takes your route (or nothing): api.users("checkEmail") or api.users() Commented May 23, 2020 at 15:39
  • @Pablo CG I know axios, but I use fetch method. I do not want to use such a solution. I want to call an object with default value Commented May 23, 2020 at 15:39
  • @Nick checkEmail is a function Commented May 23, 2020 at 15:40
  • An object cannot be a string. Using .base is the proper solution. You might alternatively put a .toString() method on it, but it'll be unclear what that refers to. Commented May 23, 2020 at 15:43

3 Answers 3

3

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'})
Sign up to request clarification or add additional context in comments.

Comments

1

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
};

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.