How to validate async api response with Typescript - reactjs

How do I validate my API response when storing it as an object. For example:
const { data } = await axios.get("/form");
How do I make sure this data conforms to:
interface Form {
title: string;
questions: Question[]
}

Typescript does not perform any type of validations out of the box like java or other strong typed languages.
Things like
const { data } = await axios.get<Form>("/form");
does not guarantee that the response data is of Form type. It is just used for static typing checks (no errors/validation in runtime). For having a guarantee I would recommend the implementation of a guard function (https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) with some JSON schema validation. For example, you can use
https://www.npmjs.com/package/ajv or https://www.npmjs.com/package/joi package to do that.

Use:
const { data } = await axios.get<Form>("/form");
If you want to make sure that the incoming data is indeed compatible with Form interface, you have to do it in runtime (thus can't be done with TS). Checking runtime data is compatible with a type isn't always straightforward or easy (especially with nested data). io-ts is one of the libraries that does it.

Related

Can you use aws-sdk/util-dynamodb unmarshall in Reactjs?

I have an API Gateway resource which calls a Dynamodb post request to query a table. I'm trying to call the API within my React app using Axios. The API is working, returning the data as expected (console log) but I'm getting errors if I try to use #aws-sdk/util-dynamodb (unmarshall) to convert the api data.items response into JSON and use within React.
./node_modules/#aws-sdk/util-dynamodb/dist-es/convertToNative.js 45:14 Module parse failed: Unexpected token (45:14)
Is there a way to use 'unmarshall' within React? Something like this:
useEffect(() => {
const getGoal = async () => {
try {
const response = await api.get(apiUrl)
setGoal(unmarshall(response.data.Items))
This works if I use a Lambda service, but I'm try to see if I can reduce my code.
While I'm not sure on the issue you are getting with unmarshall function, I would suggest using the DynamoDB Document Client which will return your data already unmarshalled.
The DynamoDB Document client simplifies working with items by abstracting the notion of attribute values. This abstraction annotates native JavaScript types supplied as input parameters, and converts annotated response data to native JavaScript types.

How to store data in this very simple React Native app?

I'm developing an app using React Native that allows you to create your own checklists and add items to them.
For example you'd have "Create Checklist", and inside that you'll have the option to "Add Item", "Delete Item" "Edit Item", basic CRUD methods etc.
It's going to be completely offline but I'm wondering what the best approach to storing this data locally would be.
Should I be using a DB such as firebase? I have read that it is overkill and to use something like Redux but I'm not sure if the latter will accomplish everything I need. As long as it's storing data which can be edited, and will save on the user's device (with minimal effort) it sounds good to me.
Would appreciate some input on this, thanks!
You could use AsyncStorage for persisting data locally on the user's phone. It is a simple persistent key-value-storage.
Each checklist is most likely an array of JS objects. The documentation provides an example on how to store objects.
const storeData = async (value) => {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem('#storage_Key', jsonValue)
} catch (e) {
// saving error
}
}
The value parameter is any JS object. We use JSON.stringify to create a JSON string. We use AsyncStorage.setItem in order to persist the data. The string #storage_Key is the key for the object. This could be any string.
We retrieve a persisted object as follows.
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#storage_Key')
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch(e) {
// error reading value
}
}
Both examples are taken from the official documentation.
Keep in mind that this functionality should be used for persistence only. If the application is running, you should load the complete list, or parts of the list if it is very large, in some sort of application cache. The implementation for this functionality now heavily depends on how your current code looks like. If you have a plain view, then you could access the local storage in an effect and just store it in a local state.
function MySuperList() {
const [list, setList] = useState([]);
React.useEffect(() => {
// retrieve data using the above functionality and set the state
}, [])
// render list
return (...)
}
I would implement some sort of save button for this list. If it is pressed, then we persist the data in the local storage of the phone.

What type should be in FuriaError variable

async Furia(URL) {
try {
const Res = await fetch(URL);
const Furia0= await Res.json();
return Furia0;
} catch (FuriaError) { //type FuriaError?
return FuriaError;
}
}
What type should be in FuriaError variable?.
It seems to be not possible with TypeScript.
We don't allow type annotations on catch clauses because there's really no way to know what type an exception will have. You can throw objects of any type and system generated exceptions (such as out of memory exception) can technically happen at any time. Even if we had a Java-like notion of throws annotations it is not clear that we could ever really rely on them. (And, it is also not clear that they work all that well in Java, but that's another discussion.)
Also look here, where all related github issues are linked.

How to synchronize object types sent from Web API backend to React/Redux frontend

I have written a backend for my app on .net, that provides me CRUD operations.
For example, the response for POST action is whole new item object:
{"Id":"7575c661-a40b-4161-b535-bd332edccc71","Text":"as","CreatedAt":"2018-09-13T15:29:52.7128732+00:00","LastChange":"2018-09-13T15:29:52.7128732+00:00"}
Now I need to use this Id in my frontend (to rewrite a temporary id I made on frontend until I receive confirmation that the upload was successful).
The problem comes with typescript when I type my thunk action "as I think it's right":
export const uploadItem = (fetch: (text: string) => Promise<Item>) =>
(generateId: () => ItemId) =>
(text: string) =>
async (dispatch: Dispatch<ThunkAction<IAction, IAppState, void>>): Promise<IAction> => {
const id = generateId();
try {
dispatch(addItem(id, text));
const itemWithOfficialId = await fetch(text);
dispatch(synchronizeItemId(id, itemWithOfficialId.id));
return dispatch(setAsSynchronized(itemWithOfficialId.id));
} catch {
return dispatch(requestFailedForItem(id, errorMessageTypes.UPLOAD, 'Failed to upload. '));
}
};
The problem I see is on the first line:
fetch: (text: string) => Promise<Item>
that forces me to do:
itemWithOfficialId.id
because my Item has only property id and I need to extract Id.
But typescript won't let me change it to itemWithOfficialId.Id.
I know I could make a new Item model, that would consist of properties returned from the server and use it like Promise<ItemFromServer>, but it feels a bit cheesy to me and I wanted to know if there was another way how to convert these without creating a new representation of the same object?
If you have two objects with different sets of properties, even if the property names differ only in case, I don't think you can get around declaring two different types. However, there are tools such as Typewriter that you may be able to use to generate the ItemFromServer declaration automatically from your server code.

Is there a convention for Flux messages sent via the Dispatcher?

I'm building my first React front end and see a number of conventions for messages sent via the Dispatcher. e.g.
{
type: ActionTypes.RECEIVE_RAW_MESSAGES,
rawMessages: rawMessages
}
https://github.com/facebook/flux/blob/master/examples/flux-chat/js/actions/ChatServerActionCreators.js#L21
and
{
source: 'VIEW_ACTION',
action: action
}
http://facebook.github.io/react/blog/2014/09/24/testing-flux-applications.html#putting-it-all-together
What is the best message format to use & why?
The short answer is, it probably doesn't really matter—as long as your stores look for the right data. I always use the following format:
{
type: 'ACTION_TYPE', // usually defined by a constant
payload: { ... } // a payload of JSON serializable types
}
If your app needs to distinguish between actions that are initiated by the user and actions that come from the server or some other source, you may considering adding a source key; I personally use separate action types or data within the payload for this purpose.
I always make payload an object (never a raw value) so that data can be added easily without changing receiving sites. For example, instead of
dispatch({type: ACTION_TYPE, payload: id})
I would recommend
dispatch({type: ACTION_TYPE, payload: {id: id}})
Of course, some of this may be dictated by which flux implementation (if any) that you use. The Facebook dispatcher is very agnostic (you can send pretty much anything you want), but some implementations require specific keys (like type, etc).
Flux Standard Action is a project to standardize Flux actions.
In short, an action must have a type, and may have a error, payload, or meta.
Examples
{
type: 'ADD_TODO',
payload: {
text: 'Do something.'
}
}
For an error:
{
type: 'ADD_TODO',
payload: new Error(),
error: true
}
The meta field is indented for "any extra information that is not part of the payload". I haven't seen this used, so I'd try to stick to payload.

Resources