Invariant Violation: fetch is not found globally and no fetcher passed, to fix pass a fetch for your environment - node.js

I'm trying to run a NodeJS app with an AWS Lambda handler. My package.json is very simple:
...
"dependencies": {
"aws-appsync": "^4.1.7",
"aws-sdk": "^2.1202.0",
"graphql-tag": "^2.12.6"
}
When I try to run anything I get:
Invariant Violation:
fetch is not found globally and no fetcher passed, to fix pass a fetch for
your environment like https://www.npmjs.com/package/node-fetch.
For example:
import fetch from 'node-fetch';
import { createHttpLink } from 'apollo-link-http';
const link = createHttpLink({ uri: '/graphql', fetch: fetch });
at new InvariantError (/Users/jamesdaniels/Code/node_modules/ts-invariant/lib/invariant.js:16:28)
at Object.exports.checkFetcher (/Users/jamesdaniels/Code/node_modules/apollo-link-http-common/lib/index.js:65:15)
at Object.createHttpLink (/Users/jamesdaniels/Code/node_modules/apollo-link-http/lib/bundle.umd.js:47:30)
at Object.exports.createAppSyncLink (/Users/jamesdaniels/Code/node_modules/aws-appsync/lib/client.js:144:201)
at new AWSAppSyncClient (/Users/jamesdaniels/Code/node_modules/aws-appsync/lib/client.js:214:72)
at Object.<anonymous> (/Users/jamesdaniels/Code/utils/appsync.js:16:23)
The error appears to be with the aws-appsync package. The error only occurs when I introduce that to my app:
const AWS = require("aws-sdk") // Works fine
const AUTH_TYPE = require("aws-appsync").AUTH_TYPE;
const AWSAppSyncClient = require("aws-appsync").default;
// GraphQL client config
const appSyncClientConfig = {
url: "https://xxxxxxxxxxxxxxxxx.appsync-api.eu-west-2.amazonaws.com/graphql",
region: "eu-west-2",
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
};
// Initialise the AppSync client
const appSyncClient = new AWSAppSyncClient(appSyncClientConfig);
An error is thrown from dependent modules aws-appsync > apollo-link-http > apollo-link-http-common.

Turns out the error message was telling me exactly what I needed to know. aws-appsync isn't designed for a back-end environment. It's intended for front-end environments, where fetch is available globally. In the back-end we need to create our own global variable for our own fetch if we want it to be available for any node packages that we've installed.
The solution is to download node-fetch and then follow the instructions in the "Provide Global Access" section:
Providing global access
To use fetch() without importing it, you can patch the global object in node:
// fetch-polyfill.js
import fetch, {
Blob,
blobFrom,
blobFromSync,
File,
fileFrom,
fileFromSync,
FormData,
Headers,
Request,
Response,
} from 'node-fetch'
if (!globalThis.fetch) {
globalThis.fetch = fetch
globalThis.Headers = Headers
globalThis.Request = Request
globalThis.Response = Response
}
// index.js
import './fetch-polyfill'
// ...

Related

How does one secure api keys on sveltekit 1.0

