How to query Sanity client with NestJS? - nestjs

Right now I have React app initialized with Vite connected with Sanity.
Everything is working just fine, Sanity client is connected, React is fetching data from Sanity, I'm receiving it with no problem.
But the problem is, that if I deploy React app with Sanity connected, then I will leak my projectID and sanity_token to the fronted, which I want to avoid of course.
So I have to make some backend REST API which will be fetched by React, and then my API will fetch Sanity. I could do it with Node.js and Express without problem, but I decided that I will use NestJS and learn something instead.
But when it comes to NestJS, everything is connected a bit different.
On the front I had :
import sanityClient from '#sanity/client';
export const client = sanityClient({
projectId: import.meta.env.VITE_SANITY_PROJECT_ID,
dataset: 'production',
apiVersion: '2022-02-01',
useCdn: true,
token: import.meta.env.VITE_SANITY_TOKEN
});
And for NestJS I found something like this:
import { Injectable } from '#nestjs/common';
import sanityClient, { SanityClient } from '#sanity/client';
#Injectable()
export class SanityService {
public readonly client: SanityClient = sanityClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: 'production',
apiVersion: '2022-02-01',
useCdn: true,
token: process.env.SANITY_TOKEN
});
}
My question is that if it's a good way to connect Sanity client?
How to query Sanity client with specified GROQ query?
Till now I was using this on the frontend, but it's not gonna work in NestJS:
const query = '*[_type == "blogPost"]';
client.fetch(query)
.then((data) => {
setPosts(data);
})
.catch((err) => {
console.log(err);
})

It turned out that this is the proper way to connect with Sanity client, I had an issue with that similar to this thread
And the solution was the same as in the thread above. Add "esModuleInterop": true to tsconfig.json.
{
"compilerOptions": {
...
"esModuleInterop": true,
...
}
}
Then call for data to sanity client is working properly:
#Get()
async getAllPosts() {
// initialize sanity instance
const sanityService = new SanityService();
// your query
const query = '*[_type == "blogPost"]';
try {
const data = await sanityService.client.fetch(query);
return data;
} catch (error) {
console.log(error.msg);
}
}

Related

How to implement JSON-Serever in NestJS Application?

I am new to Nest JS Framework and I dont whether I can use json-server to mock external API.
I have already checked NestJS documentation but its not having any example.
I found one question on Stack-overflow but it not complete Example Json-server mocking API instead of calls
I simply want to make one POST call and 1 GET Call.
Dont know which file I write these mocked POST and GET calls.
My first question is, what is your purpose for using NestJS?
You can think of NestJS and json-server as "similar" in their goal; you would use one OR the other. Not both.
If your goal is just to mock data and server, then you have everything you need with json-server. You wouldn't need NestJS.
If what you are looking for is to mock data to retrieve instead of creating a database, you can simply create a simple object in NestJS and retrieve data from there.
For the latter, it might look something like this (not tested):
// app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { AppService } from '../services/app.service';
#Controller('api')
export class AppController {
constructor(private readonly appService: AppService) {}
#Get('/users')
findAllUsers(): Promise<any> {
return this.appService.findAllUsers();
}
}
// app.service.ts
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
private getMockData() {
return {
users: [
{
name: 'john',
email: 'john#doe.com',
},
{
name: 'jane',
email: 'jane#doe.com',
},
],
};
}
findAllUsers(): Promise<any> {
return new Promise(function (resolve, reject) {
const data = this.getMockData();
if (data.users.length > 0) resolve(data.users);
else reject('No users found');
});
}
}
Now you would just have to do the request GET <host>/api/users where the host will be something like http://localhost:8080 in your local machine.

How to configure firebase-admin-sdk for `verifyIdToken` to pass?

I try to use Firebase in my application. The frontend logs the user in using the Web SDK, without any backend. Later, I would like to call some backend APIs. For this reason, I pass the idToken to the backend and try to validate the user as described in the Firebase docs.
When I do the above flow locally using the Firebase Emulator everything works as expected.
When I switch off the Emulator the idToken validation fails with
{
errorInfo: {
code: 'auth/argument-error',
message: 'Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
},
codePrefix: 'auth'
}
I created a Google hosted Firebase function to check if I can get the idToken validated there. The above setup works when the validation happens within the Google infrastructure.
Based on the above, I think the issue is in my FirebaseApp setup in the API. What that issue might be?
This is my setup.
I define 3 environment variables:
FIREBASE_DB_URL=https://<project-id>.firebaseio.com
FIREBASE_PROJECT_ID=<project-id>
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
I checked and cat $GOOGLE_APPLICATION_CREDENTIALS prints the correct file.
I initialize Firebase in the API with
import admin from "firebase-admin";
if(admin.apps.length == 0) {
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: process.env.FIREBASE_DB_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
});
console.log('Firebase initialized')
} else {
console.warn('Firebase already initialized')
}
and this is the validating code
import { DecodedIdToken } from 'firebase-admin/lib/auth/token-verifier';
import { getAuth } from 'firebase-admin/auth';
import './initializeFirebase';
export default async function needsLoggedInUser(idToken: string): Promise<DecodedIdToken|false> {
try {
return await getAuth().verifyIdToken(idToken)
} catch(err) {
console.error(err)
return false
}
}
I use the above in a NextJS API code as
import { NextApiRequest, NextApiResponse } from 'next'
import { getDatabase } from 'firebase-admin/database';
import 'services/backend/initializeFirebase';
import needsLoggedInUser from 'services/backend/needsLoggedInUser';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// As an admin, the app has access to read and write all data, regardless of Security Rules
const decodedToken = await needsLoggedInUser(req.body.user)
if(!decodedToken) {
return res.status(403).send("403 Forbidden")
}
/// ... rest of the API
}

