I'm using this Amplify guide https://aws-amplify.github.io/docs/js/tutorials/building-react-native-apps/#connect-to-your-backend-1 and when I create an API using "aplify add api" the app fails.
I'm using "expo" and I'm using IphoneX for test phase.
My app code is
import React, { Component } from 'react';
import { StyleSheet, Text, Button, View, Alert } from 'react-native';
import Amplify, { API } from 'aws-amplify';
import amplify from './aws-exports';
import awsmobile from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react-native';
Amplify.configure(amplify);
Amplify.configure(awsmobile);
state = { apiResponse: null };
class App extends Component {
async getSample() {
const path = "/items"; // you can specify the path
const apiResponse = await API.get("theListApi" , path); //replace the API name
console.log('response:' + apiResponse);
this.setState({ apiResponse });
}
render() {
return (
<View style={styles.container}>
<Text>test</Text>
<Button title="Send Request" onPress={this.getSample.bind(this)} />
<Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
</View>
);
}
}
export default withAuthenticator(App);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#63C8F1',
alignItems: 'center',
justifyContent: 'center',
},
});
executing "expo start" the command line return this message error:
jest-haste-map: Haste module naming collision: theListFunction
The following files share their name; please adjust your hasteImpl:
* <rootDir>/amplify/backend/function/theListFunction/src/package.json
* <rootDir>/amplify/#current-cloud-backend/function/theListFunction/src/package.json
Failed to construct transformer: DuplicateError: Duplicated files or mocks. Please check the console for more info
at setModule (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:620:17)
at workerReply (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:691:9)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 391) {
mockPath1: 'amplify/backend/function/theListFunction/src/package.json',
mockPath2: 'amplify/#current-cloud-backend/function/theListFunction/src/package.json'
}
(node:1506) UnhandledPromiseRejectionWarning: Error: Duplicated files or mocks. Please check the console for more info
at setModule (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:620:17)
at workerReply (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:691:9)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 391)
(node:1506) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:1506) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
or
Error: Duplicated files or mocks. Please check the console for more info
at setModule (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:620:17)
at workerReply (/Users/j_hen/Documents/jdev/smartApp/sourcecode/mySmaertProject/node_modules/jest-haste-map/build/index.js:691:9)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 391)
What is wrong? How can I use the API correctly?
Amplify creates a copy of your current cloud backend configuration in amplify/#current-cloud-backend/.
You don't need those files to build your app, so you can ignore them in order to get rid of the error.
To do so, you can create a blacklist and add the folder to it.
Create a rn-cli.config.js file in the root of your project.
./rn-cli.config.js:
// works with older react native versions
// const blacklist = require('metro').createBlacklist;
const blacklist = require('metro-config/src/defaults/blacklist');
module.exports = {
resolver: {
blacklistRE: blacklist([/#current-cloud-backend\/.*/]),
},
};
Reference issue.
TypeScript considerations:
(As stated in Mush's answer)
If you are using typescript you should create the blacklist on
metro.config.js NOT rn-cli.config.js.
module.exports = {
resolver: {
blacklistRE: /#current-cloud-backend\/.*/
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
As stated
here.
2022
In Metro v0.64.0 blacklist was renamed to blocklist, release notes
My current solution is to edit the metro.config.js (or create a new one) and add the following
const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = {
resolver: {
blacklistRE: exclusionList([/amplify\/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
If you are using typescript you should create the blacklist on metro.config.js NOT rn-cli.config.js.
module.exports = {
resolver: {
blacklistRE: /#current-cloud-backend\/.*/
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
As stated here.
I just ran into this problem and had to create ./rn-cli.config.js with the following as blacklist was in a different folder:
const blacklist = require('metro-config/src/defaults/blacklist');
module.exports = {
resolver: {
blacklistRE: blacklist([/#current-cloud-backend\/.*/]),
},
};
After updating to Expo SDK41 this issue came back.
I needed to change previous rn-cli.config.js to metro.config.js (even though I'm not using TS) and install #expo/metro-config as devDependency.
Expo + Late 2021 Update
Not sure if this is fix is expo specific, but the current way to do this is with blacklist (rather than exclusionList). Like so:
metro.config.js
const blacklist = require("metro-config/src/defaults/blacklist")
module.exports = {
resolver: {
blacklistRE: blacklist([/amplify\/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
}
For anyone who is still facing this issue I had to change something after this issue re-appeared.
For me, I had to change blacklist in line 1 to exclusionList
metro.config.js
const blacklist = require("metro-config/src/defaults/exclusionList")
module.exports = {
resolver: {
blacklistRE: blacklist([/amplify\/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
}
Related
I am creating an app with TypeScript + Firebase. I've followed this website to set it up: https://rnfirebase.io. After I finished with authentication I wanted to get a value from the real time database. However making the request doesn't resolve. I've also put it in the await version however that didn't resolve either.
import React, { useEffect } from "react";
import { Text } from "react-native";
import { firebase } from "#react-native-firebase/database";
import { REALTIME_DATABASE_ENV } from "react-native-dotenv";
const TestPage = () => {
useEffect(() => {
const reference = firebase
.app()
.database(REALTIME_DATABASE_ENV)
.ref("particularities/")
.once("value")
.then((snapshot) => {
console.log(`snapshot: ${snapshot.val()}`);
//expected result:
// {
// sickness: {
// label: "Sickness",
// },
// allergic: {
// label: "Allergic",
// },
// };
})
.catch((e: unknown) => {
console.log(`catch: ${e}`);
});
}, []);
return (
<Text>Test page</Text>
);
};
export default TestPage;
The rules that are applied to the real time database:
{
"rules": {
".read": false,
".write": false,
// ...
"particularities": {
".read": true,
".write": true,
},
}
}
Thing we found: logging out of the app does resolve all the requests made. Testing while logged in and all rules set to public gives the same result as before with the promise not resolving
As per the documentation here is how you can read the data once.
https://rnfirebase.io/database/usage#one-time-read
You don't need to pass database ref other than an 'us-central1'
import database from '#react-native-firebase/database';
database()
.ref('particularities/')
.once('value')
.then(snapshot => {
console.log('Data: ', snapshot.val());
})
.catch((e: unknown) => {
console.log(`catch: ${e}`);
});
When I trigger requests to the backend within a Storybook story, the story is re-rendered after receiving the response from Mock Service Worker (MSW). How can I prevent the re-rendering from happening?
Here's the story:
I followed the tutorial on https://storybook.js.org/addons/msw-storybook-addon to set up MSW for my Storybook stories.
So I:
installed msw and msw-storybook-addon
generated a service worker: npx msw init static/
updated my preview.js to call the initialize() function, added the mswDecorator, and set some global handlers (e. g. for "/api/customers")
When opening a component in Storybook that includes a request to "/api/customers" the following happens:
Initially MSW is telling me that it's enabled: [MSW] Mocking enabled.
I click a button to manually send a request to "/api/customers"
MSW is telling me that this API request is covered: [MSW] 15:21:20 GET /api/customers/ (200 OK)
I print the request results on the console - that works and I can see the expected results printed
But right after that the story is unintentionally re-rendered - this is what I want to suppress.
FF is telling me:
The connection to http://localhost:6006/__webpack_hmr was interrupted
while the page was loading.
Chrome doesn't give me that piece of information.
Here's the setup:
package.json:
{
...
"msw": {
"workerDirectory": "static"
}
}
main.js (Storybook):
// imports ...
const toPath = (filePath) => path.join(process.cwd(), filePath);
module.exports = {
core: {
builder: "webpack5",
},
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.#(js|jsx|ts|tsx)"],
addons: [
"#storybook/addon-links",
"#storybook/addon-essentials",
],
features: {
emotionAlias: false,
},
webpackFinal: (config) => {
return {
...config,
module: {
...config.module,
rules: custom.module.rules, // rules how to handle file extensions
},
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
"#emotion/core": toPath("node_modules/#emotion/react"),
"emotion-theming": toPath("node_modules/#emotion/react"),
},
},
};
},
staticDirs: ["../path/to/static/"], // that's our public
};
Here's some code:
preview.js
export const parameters = {
...
msw: {
handlers: [
rest.get("/api/customers", (req, res, ctx) => {
let customers = ...;
return res(
ctx.json({
data: customers,
})
);
}),
],
},
}
ExampleStory.stories.jsx:
export default {
title: "Customers",
};
const getCustomers = () => {
// ... set axios config etc.
axios.get("/api/customers", config)
.then((response) => response.data.data)
.then((customers) => console.log(customers)); // yep, the customers gets printed!
};
export const ExampleStory = () => {
return (
<div>
Simple demo story
<button onClick={getCustomers}>Get customers</button>
</div>
);
};
Node Modules:
Storybook#6.5.9
msw#0.42.3
msw-storybook-addon#1.6.3
I'm trying to test my components with jest. I'm using apollo client to query date from apollo server. I have follewed apollo client docs for testing queries, but without success.
I'm using nextjs and mongoDB with mongoose.
I'm getting this error:
FAIL components/organisms/createPost/CreatePost.test.jsx
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Details:
/Users/eyub/Documents/Learning/Projects/lim/node_modules/uuid/dist/esm-browser/index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js';
^^^^^^
SyntaxError: Unexpected token 'export'
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1796:14)
here is my component
export default function CreatePost({}) {
const [createPost] = useMutation(CREATE_POST, {
refetchQueries: [{ query: GET_USER }, 'getUserData'],
})
const sumbitHandler = (event) => {
event.preventDefault()
createPost({ variables: { text: event.target[0].value } })
event.target[0].value = ''
}
return (
<Component onSubmit={sumbitHandler}>
<textarea placeholder="What's happening?" />
<button>
<Icon icon={'add'} />
</button>
</Component>
)
}
here is my test:
import { MockedProvider } from '#apollo/client/testing'
import { CREATE_POST } from '#graphql/client/mutation'
import '#testing-library/jest-dom'
import { render } from '#testing-library/react'
import CreatePost from './CreatePost'
const newPostData = {
text: '',
}
const mocks = [
{
request: {
query: CREATE_POST,
variables: {
text: '',
},
},
result: { data: newPostData },
},
]
describe('<CreatePost />', () => {
test('mocking test pass', () => {
const component = render(
<MockedProvider mocks={mocks} addTypename={false}>
<CreatePost />
</MockedProvider>
)
component.debug()
})
})
here my jest config:
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
})
// Add any custom config to be passed to Jest
const customJestConfig = {
// Add more setup options before each test is run
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
moduleNameMapper: {
'^#components/(.*)$': '<rootDir>/components/$1',
'^#utils/(.*)$': '<rootDir>/utils/$1',
'^#graphql/(.*)$': '<rootDir>/graphql/$1',
},
collectCoverageFrom: ['./components/**/*.{js,jsx}'],
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
}
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)
I am having an issue when trying to deploy my app using Vercel. I am using dynamic routes for my projects pages, therefore I am using getStaticPaths and getStaticProps in the page. The code should call an API that retrieve my records from Airtable, however, it was failing as we should not call the APIs in there, so I have replaced the API call for the API code, it is working for me in localhost, even when I use npm run build. However, when I push my code to GH, Vercel cannot build it with the following error:
message: 'there was an error retrieving the records for paths',
e: AirtableError {
error: 'NOT_FOUND',
message: 'Could not find what you are looking for',
statusCode: 404
}
}
Build error occurred
TypeError: Cannot read property 'map' of undefined
at getStaticPaths (/vercel/path0/.next/server/pages/projects/[project].js:97:29)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async buildStaticPaths (/vercel/path0/node_modules/next/dist/build/utils.js:498:31)
at async /vercel/path0/node_modules/next/dist/build/utils.js:641:119
at async Span.traceAsyncFn (/vercel/path0/node_modules/next/dist/trace/trace.js:75:20) {
type: 'TypeError'
}
Error: Command "npm run build" exited with 1
import Image from "next/image";
import Link from "next/link";
import { projectsTable } from "../../airtable/airtable";
export const getStaticPaths = async () => {
const fetchProjects = async () => {
try {
let projects = [];
await projectsTable.select().eachPage((records, fetchNextPage) => {
records.forEach((record) => {
projects.push(record.fields);
});
fetchNextPage();
});
return projects;
} catch (e) {
return console.log({
message: "there was an error retrieving the records for paths",
e,
});
}
};
const projects = await fetchProjects();
const paths = projects.map((project) => {
return {
params: { project: project.name.toLowerCase().toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const fetchProjects = async () => {
try {
let projects = [];
await projectsTable.select().eachPage((records, fetchNextPage) => {
records.forEach((record) => {
projects.push(record.fields);
});
fetchNextPage();
});
return projects;
} catch (e) {
return console.log({
message: "there was an error retrieving the records for props",
e,
});
}
};
const projectName = context.params.project;
const projects = await fetchProjects();
const project = projects.find(
(project) => project.name.toLowerCase().toString() == projectName
);
return {
props: { project },
};
};
The code is just fetching the array of projects and use the name to find the correct one and use it as props to generate the rest of the page content. I tried to debug it using console.log, but the projects array is correct when I try, and I dont get any other error T.T Could anyone help me with this? Anyone having same issue or maybe could spot the error in my code? Thank you a lot in advance!
I don't understand these errors when I export as production npm run build , but when I test npm run dev it works just fine. I use getStaticProps and getStaticPath fetch from an API route.
First when I npm run build
FetchError: invalid json response body at https://main-website-next.vercel.app/api/products reason: Unexpected token T in JSON at position
0
at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:272:32
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\product\[slug].js:1324:18)
at async buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:80)
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:612
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
type: 'invalid-json'
}
\pages\product\[slug]
import { assetPrefix } from '../../next.config'
export default function Page(){...}
export const getStaticProps = async ({ params: { slug }, locale }) => {
const res = await fetch(`${assetPrefix}/api/products/${slug}`)
const result = await res.json()
const data = result.filter(item => item.locale === locale)[0]
const { title, keywords, description } = data
return {
props: {
data,
description,
keywords,
title
}
}
}
export const getStaticPaths = async () => {
const res = await fetch(`${assetPrefix}/api/products`)
const result = await res.json()
const paths = result.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
return {
fallback: true,
paths,
}
}
next.config.js
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
assetPrefix: isProd ? 'https://main-website-next.vercel.app' : 'http://localhost:3000',
i18n: {
localeDetection: false,
locales: ['en', 'th'],
defaultLocale: 'en',
}
}
API routes
// pages/api/products/index.js
import data from '../../../data/products'
export default (req, res) => {
res.status(200).json(data)
}
// pages/api/products/[slug].js
import db from '../../../data/products'
export default ({ query: { slug } }, res) => {
const data = db.filter(item => item.slug === slug)
if (data.length > 0) {
res.status(200).json(data)
} else {
res.status(404).json({ message: `${slug} not found` })
}
}
// ../../../data/products (data source)
module.exports = [
{ locale: "en", slug: "google-sheets-combine-your-cashflow",
title: "Combine your cashflow",
keywords: ["Google Sheets","accounting"],
description: "...",
},
...
]
Second when I remove the production domain, I run npm run build but still get the error like
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1305:9)
at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1410:19
at new Promise (<anonymous>)
at fetch (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1407:9)
at getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\[slug].js:938:21)
at buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:86)
at D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:618
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
type: 'TypeError'
}
My next.config.js after remove
const isProd = process.env.NODE_ENV === 'production'
module.exports = { //remove
assetPrefix: isProd ? '' : 'http://localhost:3000',
i18n: {
localeDetection: false,
locales: ['en', 'th'],
defaultLocale: 'en',
}
}
My package.json when I npm run build script
{
"name": "main-website-next",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start"
},
"dependencies": {
"next": "10.0.6",
"react": "17.0.1",
"react-dom": "17.0.1"
}
}
You should not call an internal API route inside getStaticProps. Instead, you can safely use your API logic directly in getStaticProps/getStaticPaths. These only happen server-side so you can write server-side code directly.
As getStaticProps runs only on the server-side, it will never run on
the client-side. It won’t even be included in the JS bundle for the
browser, so you can write direct database queries without them being
sent to browsers.
This means that instead of fetching an API route from
getStaticProps (that itself fetches data from an external source),
you can write the server-side code directly in getStaticProps.
Furthermore, your API routes are not available during build-time, as the server has not been started at that point.
Here's a small refactor of your code to address the issue.
// /pages/product/[slug]
import db from '../../../data/products'
// Remaining code..
export const getStaticProps = async ({ params: { slug }, locale }) => {
const result = db.filter(item => item.slug === slug)
const data = result.filter(item => item.locale === locale)[0]
const { title, keywords, description } = data
return {
props: {
data,
description,
keywords,
title
}
}
}
export const getStaticPaths = async () => {
const paths = db.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
return {
fallback: true,
paths,
}
}