I am using ghost, i made an integration and i would like to hide the api key from the front-end. I do not believe i can set restrictions on the ghost cms (that would also work). And i do believe so +page.js files are run on the browser also, so im a little confused on how to achieve this?
The interal sveltekit module $env/static/private (docs) is how you use secure API keys. Sveltekit will not allow you to import this module into client code so it provides an extra layer of safety. Vite automatically loads your enviroment variables from .env files and process.env on build and injects your key into your server side bundle.
import { API_KEY } from '$env/static/private';
// Use your secret
Sveltekit has 4 modules for accessing enviroment variables
$env/static/private (covered)
$env/static/public accessiable by server and client and injected at build (docs)
$env/dynamic/private provided by your runtime adapter; only includes variables with that do not start with the your public prefix which defaults to PUBLIC_ and can only be imported by server files (docs)
$env/dynamic/public provided by your runtime adapter; only includes variables with that do start with the your public prefix which defaults to PUBLIC_ (docs)
You don't need to hide the key.
Ghost Content API Docs:
These keys are safe for use in browsers and other insecure environments, as they only ever provide access to public data.
One common way to hide your third-party API key(s) from public view is to set up proxy API routes.
The general idea is to have your client (browser) query a proxy API route that you provide/host, have that proxy route query the third-party API using your credentials (API key), and pass on the results from the third-party API back to the client.
Because the query to the third-party API takes place exclusively on the back-end, your credentials are never exposed to the client (browser) and thus not visible to the public.
In your use case, you would have to create 3 dynamic endpoint routes to replicate the structure of Ghost's API:
src/routes/api/[resource]/+server.js to match /posts/, /authors/, /tags/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
src/routes/api/[resource]/[id]/+server.js to match /posts/{id}/, /authors/{id}/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource, id } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/${id}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
src/routes/api/[resource]/slug/[slug]/+server.js to match /posts/slug/{slug}/, /authors/slug/{slug}/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource, slug } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/slug/${slug}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
Then all you have to do is call your proxy routes in place of your original third-party API routes in your app:
// very barebones example
<script>
let uri;
let data;
async function get() {
const res = await fetch(`/api/${uri}`);
data = await res.json();
}
</script>
<input name="uri" bind:value={uri} />
<button on:click={get}>GET</button>
{data}
Note that using proxy API routes will also have the additional benefit of sidestepping potential CORS issues.

error detecting global http method from vue test

I'm running vue3 and testing a vue component using the vue test utils library and in the component i am making an api call like this:
const api = this.$http.publisher.fetchValidatedWebsites();
I have this global http variable registered
app.config.globalProperties.$http = HttpServiceFactory(HttpClient);
HttpServicefactory returns publisher like so:
const httpServiceFactory = (HttpClient) => ({
publisher: PublisherService(HttpClient),
});
and in the PublisherService i have my fetchValidatedWebsites() method; but when i run the test
expect(wrapper.vm.$http.publisher.fetchValidatedWebsites)
.toHaveBeenCalledTimes(1);
i get this error:
TypeError: Cannot read property 'fetchValidatedWebsites' of undefined
Seems like for some reason the vm doesn't recognise this variable.
In my package.json:
"vue": "^3.2.12",
"#vue/test-utils": "^2.0.0-rc.21"
How do i go about fixing this?
You need to register $http as a global mock in your vue-test-utils config.
Try something like:
import { config } from '#vue/test-utils'
config.global.mocks = {
$http: (HttpClient) => ({
publisher: PublisherService(HttpClient),
})
}

Firebase Cloud functions - Failed to load function definition from source: Restful Endpoints - Failed to add the endpoint defined

I am using firebase cloud functions with typescript in node 16.14.
My index.ts look like this:
import { FunctionParser } from "firebase-backend";
exports = new FunctionParser({rootPath: __dirname, exports,
verbose:true}).exports;
Here is the addEvent.endpoint.ts:
import { Request, Response } from "express";
import { Endpoint, RequestType } from "firebase-backend";
// import * as functions from "firebase-functions";
const fs = require('firebase-admin');
const serviceAccount = require('./key.json');
fs.initializeApp({
credential: fs.credential.cert(serviceAccount)
});
const db = fs.firestore();
export default new Endpoint(
"addEvent",
RequestType.POST,
(request: Request, response: Response) => {
const message = request.body["message"];
db.collection("events").doc("adkakjagjh").set("message", message);
return response.status(201).send({
message: message
});
}
)
I have the following file structure as below.
My cloud functions worked just recently but some change that I am not aware of has made them error out with the following error for just this 1 endpoint "addEvent.endpoint.ts": Error: Failed to load function definition from source: Failed to generate manifest from function source: Error: Restful Endpoints - Failed to add the endpoint defined in C:/Development/EventFeeder/Backend/Firebase/functions/lib/users/restful/addEvent.endpoint.js to the users Api.
How can I fix this issue?
I am not sure what I need to try because the error message is not that specific about the problem's root cause.
I did not find any post about this particular problem.
I try to run the functions with "npm run build && firebase emulators:start --only functions" and "firebase emulators:start"
The issue disappear when I remove the addEvent.endpoint.ts file
I was facing the same problem, and I figured out that you shouldn't initialize the app (initializeApp()) in each endpoint, just write it in the index.ts one time and everything will work as expected.