How to track an error generated by providing wrong instrumentation key in Azure App Insights

I'm using Azure app Insights for collecting analytical data.I want to handle a case where I'm getting a 400 error due to providing the wrong instrumentation key in the AppInsights.js file. I have created a profile for encrypting instrumentation key in App. Where can I catch this error in code and display fallback UI in a React App.The backend is in Java.
import { ApplicationInsights from'#microsoft/applicationinsightsweb';
import {ReactPlugin,AppInsightsErrorBoundary,} from
'#microsoft/applicationinsights-react-js';
import { createBrowserHistory } from 'history';
import { ClickAnalyticsPlugin } from '#microsoft/applicationinsights-
clickanalytics-js';
import io from 'react/IOGlobal';
const clickPluginInstance = new ClickAnalyticsPlugin();
const browserHistory = createBrowserHistory({ basename: '' });
const reactPlugin = new ReactPlugin();
const clickPluginConfig = {
autoCapture: true,
dataTags: {
useDefaultContentNameOrId: true,
},
};
const appInsights = new ApplicationInsights({
config: { instrumentationKey:
***io.platform.getProfileValue('APP_INSTRUMENTATION_KEY')***,
extensions: [reactPlugin, clickPluginInstance],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory },
[clickPluginInstance.identifier]: clickPluginConfig,
},
},
});
appInsights.loadAppInsights();
export default { reactPlugin, appInsights };
Also please help with any other implementation in App Insights to catch the same error.
Thanks In Advance.
Update index.js by putting the following route at the end of the route declaration, before app.listen():
…
// this matches all routes and all methods
app.use((req, res, next) => {
res.status(404).send({
status: 404,
error: ‘Not found’
// You can add your alert
})
})
Check here for more info here
First need to set the Instrumentation key .setup(“Instrumentation key”) To get the instance of the default client that is configured with .setup("Instrumentation key") use appInsights.client. Alternatively, if you want a new client just use getClient("Instrumentation key").
Just like for setup, you can skip providing the Instrumentation key in code if it's in the special instrumentation key environment variable.
Refer for similar kind of issue here

WebSocket connection to Hasura does not work with ApolloClient v3 on Node.js

I created a Express/Node service which uses ApolloClient v3 to subscribe to a database on Hasura (database was created with Heroku on the Hasura platform). I want to subscribe to database changes with ApolloClient.
My ApolloClient setup looks like this (inspired by Split Communication Docs):
import express, { Request, Response } from "express";
import "cross-fetch/polyfill";
import ws from "ws";
import {
ApolloClient,
HttpLink,
InMemoryCache,
split,
gql,
} from "#apollo/client/core";
import { WebSocketLink } from "#apollo/client/link/ws";
import { getMainDefinition } from "#apollo/client/utilities";
const app = express();
const port = 8080;
const httpLink = new HttpLink({
uri: "https://my.hasura.app/v1/graphql",
fetch,
});
const wsLink = new WebSocketLink({
uri: "ws://my.hasura.app/v1/graphql",
options: {
reconnect: true,
},
webSocketImpl: ws,
});
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
Things I already discovered for using ApolloClient with Node.js:
Use the "#apollo/client/core" import and not "#apollo/client", because this needs a react dependency (and i don't want that on my backend service)
import ws from "ws"; for a web socket implementation for node, as this is a Browser-Feature
import "cross-fetch/polyfill"; to have a fetch functionality, which is used by ApolloClient
Now the app runs without errors, but I don't know why my subscription is not working. I have the same code on the frontend (a little bit different, as it is React) and it works well there.
This is my subscription logic:
const observable = client.subscribe({
query: gql`
subscription {
payments() {
amount
id
time
userId
}
}
`,
});
observable.subscribe((data: any) => {
console.log("data", data);
});
I am expecting that my console.log is logging some data, when the database changes - but it doesn't and my Hasura Dashboard lists no new connection.
How can I establish a websocket connection to my database?
I discovered, that the WebSocket uri on a Node.js client has to start with http/https and not ws/wws.
With this code it works now:
const wsLink = new WebSocketLink({
uri: "https://my.hasura.app/v1/graphql", // instead of "ws://my.hasura.app/v1/graphql"
options: {
reconnect: true,
},
webSocketImpl: ws,
});

How to validate header in hapi

I want to add a middleware to check the header values. if the expected do not present in the header then I want throw an error on API response. Following code is throwing error and I can see it in the console but I want to send this error to the user.
const server = new Server({
host: config.get('host'),
port: config.get('port' )
});
await server.register(require('#hapi/inert')); // eslint-disable-line #typescript-eslint/no-var-requires
server.register({
plugin: AuthService
})
server.route(
// mapping route paths with context path
routes.map((datum) => {
return {
...datum,
path: `${config.get('basePath')}${datum.path}`
};
})
);
plugin
import { notFound } from '#hapi/boom';
export const AuthService = {
name: 'authService',
version: '1.0.0',
register: async function (server: any, options: any) {
throw notFound(`Unauthorized user not found`);
}
};
Well, I think you are thinking it the express way too much. You should look at the authentication part of Hapi.
You can create a custom strategy and return an error if the header is not present.
Also, you can check how to use a Joi.schema() for all your routes.

Resources