tmpl-cli
api
Summaryapi [name]api [name] -t
docs
env
git
hbs
html
husky
jest
lintstaged
lisp
node
prettier
python
react
readme
schema
style
util
vue
web

$ tmpl api [name] -t

Creates an interface for talking with a rest endpoint. It will generate a file that uses fetch with a try/catch wrapper, schema validation and logging.

Force Typescript.

Files

project
└─src
└──api
└───__tests__
└────foo.test.ts
└───foo.ts

foo.test.ts

// Recomendation: Wire node-fetch into your jest setup
import fetch from "node-fetch";
//@ts-ignore
window.fetch = fetch;
import nock from "nock";
import { fetchResource, fooDomain } from "../foo";
describe("API foo", () => {
describe("fetchData()", () => {
it("should have success status with expected data", async () => {
// Arrange
const mockRequest = { some: "input" };
const mockResponse = { id: 1, value: "foobar" };
nock(fooDomain)
.post("/endpoint", mockRequest)
.reply(200, mockResponse);
// Act
const result = await fetchResource(mockRequest);
// Assert
expect(result.status).toEqual("SUCCESS");
expect(result.data).toEqual(mockResponse);
});
it("should have invalid status for invalid data", async () => {
// Arrange
const mockRequest = { some: "input" };
const mockResponse = { id: 1 };
nock(fooDomain)
.post("/endpoint", mockRequest)
.reply(200, mockResponse);
// Act
const result = await fetchResource(mockRequest);
// Assert
expect(result.status).toEqual("INVALID");
expect(result.data).toBeUndefined();
});
it("should have error status with undefined data", async () => {
// Arrange
const errorRestore = console.error;
console.error = () => {};
const mockRequest = { some: "input" };
nock(fooDomain)
.post("/endpoint", mockRequest)
.reply(500);
// Act
const result = await fetchResource(mockRequest);
// Assert
expect(result.status).toEqual("ERROR");
expect(result.data).toBeUndefined();
// Cleanup
console.error = errorRestore;
});
});
});

foo.ts

import { response } from "express";
import Schema from "validate"
export const fooDomain = "https://some-domain.com";
interface IDataType {
id: number,
value: string
}
const dataSchema = new Schema({
id: { "type": Number, required: true },
value: { "type": String, required: true }
})
export const fetchResourceValidate = (response: Response) => {
try {
const unknown: any = await response.json();
if (dataSchema.validate(unknown).length > 0) {
return { data: undefined, status: "INVALID" };
}
return { data: unknown as IDataType, status: "SUCCESS" };
} catch(err) {
return { data: undefined, status: "ERROR" };
}
}
export const fetchResource = async (payload: any) => {
try {
const response = await fetch(`${ fooDomain}/endpoint`, {
method: "post",
body: JSON.stringify(payload),
headers: { "Content-Type": "application/json" }
});
if (response.statusCode >= 400) {
const body = await response.text();
throw Error(`Error talking to service - ${response.statusCode} - ${body});
}
return fetchResourceValidate(response);
} catch (err) {
console.error(err);
return { data: undefined, status: "ERROR" };
}
};