How to set environment variable for ApolloClient that should be server side rendered for CI/CD

I have the following apolloClient
/**
* Initializes an ApolloClient instance. For configuration values refer to the following page
* https://www.apollographql.com/docs/react/api/core/ApolloClient/#the-apolloclient-constructor
*
* #returns ApolloClient
*/
const createApolloClient = (authToken: string | null) => {
const httpLinkHeaders = {
...(authToken && { Authorization: `Bearer ${authToken}` })
};
console.log('CREATING APOLLO CLIENT WITH HEADERS >>>>', httpLinkHeaders);
console.log(
'Graph Env Variable URL >>>>>',
publicRuntimeConfig.GRAPHQL_URL
);
return new ApolloClient({
name: 'client',
ssrMode: typeof window === 'undefined',
link: createHttpLink({
uri: publicRuntimeConfig.GRAPHQL_URL,
credentials: 'same-origin',
headers: httpLinkHeaders
}),
cache: new InMemoryCache()
});
};
/**
* Initializes the apollo client with data restored from the cache for pages that fetch data
* using either getStaticProps or getServerSideProps methods
*
* #param accessToken
* #param initialState
*
* #returns ApolloClient
*/
export const initializeApollo = (
accessToken: string,
initialState = null,
forceNewInstane = false
): ApolloClient<NormalizedCacheObject> => {
// Regenerate client?
if (forceNewInstane) {
apolloClient = null;
}
const _apolloClient = apolloClient || createApolloClient(accessToken);
// for pages that have Next.js data fetching methods that use Apollo Client,
// the initial state gets hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract();
// Restore the cache using the data passed from
// getStaticProps/getServerSideProps combined with the existing cached data
_apolloClient.cache.restore({ ...existingCache, ...initialState });
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
};
/**
* Hook to initialize the apollo client only when state has changed.
*
* #param initialState
*
* #returns
*/
export const useApollo = (
initialState: any
): ApolloClient<NormalizedCacheObject> => {
return useMemo(() => {
if (process.browser) {
const accessToken = extractCookie(document.cookie, 'access_token');
return initializeApollo(accessToken, initialState);
}
// document is not present and we can't retrieve the token but ApolloProvider requires to pass a client
return initializeApollo(null, initialState);
}, [initialState]);
};
That is initialized in the _app.tsx file like so
const updateApolloWithNewToken = useCallback(
(accessToken: string) => {
// Initialize apollo client with new access token
setClient(
initializeApollo(accessToken, pageProps.initialApolloState, true)
);
// Show the dashboard
router.replace('/dashboard');
},
[router]
);
With the following Next Config
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
module.exports = (phase, { defaultConfig }) => {
console.log('Phase >>>>', phase);
if (phase === PHASE_DEVELOPMENT_SERVER) {
console.log('RETURNING DEVELOPMENT CONFIGURATION...');
return {
publicRuntimeConfig: {
GRAPHQL_URL: process.env.GRAPHQL_URL
}
};
}
console.log('RETURNING PRODUCTION CONFIGURATION...');
console.log('GRAPHQL_URL', process.env.GRAPHQL_URL);
return {
publicRuntimeConfig: {
GRAPHQL_URL: process.env.GRAPHQL_URL
}
};
};
This is my _app.tsx
function MyApp({ Component, pageProps }: AppProps) {
// Grab the apollo client instance with state hydration from the pageProps
const router = useRouter();
const apolloClient = useApollo(pageProps.initialApolloState);
const [client, setClient] = useState(apolloClient);
React.useEffect(() => {
// Refresh token on browser load or page regresh
handleAcquireTokenSilent();
// We also set up an interval of 5 mins to check if token needs to be refreshed
const refreshTokenInterval = setInterval(() => {
handleAcquireTokenSilent();
}, REFRESH_TOKEN_SILENTLY_INTERVAL);
return () => {
clearInterval(refreshTokenInterval);
};
}, []);
const updateApolloWithNewToken = useCallback(
(accessToken: string) => {
// Initialize apollo client with new access token
setClient(
initializeApollo(accessToken, pageProps.initialApolloState, true)
);
// Show the dashboard
router.replace('/dashboard');
},
[router]
);
return pageProps.isAuthenticated || pageProps.shouldPageHandleUnAuthorize ? (
<ApolloProvider client={client}>
<ThemeProvider theme={theme}>
<SCThemeProvider theme={theme}>
<StylesProvider injectFirst>
<Component
{...pageProps}
updateAuthToken={updateApolloWithNewToken}
/>
</StylesProvider>
</SCThemeProvider>
</ThemeProvider>
</ApolloProvider>
) : (
<UnAuthorize />
);
}
/**
* Fetches the Me query so that pages/components can grab it from the cache in the
* client.
*
* Note: This disables the ability to perform automatic static optimization, causing
* every page in the app to be server-side rendered.
*/
MyApp.getInitialProps = async (appContext: AppContext) => {
const appProps = await App.getInitialProps(appContext);
const req = appContext.ctx.req;
// Execute Me query and handle all scenarios including, unauthorized response, caching data so pages can grab
// data from the cache
return await handleMeQuery(appProps, req);
};
export default MyApp;
My problem is that when I run yarn build I get a server error generating 500 page. I know it is because when creating the Apollo Client it doesn't have access to the publicRuntimeConfig, it seems like Next is trying to build the ApolloClient when I run yarn build, I am using getInitialProps and getServerSideProps so I just want to access all the env variables on runtime not on build, because we want one build for our pipeline.
All other env variables in my app that are using publicRuntimeConfig are working, I tested by removing the env vars on build and adding them back on start and the app functioned as normal.
If there isn't a way to do this with apollo client, what would be reccomended to be able to pass different uri's as env variables on start of the app not on build for Apollo Client or alternate solution?
Thanks for any help ahead of time
So I don't know if I have explained the problem well enough.
Basically the graphql URL is different depending on the environment it is in in development, staging, and production however they are supposed to use the same build so I need to access the GRAPHQL_URL via a runtime variable, but in my current setup it is just undefined.
Generally you don't want to use publicRuntimeConfig because it adds overhead and is unnecessary for your use-case. It also disables automatic static optimization.
Traditionally, environment variables are the way to handle dynamic settings based on the environment. Next.js has three default environments – development, test, and production.
Your Next.js app will automatically grab (and merge) the correct variables depending on the environment.
.env.local
GRAPHQL_URL = localhost:8000/graphql
.env.test
GRAPHQL_URL = test.example.com/graphql
.env or .env.production
GRAPHQL_URL = production.example.com/graphql
Apollo config
new ApolloClient({
link: createHttpLink({
uri: process.env.GRAPHQL_URL,
})
});
Environment variable files
In your project root you can create files named
.env
.env.local
.env.test
.env.test.local
.env.production
I believe you can append .local to any environment to create a local only version .env.production.local - this has limited use in your case
Next.js Environment variables
All environments - .env
loaded in development, test, and production environments. This file is used for defaults across all environments. The variables in this file will be overroad if another environment variable file has a variable with the same name.
Development environment .env.local
loaded only when NODE_ENV = development (next dev)
Test environment .env.test
loaded only when NODE_ENV = test
Test environment .env.test.local
loaded only when NODE_ENV = test and local
Production environment .env.production
loaded only when NODE_ENV = production (next start)
Example production only variable
Create a .env.production and add the variable there. You can repeat the same for test and local only variables.
Notes
It's good practice to add your .env to your .gitignore so you don't accidently commit secrets to your repositories. At a minimum you should omit .env*.local files from git.
Depending on your CI/CD setup you can set your environment variables in the deployment platform like vercel, github actions, etc. This would allow you to set the test and production variables in the hosting platform and not in your code.
If you need an environment variable accessible in the browser you need to prefix the variabel with NEXT_PUBLIC_
First, there is redundant code and inefficiency. Primarily the hooks updateApolloWithNewToken and useApollo but also in the way you inject the accessToken.
I would recommend throwing the ApolloClient in it's own separate file and using it's configurable links for your use-case.
However, the real problem most likely lies in the initialization of the client and your attempt at memoizing the client.
First, try updating the following,
link: createHttpLink({
uri: publicRuntimeConfig.GRAPHQL_URL,
credentials: 'same-origin',
headers: httpLinkHeaders
}),
to
link: createHttpLink({
// ...your other stuff
uri: () => getConfig().publicRuntimeConfig.GRAPHQL_URL
})
If that doesn't work right away, I would recommend trying with the outmost minimal example.
Create a client you export, a provider and a component that uses it (without using state, useEffect or anything else). We can go from there if that still doesn't work.

