Skip to content

Service Return

Thiago Bustamante edited this page Feb 18, 2019 · 7 revisions

Service Return

This library automatically handle the serialisation of the response of your methods. It takes care of content type headers and status codes to be sent.

When a primitive type is returned by a service method, it is sent as a plain text into the response body.

@GET
sayHello(): string {
  return "Hello";
}

The response will contains only the String Hello as a plain text

When an object is returned, it is sent as a JSON serialised string into the response body.

@GET
@Path(":id")
getPerson(@PathParam("id") id: number): Person {
  return new Person(id);
}

The response will contains the person JSON serialisation (ex: {id: 123}. The response will have a application/json context type.

When the method returns nothing, an empty body is sent with a 204 status code.

@POST
test(myObject: MyClass): void {
  //...
}

We provide also, some special types to inform that a reference to a resource is returned and that the server should handle it properly.

Type Description
NewResource Inform that a new resource was created. Server will add a Location header and set status to 201
RequestAccepted Inform that the request was accepted but is not completed. A Location header should inform the location where the user can monitor his request processing status. Server will set the status to 202
MovedPermanently Inform that the resource has permanently moved to a new location, and that future references should use a new URI with their requests. Server will set the status to 301
MovedTemporarily Inform that the resource has temporarily moved to another location, but that future references should still use the original URI to access the resource. Server will set the status to 302
import {Return} from "typescript-rest";

@Path("test")
class TestService {
   @POST
   test(myObject: MyClass, @ContextRequest request: express.Request): Return.NewResource<void> {
      //...
      return new Return.NewResource<void>(req.url + "/" + generatedId);
   }
}

The server will return an empty body with a 201 status code and a Location header pointing to the URL of the created resource.

It is possible to specify a body to be sent in responses:

import {Return} from "typescript-rest";

interface NewObject {
  id: string;
}

@Path("test")
class TestService {
   @POST
   test(myObject: MyClass, @ContextRequest request: express.Request): Return.NewResource<NewObject> {
      //...
      return new Return.NewResource<NewObject>(req.url + "/" + generatedId, {id: generatedId}); //Returns a JSON on body {id: generatedId}
   }

   @POST
   testWithStringBody(myObject: MyClass, @ContextRequest request: express.Request): Return.NewResource<string> {
      //...
      return new Return.NewResource<string>(req.url + "/" + generatedId, 'The body of the response');
   }
}

You can also use special return types to handle file downloads:

Type Description
DownloadResource Used to reference a resource (by its fileName) and download it
DownloadBinaryData Used to return a file to download, based on a Buffer object

For example:

import {Return} from "typescript-rest";

@Path("download")
class TestDownload {
  @GET
  testDownloadFile(): Return.DownloadResource {
    return new Return.DownloadResource(__dirname +'/test-rest.spec.js', '/test-rest.spec.js');
  }

  @GET
  testDownloadFile(): Promise<Return.DownloadBinaryData> {
    return new Promise<Return.DownloadBinaryData>((resolve, reject)=>{
      fs.readFile(__dirname + '/test-rest.spec.js', (err, data)=>{
        if (err) {
          return reject(err);
        }
        return resolve(new Return.DownloadBinaryData(data, 'application/javascript', 'test-rest.spec.js'))
      });
    });
  }
}

No Response

If you want to handle by yourself the server response, you can use NoResponse return value to tell typescript-rest that you don't want that it send any response to the client. Eg:

import {Return} from "typescript-rest";

@Path("noresponse")
class TestNoResponse {
  @GET
  public test() {
    return Return.NoResponse;
  }
}
app.use("/noresponse", (req, res, next) => {
  res.send("I am handling the response here, in other middleware");
});

or

import {Return} from "typescript-rest";

@Path("noresponse")
class TestNoResponse {
  @GET
  public test(@ContextResponse res: express.Response) {
    res.send("I am handling the response here, don't do it automatically");
    return Return.NoResponse;
  }
}

Asynchronous services

The above section shows how the types returned are handled by the Server. However, most of the previous examples are working synchronously. The recommended way is to work asynchronously, for a better performance.

To work asynchronously, you can return a Promise on your service method. The above rules to handle return types applies to the returned promise resolved value.

Some examples:

import {Return} from "typescript-rest";

@Path("async")
class TestService {
   @POST
   test(myObject: MyClass, @ContextRequest request: express.Request): Promise<Return.NewResource> {
      return new Promise<Return.NewResource>(function(resolve, reject){
         //...
         resolve(new Return.NewResource(req.url + "/" + generatedId));
      });
   }

   @GET
   testGet() {
      return new Promise<MyClass>(function(resolve, reject){
         //...
         resolve(new MyClass());
      });
   }
}

It is important to observe that you can inform your return type explicitly or not, as you can see in the above example.

You can also use async and await:

@Path('async')
export class MyAsyncService {
    @GET
    @Path('test')
    async test( ) {
        let result = await this.aPromiseMethod();
        return result;
    }

    @GET
    @Path('test2')
    async test2( ) {
        try {
            let result = await this.aPromiseMethod();
            return result;
        } catch (e) {
            // log error here, if you want
            throw e;
        }
    }

    private aPromiseMethod() {
        return new Promise<string>((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 10);
        });
    }
}