Skip to content

Standard Response Object

andreabbondanza edited this page Jan 13, 2023 · 2 revisions

Standard Response object details

Reason

Usually when we communicate between server and client we have these problems:

  1. We must understand if the output is an error or not;
  2. We must read a message or data in both ways;
  3. We must treat development and productino in differents way.

The StandardResponse object help us to face these problems.

export interface IStandardResponse<T> {
  // this is the one you want to sho into the UI
  Message: string;
  // the data
  Data: T;
  // for dev purposes
  Error: { Num: number; Desc: string };
}

NOTE: I usually use this object with capitalized notation to distinguish it from the rest of the models.

Client Side

The idea is that when a client make a request to a server, it will always respond with the standard object. This way, you can treat the answer in the same way for every api you call.

But with some example everything can be much clear:

class TokenResponse {
  public token: string = "";
  public refreshToken: string = "";
}

class Example {
  public async Login(body: {
    mail: string;
    pwd: string;
  }): Promise<StandardResponse<TokenResponse> | null> {
    const response = await this._client.post<TokenResponse>(
      "public/auth/login",
      body
    );
    return response;
  }
}

async function login() {
  const example = new Example();
  const mail = document.getElementById("mail").text;
  const pwd = document.getElementById("pwd").text;
  try {
    const response = await example.login({ mail, pwd });
    localStorage.set("token", response.data.token);
    localStorage.set("r_token", response.data.refreshToken);
    alert(response.message);
  } catch (err) {
    // axios error object
    alert(err.response.data.message);
    // for dev purpose
    console.log(err.response.error.Desc);
  }
}

from the code you can note that:

  • Every Time (that server works) you will get the standard response. So you don't care if it's about an error or everything is ok. You know that message you'll get is a StandardResponse object.
  • The type data inside a StandardResponse can be definite via generics. This way you can use the intellisense.
  • From the server for dev purpose, you can fill the error object with exceptions details.

Server Side

After the client, let's se how to use the StandardRepsonse object inside the server. We'll use our login endpoint:

export class LoginResponse
{
    public auth_token: string = "";
    public refresh_token: string = "";
}

...

 public login(): Route
    {
        return this._registerEndpoint("/login", ["public"], "post")
            .endpoint(
                async (req, res) =>
                {
                    /// init the standard response object
                    const response: IStandardResponse<LoginResponse> = initSR();
                    try
                    {
                        const authService = this.initService<AuthService>(new AuthService());
                        const secret = this.env.configHost.app.secret;
                        const body = req.body;

                        if (!body) return res.status(400).send(initSR({ Message: "Invalid data", Error: { Num: 400, Desc: "Body unsupported" } }));
                        if (!body.email || !REGEX_EMAIL.test(body.email)) return res.status(400).send(initSR({ Message: "Invalid email", Error: { Num: 400, Desc: "Bad Email" } }));
                        if (!body.pwd) return res.status(400).send(initSR({ Message: "Invalid password", Error: { Num: 400, Desc: "Bad Password" } }));
                        const pwd: string = body.pwd;
                        const hash = crypto.createHash(this.env.configHost.encryption.algorithm).update(pwd.trim()).digest(this.env.configHost.encryption.encoding);
                        const user = await authService.login(body.email, hash);
                        if (user)
                        {
                            response.Data = authService.tokenGeneration(user, secret);
                            res.status(200).send(response);
                        }
                        else
                        {
                            response.Message = "User not found";
                            res.status(404).send(response);
                        }
                    } catch (err)
                    {
                        this.log.fileLog(JSON.stringify(err));
                        response.Error.Desc = (err as any).message;
                        res.status(500).send(response);
                    }
                }).route;
    }

You can see in the code above we init the response of type StandardResponse<LoginResponse>. After this, we check before the body, the email and the password and, if one of them is invalid, we'll return the 404 status with the StandardObject with error message (in this case also the Error like example).

Note that the StandardResponse is returned even for status 500 error from the server.

In the case of current inputs, we'll return the LoginResponse object into the StandardResponse.

In everyone of these case, the client knows that it's waiting for a StandardObject.

NOTE: You can find the StandardResponse also in the middlewares

Clone this wiki locally