Import contentful in to react-native

I am extremely excited about using contentful for my project, but I can't get the JS library to work in a react-native project.
I get the following error:
I have tried the following approaches:
import contentful from 'contentful';
// or
var contentful = require('contentful');
You can reproduce the bug by going to this repo and following the steps I have provided.
Help would be greatly appreciated.
I am maintaining the Contentful sdk. I've put together a simple example
that shows how to use the SDK in React Native, you can check it here
It is basically getting a list of items from one of our example spaces and display the names in a ListView.Check indes.ios.js.
It looks like there is something wrong with the caching in your machine or so.
Anyway I hope this helps.If you have more problems please feel free to create issues in our github page
[UPDATE]
you can now configure the axios instance used in the SDK to use a different adapter. You can pass that when calling createClient
adapter: config => {
config.adapter = null // this is important if it is passing to another axios instance
// an http client combatible with React Native
return fetch(config)
}
Best,
Khaled
I've tried every option and it will never work using the Contentful SDK.
However, you can get it with REST and transform the response using the types from the contentful lib.
import axios from 'axios';
import {EntryCollection} from 'contentful';
import Env from '../Env';
const contentfulApi = 'https://cdn.contentful.com';
/**
* Default locale used in contentful calls
*
* #see {#link getContentfulEntries}
* #constant
*/
export const ContentfulLocale = 'sv';
/**
* #typedef ContentfulProps
* #param locale optional locale to use. Default is {#link ContentfulLocale}
* #param entryType content type in contentful model
*/
type ContentfulProps = {
entryType: string;
locale?: string;
};
/**
* Get entries from Contentful content API
* #param props See {#link ContentfulProps}
*/
export const getContentfulEntries = async <T>(
props: ContentfulProps,
): Promise<EntryCollection<T>> => {
const client = axios.create({
baseURL: `${contentfulApi}/spaces/${Env.CONTENTFUL_SPACEID}/environments/master/entries?`,
timeout: 1000,
headers: {Authorization: `Bearer ${Env.CONTENTFUL_TOKEN}`},
});
const result = await client.get<EntryCollection<T>>(
'&content_type=' + props.entryType,
);
return result.data;
};
export default getContentfulEntries;
I think the best way to use Contentful APIs with React Native is to use Apollo client and graphQL packages.
I worked around the same and get it done.
Install Apollo client and GraphQL npm package
npm install #apollo/client graphql
Install react-native async storage to store cache
npm install #react-native-async-storage/async-storage
Install apollo cache persist to persist the cache
npm install apollo3-cache-persist
you can read apollographql official documents for implementation
Create ApolloClient in the app.tsx/.js file
import { ApolloClient, ApolloProvider, InMemoryCache } from '#apollo/client';
const cache = new InMemoryCache();
const client = new ApolloClient({
uri: 'https://graphql.contentful.com/content/v1/spaces/{SPACE_ID}',
cache,
credentials: 'same-origin',
headers: {
Authorization: `Bearer {CDA-Access-Token}`,
},
});
Wrap all components in ApolloProvider
const App = () => {
const [loadingCache, setLoadingCache] = useState(true);
useEffect(() => {
persistCache({
cache,
storage: AsyncStorage,
}).then(() => setLoadingCache(false));
}, []);
if (loadingCache) {
return <Loader />;
}
return (
<ApolloProvider client={client}>
<SafeAreaView style={{flex: 1}}>
<Posts />
</SafeAreaView>
</ApolloProvider>
);
};
export default App;
Import gql and useQuery to fetch data
import { gql, useQuery } from '#apollo/client';
Now, write GraphQL query
const QUERY_COLLECTION = gql`
{
postsCollection {
items {
title
author
publishDate
inshorts
featuredImage {
url
}
}
}
}
`;
Fetch data using useQuery function
const { data, loading } = useQuery(QUERY_COLLECTION);
That's all to fetch data from Contentful in React Native App.
To read this in detailed, have a look to this post

Resources